You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
719 lines
17 KiB
719 lines
17 KiB
// kmsender.cpp |
|
|
|
|
|
#ifndef KRN |
|
#include "kmfoldermgr.h" |
|
#include "kmglobal.h" |
|
#include "kmfolder.h" |
|
#endif |
|
|
|
#include "kmsender.h" |
|
#include "kmmessage.h" |
|
#include "kmidentity.h" |
|
#include "kmiostatusdlg.h" |
|
|
|
#include <kconfig.h> |
|
#include <kapp.h> |
|
#include <kprocess.h> |
|
#include <kapp.h> |
|
#include <qregexp.h> |
|
|
|
#ifdef KRN |
|
#include <kapp.h> |
|
#include <kapp.h> |
|
extern KApplication *app; |
|
extern KLocale *nls; |
|
extern KMIdentity *identity; |
|
#endif |
|
|
|
#include <assert.h> |
|
#include <stdio.h> |
|
#include <unistd.h> |
|
#include <fcntl.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <sys/wait.h> |
|
|
|
#define SENDER_GROUP "sending mail" |
|
|
|
// uncomment the following line for SMTP debug output |
|
#define SMTP_DEBUG_OUTPUT |
|
|
|
#define MSG_BLOCK_SIZE 1024 |
|
|
|
//----------------------------------------------------------------------------- |
|
KMSender::KMSender() |
|
{ |
|
initMetaObject(); |
|
mSendDlg = NULL; |
|
mSendProc = NULL; |
|
mSendProcStarted = FALSE; |
|
mSendInProgress = FALSE; |
|
mCurrentMsg = NULL; |
|
readConfig(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
KMSender::~KMSender() |
|
{ |
|
writeConfig(FALSE); |
|
if (mSendProc) delete mSendProc; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::readConfig(void) |
|
{ |
|
QString str; |
|
KConfig* config = app->getConfig(); |
|
|
|
config->setGroup(SENDER_GROUP); |
|
|
|
mSendImmediate = (bool)config->readNumEntry("Immediate", TRUE); |
|
mSendQuotedPrintable = (bool)config->readNumEntry("Quoted-Printable", FALSE); |
|
mMailer = config->readEntry("Mailer", "/usr/sbin/sendmail"); |
|
mSmtpHost = config->readEntry("Smtp Host", "localhost"); |
|
mSmtpPort = config->readNumEntry("Smtp Port", 25); |
|
|
|
str = config->readEntry("Method"); |
|
if (str=="mail") mMethod = smMail; |
|
else if (str=="smtp") mMethod = smSMTP; |
|
else mMethod = smUnknown; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::writeConfig(bool aWithSync) |
|
{ |
|
KConfig* config = app->getConfig(); |
|
config->setGroup(SENDER_GROUP); |
|
|
|
config->writeEntry("Immediate", mSendImmediate); |
|
config->writeEntry("Quoted-Printable", mSendQuotedPrintable); |
|
config->writeEntry("Mailer", mMailer); |
|
config->writeEntry("Smtp Host", mSmtpHost); |
|
config->writeEntry("Smtp Port", mSmtpPort); |
|
config->writeEntry("Method", (mMethod==smSMTP) ? "smtp" : "mail"); |
|
|
|
if (aWithSync) config->sync(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSender::settingsOk(void) const |
|
{ |
|
if (mMethod!=smSMTP && mMethod!=smMail) |
|
{ |
|
warning(i18n("Please specify a send\nmethod in the settings\n" |
|
"and try again.")); |
|
return FALSE; |
|
} |
|
if (!identity->mailingAllowed()) |
|
{ |
|
warning(i18n("Please set the required fields in the\n" |
|
"identity settings:\n" |
|
"user-name and email-address")); |
|
return FALSE; |
|
} |
|
return TRUE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSender::send(KMMessage* aMsg, short sendNow) |
|
{ |
|
#ifndef KRN |
|
int rc; |
|
|
|
assert(aMsg != NULL); |
|
if (!settingsOk()) return FALSE; |
|
|
|
if (!aMsg->to() || aMsg->to()[0]=='\0') |
|
{ |
|
warning(i18n("You must specify a receiver")); |
|
return FALSE; |
|
} |
|
|
|
if (sendNow==-1) sendNow = mSendImmediate; |
|
|
|
outboxFolder->open(); |
|
aMsg->setStatus(KMMsgStatusQueued); |
|
rc = outboxFolder->addMsg(aMsg); |
|
if (rc) |
|
{ |
|
warning(i18n("Cannot add message to outbox folder")); |
|
return FALSE; |
|
} |
|
|
|
if (sendNow && !mSendInProgress) rc = sendQueued(); |
|
else rc = TRUE; |
|
outboxFolder->close(); |
|
|
|
return rc; |
|
#else |
|
return true; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSender::sendQueued(void) |
|
{ |
|
#ifndef KRN |
|
if (!settingsOk()) return FALSE; |
|
|
|
if (mSendInProgress) |
|
{ |
|
warning(i18n("Sending still in progress")); |
|
return FALSE; |
|
} |
|
|
|
// open necessary folders |
|
outboxFolder->open(); |
|
sentFolder->open(); |
|
mCurrentMsg = NULL; |
|
|
|
// create a sender |
|
if (mSendProc) delete mSendProc; |
|
if (mMethod == smMail) mSendProc = new KMSendSendmail(this); |
|
else if (mMethod == smSMTP) mSendProc = new KMSendSMTP(this); |
|
else mSendProc = NULL; |
|
assert(mSendProc != NULL); |
|
connect(mSendProc,SIGNAL(idle()),SLOT(slotIdle())); |
|
|
|
// start sending the messages |
|
doSendMsg(); |
|
return TRUE; |
|
#else |
|
return TRUE; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::doSendMsg(void) |
|
{ |
|
#ifndef KRN |
|
assert(mSendProc != NULL); |
|
|
|
// Move previously sent message to folder "sent" |
|
if (mCurrentMsg) |
|
{ |
|
mCurrentMsg->setStatus(KMMsgStatusSent); |
|
sentFolder->moveMsg(mCurrentMsg); |
|
mCurrentMsg = NULL; |
|
} |
|
|
|
// See if there is another queued message |
|
mCurrentMsg = outboxFolder->getMsg(0); |
|
if (!mCurrentMsg) |
|
{ |
|
// no more message: cleanup and done |
|
cleanup(); |
|
return; |
|
} |
|
|
|
// start the sender process or initialize communication |
|
if (!mSendProcStarted) |
|
{ |
|
emit statusMsg(i18n("Initiating sender process...")); |
|
if (!mSendProc->start()) |
|
{ |
|
cleanup(); |
|
return; |
|
} |
|
mSendProcStarted = TRUE; |
|
mSendInProgress = TRUE; |
|
} |
|
|
|
// remove header fields that shall not be included in sending |
|
mCurrentMsg->removeHeaderField("Status"); |
|
mCurrentMsg->removeHeaderField("X-Status"); |
|
|
|
// start sending the current message |
|
mSendProc->preSendInit(); |
|
emit statusMsg(i18n("Sending message: ")+mCurrentMsg->subject()); |
|
if (!mSendProc->send(mCurrentMsg)) |
|
{ |
|
cleanup(); |
|
return; |
|
} |
|
// Do *not* add code here, after send(). It can happen that this method |
|
// is called recursively if send() emits the idle signal directly. |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::cleanup(void) |
|
{ |
|
#ifndef KRN |
|
assert(mSendProc!=NULL); |
|
|
|
if (mSendProcStarted) mSendProc->finish(); |
|
mSendProcStarted = FALSE; |
|
mSendInProgress = FALSE; |
|
sentFolder->close(); |
|
outboxFolder->close(); |
|
if (outboxFolder->count()<=0) outboxFolder->expunge(); |
|
else outboxFolder->compact(); |
|
|
|
emit statusMsg(i18n("Done sending messages.")); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::slotIdle() |
|
{ |
|
debug("sender idle"); |
|
assert(mSendProc != NULL); |
|
//assert(!mSendProc->sending()); |
|
if (mSendProc->sendOk()) |
|
{ |
|
// sending succeeded |
|
doSendMsg(); |
|
} |
|
else |
|
{ |
|
// sending of message failed |
|
QString msg; |
|
msg = i18n("Sending failed:"); |
|
msg += '\n'; |
|
msg += mSendProc->message(); |
|
warning(msg); |
|
cleanup(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::setMethod(Method aMethod) |
|
{ |
|
mMethod = aMethod; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::setSendImmediate(bool aSendImmediate) |
|
{ |
|
mSendImmediate = aSendImmediate; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable) |
|
{ |
|
mSendQuotedPrintable = aSendQuotedPrintable; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::setMailer(const QString& aMailer) |
|
{ |
|
mMailer = aMailer; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::setSmtpHost(const QString& aSmtpHost) |
|
{ |
|
mSmtpHost = aSmtpHost; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSender::setSmtpPort(int aSmtpPort) |
|
{ |
|
mSmtpPort = aSmtpPort; |
|
} |
|
|
|
|
|
//============================================================================= |
|
//============================================================================= |
|
KMSendProc::KMSendProc(KMSender* aSender): QObject() |
|
{ |
|
initMetaObject(); |
|
mSender = aSender; |
|
preSendInit(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendProc::preSendInit(void) |
|
{ |
|
mSending = FALSE; |
|
mSendOk = FALSE; |
|
mMsg = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendProc::failed(const QString aMsg) |
|
{ |
|
mSending = FALSE; |
|
mSendOk = FALSE; |
|
mMsg = aMsg; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendProc::start(void) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendProc::finish(void) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
const QString KMSendProc::prepareStr(const QString aStr, bool toCRLF) |
|
{ |
|
QString str = aStr.copy(); |
|
|
|
str.replace(QRegExp("\\n\\."), "\n.."); |
|
if (toCRLF) str.replace(QRegExp("\\n"), "\r\n"); |
|
|
|
return str; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendProc::statusMsg(const char* aMsg) |
|
{ |
|
if (mSender) emit mSender->statusMsg(aMsg); |
|
app->processEvents(500); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendProc::addRecipients(const QStrList& aRecipientList) |
|
{ |
|
QStrList* recpList = (QStrList*)&aRecipientList; |
|
QString receiver; |
|
int i, j; |
|
bool rc; |
|
|
|
debug("recipients: %d", aRecipientList.count()); |
|
|
|
for (receiver=recpList->first(); !receiver.isNull(); receiver=recpList->next()) |
|
{ |
|
debug("receiver: %s", receiver.data()); |
|
i = receiver.find('<'); |
|
if (i >= 0) |
|
{ |
|
j = receiver.find('>', i+1); |
|
if (j > i) receiver = receiver.mid(i+1, j-i-1); |
|
} |
|
|
|
if (!receiver.isEmpty()) |
|
{ |
|
debug("KMSendProc::addRecipients: adding %s", receiver.data()); |
|
rc = addOneRecipient(receiver); |
|
if (!rc) return FALSE; |
|
} |
|
} |
|
return TRUE; |
|
} |
|
|
|
|
|
//============================================================================= |
|
//============================================================================= |
|
KMSendSendmail::KMSendSendmail(KMSender* aSender): |
|
KMSendSendmailInherited(aSender) |
|
{ |
|
initMetaObject(); |
|
mMailerProc = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
KMSendSendmail::~KMSendSendmail() |
|
{ |
|
if (mMailerProc) delete mMailerProc; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSendmail::start(void) |
|
{ |
|
if (mSender->mailer().isEmpty()) |
|
{ |
|
warning(i18n("Please specify a mailer program\n" |
|
"in the settings.")); |
|
return FALSE; |
|
} |
|
|
|
if (!mMailerProc) |
|
{ |
|
mMailerProc = new KProcess; |
|
assert(mMailerProc != NULL); |
|
connect(mMailerProc,SIGNAL(processExited(KProcess*)), |
|
this, SLOT(sendmailExited(KProcess*))); |
|
connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)), |
|
this, SLOT(wroteStdin(KProcess*))); |
|
connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)), |
|
this, SLOT(receivedStderr(KProcess*, char*, int))); |
|
} |
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSendmail::finish(void) |
|
{ |
|
if (mMailerProc) delete mMailerProc; |
|
mMailerProc = NULL; |
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSendmail::send(KMMessage* aMsg) |
|
{ |
|
mMsgStr = prepareStr(aMsg->asString()); |
|
|
|
mMailerProc->clearArguments(); |
|
*mMailerProc << mSender->mailer(); |
|
addRecipients(aMsg->headerAddrField("To")); |
|
if (!aMsg->cc().isEmpty()) addRecipients(aMsg->headerAddrField("Cc")); |
|
if (!aMsg->bcc().isEmpty()) addRecipients(aMsg->headerAddrField("Bcc")); |
|
|
|
if (!mMailerProc->start(KProcess::NotifyOnExit,KProcess::All)) |
|
{ |
|
warning(i18n("Failed to execute mailer program %s"), |
|
(const char*)mSender->mailer()); |
|
return FALSE; |
|
} |
|
mMsgPos = mMsgStr.data(); |
|
mMsgRest = mMsgStr.length(); |
|
wroteStdin(mMailerProc); |
|
|
|
return TRUE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendSendmail::wroteStdin(KProcess *proc) |
|
{ |
|
char* str; |
|
int len; |
|
|
|
assert(proc!=NULL); |
|
|
|
str = mMsgPos; |
|
len = (mMsgRest>1024 ? 1024 : mMsgRest); |
|
|
|
if (len <= 0) |
|
{ |
|
mMailerProc->closeStdin(); |
|
} |
|
else |
|
{ |
|
mMsgRest -= len; |
|
mMsgPos += len; |
|
mMailerProc->writeStdin(str,len); |
|
// if code is added after writeStdin() KProcess probably initiates |
|
// a race condition. |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen) |
|
{ |
|
assert(proc!=NULL); |
|
mMsg.replace(mMsg.length(), buflen, buffer); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendSendmail::sendmailExited(KProcess *proc) |
|
{ |
|
assert(proc!=NULL); |
|
mSendOk = (proc->normalExit() && proc->exitStatus()==0); |
|
mMsgStr = 0; |
|
emit idle(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSendmail::addOneRecipient(const QString aRcpt) |
|
{ |
|
assert(mMailerProc!=NULL); |
|
if (!aRcpt.isEmpty()) *mMailerProc << aRcpt; |
|
return TRUE; |
|
} |
|
|
|
|
|
//============================================================================= |
|
//============================================================================= |
|
KMSendSMTP::KMSendSMTP(KMSender* aSender): |
|
KMSendSMTPInherited(aSender) |
|
{ |
|
mClient = NULL; |
|
mOldHandler = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
KMSendSMTP::~KMSendSMTP() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSMTP::start(void) |
|
{ |
|
int replyCode; |
|
|
|
mOldHandler = signal(SIGALRM, SIG_IGN); |
|
mClient = new DwSmtpClient; |
|
assert(mClient != NULL); |
|
|
|
smtpInCmd(i18n("connecting to server")); |
|
mClient->Open(mSender->smtpHost(), mSender->smtpPort()); // Open connection |
|
if(!mClient->IsOpen()) // Check if connection succeded |
|
{ |
|
QString str(256); |
|
str.sprintf(i18n("Cannot open SMTP connection to\n" |
|
"host %s for sending:\n%s"), |
|
(const char*)mSender->smtpHost(), |
|
(const char*)mClient->Response().c_str()); |
|
warning((const char*)str); |
|
return FALSE; |
|
} |
|
app->processEvents(1000); |
|
|
|
smtpInCmd("HELO"); |
|
replyCode = mClient->Helo(); // Send HELO command |
|
smtpDebug("HELO"); |
|
if (replyCode != 250) return smtpFailed("HELO", replyCode); |
|
|
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSMTP::finish(void) |
|
{ |
|
if (mClient->ReplyCode() != 0) |
|
{ |
|
int replyCode = mClient->Quit(); // Send QUIT command |
|
smtpDebug("QUIT"); |
|
if(replyCode != 221) |
|
return smtpFailed("QUIT", replyCode); |
|
} |
|
|
|
if (mClient->Close() != 0) |
|
warning(i18n("Cannot close SMTP connection.")); |
|
|
|
signal(SIGALRM, mOldHandler); |
|
delete mClient; |
|
mClient = NULL; |
|
|
|
return TRUE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSMTP::send(KMMessage *msg) |
|
{ |
|
mSendOk = smtpSend(msg); |
|
emit idle(); |
|
return mSendOk; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSMTP::smtpSend(KMMessage* aMsg) |
|
{ |
|
QString str, msgStr; |
|
int replyCode; |
|
|
|
assert(aMsg != NULL); |
|
|
|
msgStr = prepareStr(aMsg->asString(), TRUE); |
|
|
|
smtpInCmd("MAIL"); |
|
replyCode = mClient->Mail(identity->emailAddr()); |
|
smtpDebug("MAIL"); |
|
if(replyCode != 250) return smtpFailed("MAIL", replyCode); |
|
|
|
if (!addRecipients(aMsg->headerAddrField("To"))) return FALSE; |
|
|
|
if (!aMsg->cc().isEmpty()) |
|
if (!addRecipients(aMsg->headerAddrField("Cc"))) return FALSE; |
|
|
|
if (!aMsg->bcc().isEmpty()) |
|
if (!addRecipients(aMsg->headerAddrField("Bcc"))) return FALSE; |
|
|
|
app->processEvents(500); |
|
|
|
smtpInCmd("DATA"); |
|
replyCode = mClient->Data(); // Send DATA command |
|
smtpDebug("DATA"); |
|
if(replyCode != 354) |
|
return smtpFailed("DATA", replyCode); |
|
|
|
smtpInCmd(i18n("transmitting message")); |
|
replyCode = mClient->SendData((const char*)msgStr); |
|
smtpDebug("<body>"); |
|
if(replyCode != 250 && replyCode != 251) |
|
return smtpFailed("<body>", replyCode); |
|
|
|
return TRUE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSMTP::addOneRecipient(const QString aRcpt) |
|
{ |
|
int replyCode; |
|
if (aRcpt.isEmpty()) return TRUE; |
|
|
|
smtpInCmd("RCPT"); |
|
replyCode = mClient->Rcpt(aRcpt); |
|
smtpDebug("RCPT"); |
|
|
|
if(replyCode != 250 && replyCode != 251) |
|
return smtpFailed("RCPT", replyCode); |
|
return TRUE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMSendSMTP::smtpFailed(const char* inCommand, |
|
int replyCode) |
|
{ |
|
QString str(256); |
|
const char* errorStr = mClient->Response().c_str(); |
|
|
|
if (replyCode==0 && (!errorStr || !*errorStr)) |
|
errorStr = i18n("network error"); |
|
|
|
str.sprintf(i18n("a SMTP error occured.\n" |
|
"Command: %s\n" |
|
"Response: %s\n" |
|
"Return code: %d"), |
|
inCommand, errorStr ? errorStr : "(nothing)", replyCode); |
|
mMsg = str; |
|
|
|
return FALSE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendSMTP::smtpInCmd(const char* inCommand) |
|
{ |
|
char str[80]; |
|
sprintf(str,i18n("Sending SMTP command: %s"), inCommand); |
|
statusMsg(str); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMSendSMTP::smtpDebug(const char* inCommand) |
|
{ |
|
#ifdef SMTP_DEBUG_OUTPUT |
|
const char* errorStr = mClient->Response().c_str(); |
|
int replyCode = mClient->ReplyCode(); |
|
debug("SMTP '%s': rc=%d, msg='%s'", inCommand, replyCode, errorStr); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
#include "kmsender.moc"
|
|
|