|
|
|
// kmsender.cpp
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#define REALLY_WANT_KMSENDER
|
|
|
|
#include "kmsender.h"
|
|
|
|
#include "kmsender_p.h"
|
|
|
|
#undef REALLY_WANT_KMSENDER
|
|
|
|
|
|
|
|
#include <kmime_header_parsing.h>
|
|
|
|
using namespace KMime::Types;
|
|
|
|
|
|
|
|
#include <tdeio/passdlg.h>
|
|
|
|
#include <tdeio/scheduler.h>
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
|
|
#include <tdeversion.h>
|
|
|
|
#include <tdelocale.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <tdeconfig.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include "globalsettings.h"
|
|
|
|
#include "kmfiltermgr.h"
|
|
|
|
|
|
|
|
#include "kcursorsaver.h"
|
|
|
|
#include <libkpimidentities/identity.h>
|
|
|
|
#include <libkpimidentities/identitymanager.h>
|
|
|
|
#include "progressmanager.h"
|
|
|
|
#include "kmaccount.h"
|
|
|
|
#include "kmtransport.h"
|
|
|
|
#include "kmfolderindex.h"
|
|
|
|
#include "kmfoldermgr.h"
|
|
|
|
#include "kmmsgdict.h"
|
|
|
|
#include "kmmsgpart.h"
|
|
|
|
#include "protocols.h"
|
|
|
|
#include "kmcommands.h"
|
|
|
|
#include <mimelib/mediatyp.h>
|
|
|
|
#include <mimelib/enum.h>
|
|
|
|
#include <mimelib/param.h>
|
|
|
|
|
|
|
|
#define SENDER_GROUP "sending mail"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
KMSender::KMSender()
|
|
|
|
: mOutboxFolder( 0 ), mSentFolder( 0 )
|
|
|
|
{
|
|
|
|
mPrecommand = 0;
|
|
|
|
mSendProc = 0;
|
|
|
|
mSendProcStarted = false;
|
|
|
|
mSendInProgress = false;
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
mTransportInfo = new KMTransportInfo();
|
|
|
|
readConfig();
|
|
|
|
mSendAborted = false;
|
|
|
|
mSentMessages = 0;
|
|
|
|
mTotalMessages = 0;
|
|
|
|
mFailedMessages = 0;
|
|
|
|
mSentBytes = 0;
|
|
|
|
mTotalBytes = 0;
|
|
|
|
mProgressItem = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
KMSender::~KMSender()
|
|
|
|
{
|
|
|
|
writeConfig(false);
|
|
|
|
delete mSendProc;
|
|
|
|
delete mPrecommand;
|
|
|
|
delete mTransportInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::setStatusMsg(const TQString &msg)
|
|
|
|
{
|
|
|
|
if ( mProgressItem )
|
|
|
|
mProgressItem->setStatus(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::readConfig(void)
|
|
|
|
{
|
|
|
|
TQString str;
|
|
|
|
TDEConfigGroup config(KMKernel::config(), SENDER_GROUP);
|
|
|
|
|
|
|
|
mSendImmediate = config.readBoolEntry("Immediate", true);
|
|
|
|
mSendQuotedPrintable = config.readBoolEntry("Quoted-Printable", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::writeConfig(bool aWithSync)
|
|
|
|
{
|
|
|
|
TDEConfigGroup config(KMKernel::config(), SENDER_GROUP);
|
|
|
|
|
|
|
|
config.writeEntry("Immediate", mSendImmediate);
|
|
|
|
config.writeEntry("Quoted-Printable", mSendQuotedPrintable);
|
|
|
|
|
|
|
|
if (aWithSync) config.sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool KMSender::settingsOk() const
|
|
|
|
{
|
|
|
|
if (KMTransportInfo::availableTransports().isEmpty())
|
|
|
|
{
|
|
|
|
KMessageBox::information(0,i18n("Please create an account for sending and try again."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handleRedirections( KMMessage * m ) {
|
|
|
|
const TQString from = m->headerField("X-KMail-Redirect-From");
|
|
|
|
const TQString msgId = m->msgId();
|
|
|
|
if( from.isEmpty() || msgId.isEmpty() )
|
|
|
|
m->setMsgId( KMMessage::generateMessageId( m->sender() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool KMSender::doSend(KMMessage* aMsg, short sendNow)
|
|
|
|
{
|
|
|
|
if(!aMsg)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!settingsOk()) return false;
|
|
|
|
|
|
|
|
if (aMsg->to().isEmpty())
|
|
|
|
{
|
|
|
|
// RFC822 says:
|
|
|
|
// Note that the "Bcc" field may be empty, while the "To" field is required to
|
|
|
|
// have at least one address.
|
|
|
|
//
|
|
|
|
// however:
|
|
|
|
//
|
|
|
|
// The following string is accepted according to RFC 2822,
|
|
|
|
// section 3.4 "Address Specification" where they say:
|
|
|
|
//
|
|
|
|
// "An address may either be an individual mailbox,
|
|
|
|
// or a group of mailboxes."
|
|
|
|
// and:
|
|
|
|
// "group + display-name ":" [mailbox-list / CFWS] ";"
|
|
|
|
// [CFWS]"
|
|
|
|
//
|
|
|
|
// In this syntax our "undisclosed-recipients: ;"
|
|
|
|
// just specifies an empty group.
|
|
|
|
//
|
|
|
|
// In further explanations RFC 2822 states that it *is*
|
|
|
|
// allowed to have a ZERO number of mailboxes in the "mailbox-list".
|
|
|
|
aMsg->setTo("Undisclosed.Recipients: ;");
|
|
|
|
}
|
|
|
|
|
|
|
|
handleRedirections( aMsg );
|
|
|
|
|
|
|
|
if (sendNow==-1) sendNow = mSendImmediate;
|
|
|
|
|
|
|
|
KMFolder * const outbox = kmkernel->outboxFolder();
|
|
|
|
const KMFolderOpener openOutbox( outbox, "outbox" );
|
|
|
|
|
|
|
|
aMsg->setStatus(KMMsgStatusQueued);
|
|
|
|
|
|
|
|
if ( const int err = outbox->addMsg(aMsg) ) {
|
|
|
|
Q_UNUSED( err );
|
|
|
|
KMessageBox::information(0,i18n("Cannot add message to outbox folder"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Ensure the message is correctly and fully parsed
|
|
|
|
|
|
|
|
/* The above was added by Marc and seems to be necessary to ensure
|
|
|
|
* the mail is in a sane state before sending. The unGet makes the
|
|
|
|
* attached unencrypted version of the mail (if there is one ) disappear.
|
|
|
|
* though, so we need to make sure to keep it around and restore it
|
|
|
|
* afterwards. The real fix would be to replace the unGet with
|
|
|
|
* whatever parsing is triggered by it, but I'm too chicken to do that,
|
|
|
|
* in this branch.
|
|
|
|
* Note that the unencrypted mail will be lost if the mail remains in
|
|
|
|
* the outbox across a restart anyhow, but that never worked, afaikt. */
|
|
|
|
const int idx = outbox->count() - 1;
|
|
|
|
KMMessage * const unencryptedMsg = aMsg->unencryptedMsg();
|
|
|
|
outbox->unGetMsg( idx );
|
|
|
|
KMMessage * const tempMsg = outbox->getMsg( idx );
|
|
|
|
tempMsg->setUnencryptedMsg( unencryptedMsg );
|
|
|
|
|
|
|
|
if ( !sendNow || mSendInProgress )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return sendQueued();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::outboxMsgAdded(int idx)
|
|
|
|
{
|
|
|
|
++mTotalMessages;
|
|
|
|
KMMsgBase* msg = kmkernel->outboxFolder()->getMsgBase(idx);
|
|
|
|
Q_ASSERT(msg);
|
|
|
|
if ( msg )
|
|
|
|
mTotalBytes += msg->msgSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool KMSender::doSendQueued( const TQString &customTransport )
|
|
|
|
{
|
|
|
|
if (!settingsOk()) return false;
|
|
|
|
|
|
|
|
if (mSendInProgress)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// open necessary folders
|
|
|
|
mOutboxFolder = kmkernel->outboxFolder();
|
|
|
|
mOutboxFolder->open("dosendoutbox");
|
|
|
|
mTotalMessages = mOutboxFolder->count();
|
|
|
|
if (mTotalMessages == 0) {
|
|
|
|
// Nothing in the outbox. We are done.
|
|
|
|
mOutboxFolder->close("dosendoutbox");
|
|
|
|
mOutboxFolder = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
mTotalBytes = 0;
|
|
|
|
for( int i = 0 ; i<mTotalMessages ; ++i )
|
|
|
|
mTotalBytes += mOutboxFolder->getMsgBase(i)->msgSize();
|
|
|
|
|
|
|
|
connect( mOutboxFolder, TQ_SIGNAL(msgAdded(int)),
|
|
|
|
this, TQ_SLOT(outboxMsgAdded(int)) );
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
|
|
|
|
mSentFolder = kmkernel->sentFolder();
|
|
|
|
mSentFolder->open("dosendsent");
|
|
|
|
kmkernel->filterMgr()->ref();
|
|
|
|
|
|
|
|
// start sending the messages
|
|
|
|
mCustomTransport = customTransport;
|
|
|
|
doSendMsg();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::emitProgressInfo( int currentFileProgress )
|
|
|
|
{
|
|
|
|
int percent = (mTotalBytes) ? ( 100 * (mSentBytes+currentFileProgress) / mTotalBytes ) : 0;
|
|
|
|
if (percent > 100) percent = 100;
|
|
|
|
mProgressItem->setProgress(percent);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool messageIsDispositionNotificationReport( KMMessage *msg )
|
|
|
|
{
|
|
|
|
if ( msg->type() == DwMime::kTypeMessage &&
|
|
|
|
msg->subtype() == DwMime::kSubtypeDispositionNotification )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if ( msg->type() != DwMime::kTypeMultipart ||
|
|
|
|
msg->subtype() != DwMime::kSubtypeReport )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
DwMediaType& ct = msg->dwContentType();
|
|
|
|
DwParameter *param = ct.FirstParameter();
|
|
|
|
while( param ) {
|
|
|
|
if ( !tqstricmp( param->Attribute().c_str(), "report-type")
|
|
|
|
&& !tqstricmp( param->Value().c_str(), "disposition-notification" ) )
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
param = param->Next();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::doSendMsg()
|
|
|
|
{
|
|
|
|
if (!kmkernel) //To handle message sending in progress when kaplan is exited
|
|
|
|
return; //TODO: handle this case better
|
|
|
|
|
|
|
|
const bool someSent = mCurrentMsg;
|
|
|
|
if (someSent) {
|
|
|
|
mSentMessages++;
|
|
|
|
mSentBytes += mCurrentMsg->msgSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Post-process sent message (filtering)
|
|
|
|
KMFolder *sentFolder = 0, *imapSentFolder = 0;
|
|
|
|
if (mCurrentMsg && kmkernel->filterMgr())
|
|
|
|
{
|
|
|
|
mCurrentMsg->setTransferInProgress( false );
|
|
|
|
if( mCurrentMsg->hasUnencryptedMsg() ) {
|
|
|
|
kdDebug(5006) << "KMSender::doSendMsg() post-processing: replace mCurrentMsg body by unencryptedMsg data" << endl;
|
|
|
|
// delete all current body parts
|
|
|
|
mCurrentMsg->deleteBodyParts();
|
|
|
|
// copy Content-[..] headers from unencrypted message to current one
|
|
|
|
KMMessage & newMsg( *mCurrentMsg->unencryptedMsg() );
|
|
|
|
mCurrentMsg->dwContentType() = newMsg.dwContentType();
|
|
|
|
mCurrentMsg->setContentTransferEncodingStr( newMsg.contentTransferEncodingStr() );
|
|
|
|
TQCString newDispo = newMsg.headerField("Content-Disposition").latin1();
|
|
|
|
if( newDispo.isEmpty() )
|
|
|
|
mCurrentMsg->removeHeaderField( "Content-Disposition" );
|
|
|
|
else
|
|
|
|
mCurrentMsg->setHeaderField( "Content-Disposition", newDispo );
|
|
|
|
// copy the body
|
|
|
|
mCurrentMsg->setBody( newMsg.body() );
|
|
|
|
// copy all the body parts
|
|
|
|
KMMessagePart msgPart;
|
|
|
|
for( int i = 0; i < newMsg.numBodyParts(); ++i ) {
|
|
|
|
newMsg.bodyPart( i, &msgPart );
|
|
|
|
mCurrentMsg->addBodyPart( &msgPart );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mCurrentMsg->setStatus(KMMsgStatusSent);
|
|
|
|
mCurrentMsg->setStatus(KMMsgStatusRead); // otherwise it defaults to new on imap
|
|
|
|
mCurrentMsg->updateAttachmentState();
|
|
|
|
mCurrentMsg->updateInvitationState();
|
|
|
|
|
|
|
|
const KPIM::Identity & id = kmkernel->identityManager()
|
|
|
|
->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
|
|
|
|
if ( !mCurrentMsg->fcc().isEmpty() )
|
|
|
|
{
|
|
|
|
sentFolder = kmkernel->folderMgr()->findIdString( mCurrentMsg->fcc() );
|
|
|
|
if ( sentFolder == 0 )
|
|
|
|
// This is *NOT* supposed to be imapSentFolder!
|
|
|
|
sentFolder =
|
|
|
|
kmkernel->dimapFolderMgr()->findIdString( mCurrentMsg->fcc() );
|
|
|
|
if ( sentFolder == 0 )
|
|
|
|
imapSentFolder =
|
|
|
|
kmkernel->imapFolderMgr()->findIdString( mCurrentMsg->fcc() );
|
|
|
|
}
|
|
|
|
// No, or no usable sentFolder, and no, or no usable imapSentFolder,
|
|
|
|
// let's try the on in the identity
|
|
|
|
if ( ( sentFolder == 0 || sentFolder->isReadOnly() )
|
|
|
|
&& ( imapSentFolder == 0 || imapSentFolder->isReadOnly() )
|
|
|
|
&& !id.fcc().isEmpty() )
|
|
|
|
{
|
|
|
|
sentFolder = kmkernel->folderMgr()->findIdString( id.fcc() );
|
|
|
|
if ( sentFolder == 0 )
|
|
|
|
// This is *NOT* supposed to be imapSentFolder!
|
|
|
|
sentFolder = kmkernel->dimapFolderMgr()->findIdString( id.fcc() );
|
|
|
|
if ( sentFolder == 0 )
|
|
|
|
imapSentFolder = kmkernel->imapFolderMgr()->findIdString( id.fcc() );
|
|
|
|
}
|
|
|
|
if (imapSentFolder
|
|
|
|
&& ( imapSentFolder->noContent() || imapSentFolder->isReadOnly() ) )
|
|
|
|
imapSentFolder = 0;
|
|
|
|
|
|
|
|
if ( sentFolder == 0 || sentFolder->isReadOnly() )
|
|
|
|
sentFolder = kmkernel->sentFolder();
|
|
|
|
|
|
|
|
if ( sentFolder ) {
|
|
|
|
if ( const int err = sentFolder->open("sentFolder") ) {
|
|
|
|
Q_UNUSED( err );
|
|
|
|
cleanup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disable the emitting of msgAdded signal, because the message is taken out of the
|
|
|
|
// current folder (outbox) and re-added, to make filter actions changing the message
|
|
|
|
// work. We don't want that to screw up message counts.
|
|
|
|
if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
|
|
|
|
const int processResult = kmkernel->filterMgr()->process(mCurrentMsg,KMFilterMgr::Outbound);
|
|
|
|
if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
|
|
|
|
|
|
|
|
// 0==processed ok, 1==no filter matched, 2==critical error, abort!
|
|
|
|
switch (processResult) {
|
|
|
|
case 2:
|
|
|
|
perror("Critical error: Unable to process sent mail (out of space?)");
|
|
|
|
KMessageBox::information(0, i18n("Critical error: "
|
|
|
|
"Unable to process sent mail (out of space?)"
|
|
|
|
"Moving failing message to \"sent-mail\" folder."));
|
|
|
|
if ( sentFolder ) {
|
|
|
|
sentFolder->moveMsg(mCurrentMsg);
|
|
|
|
sentFolder->close("sentFolder");
|
|
|
|
}
|
|
|
|
cleanup();
|
|
|
|
return;
|
|
|
|
case 1:
|
|
|
|
if ( sentFolder && sentFolder->moveMsg(mCurrentMsg) != 0 )
|
|
|
|
{
|
|
|
|
KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
|
|
|
|
"\"outbox\" to the \"sent-mail\" folder failed.\n"
|
|
|
|
"Possible reasons are lack of disk space or write permission. "
|
|
|
|
"Please try to fix the problem and move the message manually.")
|
|
|
|
.arg(mCurrentMsg->subject()));
|
|
|
|
cleanup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (imapSentFolder) {
|
|
|
|
// Does proper folder refcounting and message locking
|
|
|
|
KMCommand *command = new KMMoveCommand( imapSentFolder, mCurrentMsg );
|
|
|
|
command->keepFolderOpen( sentFolder ); // will open it, and close it once done
|
|
|
|
command->start();
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
setStatusByLink( mCurrentMsg );
|
|
|
|
if (mCurrentMsg->parent() && !imapSentFolder) {
|
|
|
|
// for speed optimization, this code assumes that mCurrentMsg is the
|
|
|
|
// last one in it's parent folder; make sure that's really the case:
|
|
|
|
assert( mCurrentMsg->parent()->find( mCurrentMsg )
|
|
|
|
== mCurrentMsg->parent()->count() - 1 );
|
|
|
|
// unGet this message:
|
|
|
|
mCurrentMsg->parent()->unGetMsg( mCurrentMsg->parent()->count() -1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if there is another queued message
|
|
|
|
mCurrentMsg = mOutboxFolder->getMsg(mFailedMessages);
|
|
|
|
if ( mCurrentMsg && !mCurrentMsg->transferInProgress() &&
|
|
|
|
mCurrentMsg->sender().isEmpty() ) {
|
|
|
|
// if we do not have a sender address then use the email address of the
|
|
|
|
// message's identity or of the default identity unless those two are also
|
|
|
|
// empty
|
|
|
|
const KPIM::Identity & id = kmkernel->identityManager()
|
|
|
|
->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
|
|
|
|
if ( !id.primaryEmailAddress().isEmpty() ) {
|
|
|
|
mCurrentMsg->setFrom( id.fullEmailAddr() );
|
|
|
|
}
|
|
|
|
else if ( !kmkernel->identityManager()->defaultIdentity().primaryEmailAddress().isEmpty() ) {
|
|
|
|
mCurrentMsg->setFrom( kmkernel->identityManager()->defaultIdentity().fullEmailAddr() );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
KMessageBox::sorry( 0, i18n( "It's not possible to send messages "
|
|
|
|
"without specifying a sender address.\n"
|
|
|
|
"Please set the email address of "
|
|
|
|
"identity '%1' in the Identities "
|
|
|
|
"section of the configuration dialog "
|
|
|
|
"and then try again." )
|
|
|
|
.arg( id.identityName() ) );
|
|
|
|
mOutboxFolder->unGetMsg( mFailedMessages );
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!mCurrentMsg || mCurrentMsg->transferInProgress())
|
|
|
|
{
|
|
|
|
// a message is locked finish the send
|
|
|
|
if (mCurrentMsg && mCurrentMsg->transferInProgress())
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
// no more message: cleanup and done
|
|
|
|
if ( sentFolder != 0 )
|
|
|
|
sentFolder->close("sentFolder");
|
|
|
|
if ( someSent ) {
|
|
|
|
if ( mSentMessages == mTotalMessages ) {
|
|
|
|
setStatusMsg(i18n("%n queued message successfully sent.",
|
|
|
|
"%n queued messages successfully sent.",
|
|
|
|
mSentMessages));
|
|
|
|
} else {
|
|
|
|
setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
|
|
|
|
.arg(mSentMessages).arg( mTotalMessages ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cleanup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mCurrentMsg->setTransferInProgress( true );
|
|
|
|
|
|
|
|
// start the sender process or initialize communication
|
|
|
|
if (!mSendInProgress)
|
|
|
|
{
|
|
|
|
Q_ASSERT( !mProgressItem );
|
|
|
|
mProgressItem = KPIM::ProgressManager::createProgressItem(
|
|
|
|
"Sender",
|
|
|
|
i18n( "Sending messages" ),
|
|
|
|
i18n("Initiating sender process..."),
|
|
|
|
true );
|
|
|
|
connect( mProgressItem, TQ_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
|
|
|
|
this, TQ_SLOT( slotAbortSend() ) );
|
|
|
|
kapp->ref();
|
|
|
|
mSendInProgress = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString msgTransport = mCustomTransport;
|
|
|
|
if ( msgTransport.isEmpty() ) {
|
|
|
|
msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
|
|
|
|
}
|
|
|
|
if ( msgTransport.isEmpty() ) {
|
|
|
|
msgTransport = GlobalSettings::self()->defaultTransport();
|
|
|
|
}
|
|
|
|
if ( msgTransport.isEmpty() ) {
|
|
|
|
const TQStringList sl = KMTransportInfo::availableTransports();
|
|
|
|
if (!sl.empty()) msgTransport = sl.front();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mSendProc || msgTransport != mMethodStr) {
|
|
|
|
if (mSendProcStarted && mSendProc) {
|
|
|
|
mSendProc->finish();
|
|
|
|
mSendProcStarted = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSendProc = createSendProcFromString(msgTransport);
|
|
|
|
mMethodStr = msgTransport;
|
|
|
|
|
|
|
|
if( mTransportInfo->encryption == "TLS" || mTransportInfo->encryption == "SSL" ) {
|
|
|
|
mProgressItem->setUsesCrypto( true );
|
|
|
|
} else if ( !mCustomTransport.isEmpty() ) {
|
|
|
|
int result = KMessageBox::warningContinueCancel( 0,
|
|
|
|
i18n( "You have chosen to send all queued email using an unencrypted transport, do you want to continue? "),
|
|
|
|
i18n( "Security Warning" ),
|
|
|
|
i18n( "Send Unencrypted" ),
|
|
|
|
"useCustomTransportWithoutAsking", false);
|
|
|
|
|
|
|
|
if( result == KMessageBox::Cancel ) {
|
|
|
|
mProgressItem->cancel();
|
|
|
|
mProgressItem->setComplete();
|
|
|
|
slotAbortSend();
|
|
|
|
cleanup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mSendProc)
|
|
|
|
sendProcStarted(false);
|
|
|
|
else {
|
|
|
|
connect(mSendProc, TQ_SIGNAL(idle()), TQ_SLOT(slotIdle()));
|
|
|
|
connect(mSendProc, TQ_SIGNAL(started(bool)), TQ_SLOT(sendProcStarted(bool)));
|
|
|
|
|
|
|
|
// Run the precommand if there is one
|
|
|
|
if ( !mTransportInfo->precommand.isEmpty() ) {
|
|
|
|
runPrecommand( mTransportInfo->precommand );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSendProc->start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!mSendProcStarted)
|
|
|
|
mSendProc->start();
|
|
|
|
else
|
|
|
|
doSendMsgAux();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KMSender::runPrecommand( const TQString & cmd ) {
|
|
|
|
setStatusMsg( i18n("Executing precommand %1").arg( cmd ) );
|
|
|
|
mPrecommand = new KMPrecommand( cmd );
|
|
|
|
connect( mPrecommand, TQ_SIGNAL(finished(bool)),
|
|
|
|
TQ_SLOT(slotPrecommandFinished(bool)) );
|
|
|
|
if ( !mPrecommand->start() ) {
|
|
|
|
delete mPrecommand; mPrecommand = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::sendProcStarted(bool success)
|
|
|
|
{
|
|
|
|
if (!success) {
|
|
|
|
if (mSendProc)
|
|
|
|
mSendProc->finish();
|
|
|
|
else
|
|
|
|
setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
|
|
|
|
mSendProc = 0;
|
|
|
|
mSendProcStarted = false;
|
|
|
|
cleanup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
doSendMsgAux();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static TQStringList addrSpecListToStringList( const AddrSpecList & l, bool allowEmpty=false ) {
|
|
|
|
TQStringList result;
|
|
|
|
for ( AddrSpecList::const_iterator it = l.begin(), end = l.end() ; it != end ; ++it ) {
|
|
|
|
const TQString s = (*it).asString();
|
|
|
|
if ( allowEmpty || !s.isEmpty() )
|
|
|
|
result.push_back( s );
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void extractSenderToCCAndBcc( KMMessage * aMsg, TQString * sender, TQStringList * to, TQStringList * cc, TQStringList * bcc ) {
|
|
|
|
if ( sender ) *sender = aMsg->sender();
|
|
|
|
if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
|
|
|
|
// extended BCC handling to prevent TOs and CCs from seeing
|
|
|
|
// BBC information by looking at source of an OpenPGP encrypted mail
|
|
|
|
if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "X-KMail-Recipients" ) );
|
|
|
|
aMsg->removeHeaderField( "X-KMail-Recipients" );
|
|
|
|
} else {
|
|
|
|
if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "To" ) );
|
|
|
|
if ( cc ) *cc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Cc" ) );
|
|
|
|
if ( bcc ) *bcc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Bcc" ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::doSendMsgAux()
|
|
|
|
{
|
|
|
|
mSendProcStarted = true;
|
|
|
|
|
|
|
|
// start sending the current message
|
|
|
|
|
|
|
|
setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
|
|
|
|
.arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
|
|
|
|
.arg(mCurrentMsg->subject()));
|
|
|
|
TQStringList to, cc, bcc;
|
|
|
|
TQString sender;
|
|
|
|
extractSenderToCCAndBcc( mCurrentMsg, &sender, &to, &cc, &bcc );
|
|
|
|
|
|
|
|
// MDNs are required to have an empty envelope from as per RFC2298.
|
|
|
|
if ( messageIsDispositionNotificationReport( mCurrentMsg ) && GlobalSettings::self()->sendMDNsWithEmptySender() )
|
|
|
|
sender = "<>";
|
|
|
|
|
|
|
|
const TQByteArray message = mCurrentMsg->asSendableString();
|
|
|
|
if ( sender.isEmpty() || !mSendProc->send( sender, to, cc, bcc, message ) ) {
|
|
|
|
if ( mCurrentMsg )
|
|
|
|
mCurrentMsg->setTransferInProgress( false );
|
|
|
|
if ( mOutboxFolder )
|
|
|
|
mOutboxFolder->unGetMsg( mFailedMessages );
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
cleanup();
|
|
|
|
setStatusMsg(i18n("Failed to send (some) queued messages."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Do *not* add code here, after send(). It can happen that this method
|
|
|
|
// is called recursively if send() emits the idle signal directly.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::cleanup(void)
|
|
|
|
{
|
|
|
|
kdDebug(5006) << k_funcinfo << endl;
|
|
|
|
if (mSendProc && mSendProcStarted) mSendProc->finish();
|
|
|
|
mSendProc = 0;
|
|
|
|
mSendProcStarted = false;
|
|
|
|
if (mSendInProgress) kapp->deref();
|
|
|
|
mSendInProgress = false;
|
|
|
|
if (mCurrentMsg)
|
|
|
|
{
|
|
|
|
mCurrentMsg->setTransferInProgress( false );
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
}
|
|
|
|
if ( mSentFolder ) {
|
|
|
|
mSentFolder->close("dosendsent");
|
|
|
|
mSentFolder = 0;
|
|
|
|
}
|
|
|
|
if ( mOutboxFolder ) {
|
|
|
|
disconnect( mOutboxFolder, TQ_SIGNAL(msgAdded(int)),
|
|
|
|
this, TQ_SLOT(outboxMsgAdded(int)) );
|
|
|
|
mOutboxFolder->close("dosendoutbox");
|
|
|
|
if ( mOutboxFolder->count( true ) == 0 ) {
|
|
|
|
mOutboxFolder->expunge();
|
|
|
|
}
|
|
|
|
else if ( mOutboxFolder->needsCompacting() ) {
|
|
|
|
mOutboxFolder->compact( KMFolder::CompactSilentlyNow );
|
|
|
|
}
|
|
|
|
mOutboxFolder = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSendAborted = false;
|
|
|
|
mSentMessages = 0;
|
|
|
|
mFailedMessages = 0;
|
|
|
|
mSentBytes = 0;
|
|
|
|
if ( mProgressItem )
|
|
|
|
mProgressItem->setComplete();
|
|
|
|
mProgressItem = 0;
|
|
|
|
kmkernel->filterMgr()->deref();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::slotAbortSend()
|
|
|
|
{
|
|
|
|
mSendAborted = true;
|
|
|
|
delete mPrecommand;
|
|
|
|
mPrecommand = 0;
|
|
|
|
if (mSendProc) mSendProc->abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::slotIdle()
|
|
|
|
{
|
|
|
|
assert(mSendProc != 0);
|
|
|
|
|
|
|
|
TQString msg;
|
|
|
|
TQString errString;
|
|
|
|
if (mSendProc)
|
|
|
|
errString = mSendProc->lastErrorMessage();
|
|
|
|
|
|
|
|
if (mSendAborted) {
|
|
|
|
// sending of message aborted
|
|
|
|
if ( mCurrentMsg ) {
|
|
|
|
mCurrentMsg->setTransferInProgress( false );
|
|
|
|
if ( mOutboxFolder )
|
|
|
|
mOutboxFolder->unGetMsg( mFailedMessages );
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
}
|
|
|
|
msg = i18n("Sending aborted:\n%1\n"
|
|
|
|
"The message will stay in the 'outbox' folder until you either "
|
|
|
|
"fix the problem (e.g. a broken address) or remove the message "
|
|
|
|
"from the 'outbox' folder.\n"
|
|
|
|
"The following transport protocol was used:\n %2")
|
|
|
|
.arg(errString)
|
|
|
|
.arg(mMethodStr);
|
|
|
|
if (!errString.isEmpty()) KMessageBox::error(0,msg);
|
|
|
|
setStatusMsg( i18n( "Sending aborted." ) );
|
|
|
|
} else {
|
|
|
|
if (!mSendProc->sendOk()) {
|
|
|
|
if ( mCurrentMsg )
|
|
|
|
mCurrentMsg->setTransferInProgress( false );
|
|
|
|
if ( mOutboxFolder )
|
|
|
|
mOutboxFolder->unGetMsg( mFailedMessages );
|
|
|
|
mCurrentMsg = 0;
|
|
|
|
mFailedMessages++;
|
|
|
|
// reset cached password
|
|
|
|
TQMapIterator <TQString,TQString> pc;
|
|
|
|
if ( (pc = mPasswdCache.find( mMethodStr )) != mPasswdCache.end() ) {
|
|
|
|
mPasswdCache.erase(pc);
|
|
|
|
}
|
|
|
|
// Sending of message failed.
|
|
|
|
if (!errString.isEmpty()) {
|
|
|
|
int res = KMessageBox::Yes;
|
|
|
|
if (mSentMessages+mFailedMessages != mTotalMessages) {
|
|
|
|
msg = i18n("<p>Sending failed:</p>"
|
|
|
|
"<p>%1</p>"
|
|
|
|
"<p>The message will stay in the 'outbox' folder until you either "
|
|
|
|
"fix the problem (e.g. a broken address) or remove the message "
|
|
|
|
"from the 'outbox' folder.</p>"
|
|
|
|
"<p>The following transport protocol was used: %2</p>"
|
|
|
|
"<p>Do you want me to continue sending the remaining messages?</p>")
|
|
|
|
.arg(errString)
|
|
|
|
.arg(mMethodStr);
|
|
|
|
res = KMessageBox::warningYesNo( 0 , msg ,
|
|
|
|
i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
|
|
|
|
i18n("&Abort Sending") );
|
|
|
|
} else {
|
|
|
|
msg = i18n("Sending failed:\n%1\n"
|
|
|
|
"The message will stay in the 'outbox' folder until you either "
|
|
|
|
"fix the problem (e.g. a broken address) or remove the message "
|
|
|
|
"from the 'outbox' folder.\n"
|
|
|
|
"The following transport protocol was used:\n %2")
|
|
|
|
.arg(errString)
|
|
|
|
.arg(mMethodStr);
|
|
|
|
KMessageBox::error(0,msg);
|
|
|
|
}
|
|
|
|
if (res == KMessageBox::Yes) {
|
|
|
|
// Try the next one.
|
|
|
|
doSendMsg();
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
setStatusMsg( i18n( "Sending aborted." ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Sending suceeded.
|
|
|
|
doSendMsg();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mSendProc->finish();
|
|
|
|
mSendProc = 0;
|
|
|
|
mSendProcStarted = false;
|
|
|
|
|
|
|
|
cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::slotPrecommandFinished(bool normalExit)
|
|
|
|
{
|
|
|
|
delete mPrecommand;
|
|
|
|
mPrecommand = 0;
|
|
|
|
if (normalExit) mSendProc->start();
|
|
|
|
else slotIdle();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::setSendImmediate(bool aSendImmediate)
|
|
|
|
{
|
|
|
|
mSendImmediate = aSendImmediate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
|
|
|
|
{
|
|
|
|
mSendQuotedPrintable = aSendQuotedPrintable;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
KMSendProc* KMSender::createSendProcFromString( const TQString & transport )
|
|
|
|
{
|
|
|
|
mTransportInfo->type = TQString();
|
|
|
|
int nr = KMTransportInfo::findTransport(transport);
|
|
|
|
if (nr)
|
|
|
|
{
|
|
|
|
mTransportInfo->readConfig(nr);
|
|
|
|
} else {
|
|
|
|
if (transport.startsWith("smtp://")) // should probably use KURL and SMTP_PROTOCOL
|
|
|
|
{
|
|
|
|
mTransportInfo->type = "smtp";
|
|
|
|
mTransportInfo->auth = false;
|
|
|
|
mTransportInfo->encryption = "NONE";
|
|
|
|
TQString serverport = transport.mid(7);
|
|
|
|
int colon = serverport.find(':');
|
|
|
|
if (colon != -1) {
|
|
|
|
mTransportInfo->host = serverport.left(colon);
|
|
|
|
mTransportInfo->port = serverport.mid(colon + 1);
|
|
|
|
} else {
|
|
|
|
mTransportInfo->host = serverport;
|
|
|
|
mTransportInfo->port = "25";
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (transport.startsWith("smtps://")) // should probably use KURL and SMTPS_PROTOCOL
|
|
|
|
{
|
|
|
|
mTransportInfo->type = "smtps";
|
|
|
|
mTransportInfo->auth = false;
|
|
|
|
mTransportInfo->encryption = "ssl";
|
|
|
|
TQString serverport = transport.mid(7);
|
|
|
|
int colon = serverport.find(':');
|
|
|
|
if (colon != -1) {
|
|
|
|
mTransportInfo->host = serverport.left(colon);
|
|
|
|
mTransportInfo->port = serverport.mid(colon + 1);
|
|
|
|
} else {
|
|
|
|
mTransportInfo->host = serverport;
|
|
|
|
mTransportInfo->port = "465";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (transport.startsWith("file://"))
|
|
|
|
{
|
|
|
|
mTransportInfo->type = "sendmail";
|
|
|
|
mTransportInfo->host = transport.mid(7);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// strip off a trailing "/"
|
|
|
|
while (mTransportInfo->host.endsWith("/")) {
|
|
|
|
mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (mTransportInfo->type == "sendmail")
|
|
|
|
return new KMSendSendmail(this);
|
|
|
|
if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
|
|
|
|
return new KMSendSMTP(this);
|
|
|
|
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSender::setStatusByLink(const KMMessage *aMsg)
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
while (1) {
|
|
|
|
ulong msn;
|
|
|
|
KMMsgStatus status;
|
|
|
|
aMsg->getLink(n, &msn, &status);
|
|
|
|
if (!msn || !status)
|
|
|
|
break;
|
|
|
|
n++;
|
|
|
|
|
|
|
|
KMFolder *folder = 0;
|
|
|
|
int index = -1;
|
|
|
|
KMMsgDict::instance()->getLocation(msn, &folder, &index);
|
|
|
|
if (folder && index != -1) {
|
|
|
|
KMFolderOpener openFolder(folder, "setstatus");
|
|
|
|
if ( status == KMMsgStatusDeleted ) {
|
|
|
|
// Move the message to the trash folder
|
|
|
|
KMDeleteMsgCommand *cmd =
|
|
|
|
new KMDeleteMsgCommand( folder, folder->getMsg( index ) );
|
|
|
|
cmd->start();
|
|
|
|
} else {
|
|
|
|
folder->setStatus(index, status);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
//=============================================================================
|
|
|
|
KMSendProc::KMSendProc( KMSender * sender )
|
|
|
|
: TQObject( 0 ),
|
|
|
|
mSender( sender ),
|
|
|
|
mLastErrorMessage(),
|
|
|
|
mSendOk( false ),
|
|
|
|
mSending( false )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSendProc::reset()
|
|
|
|
{
|
|
|
|
mSending = false;
|
|
|
|
mSendOk = false;
|
|
|
|
mLastErrorMessage = TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSendProc::failed(const TQString &aMsg)
|
|
|
|
{
|
|
|
|
mSending = false;
|
|
|
|
mSendOk = false;
|
|
|
|
mLastErrorMessage = aMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void KMSendProc::statusMsg(const TQString& aMsg)
|
|
|
|
{
|
|
|
|
if (mSender) mSender->setStatusMsg(aMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
//=============================================================================
|
|
|
|
KMSendSendmail::KMSendSendmail( KMSender * sender )
|
|
|
|
: KMSendProc( sender ),
|
|
|
|
mMsgStr(),
|
|
|
|
mMsgPos( 0 ),
|
|
|
|
mMsgRest( 0 ),
|
|
|
|
mMailerProc( 0 )
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
KMSendSendmail::~KMSendSendmail() {
|
|
|
|
delete mMailerProc; mMailerProc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KMSendSendmail::doStart() {
|
|
|
|
|
|
|
|
if (mSender->transportInfo()->host.isEmpty())
|
|
|
|
{
|
|
|
|
const TQString str = i18n("Please specify a mailer program in the settings.");
|
|
|
|
const TQString msg = i18n("Sending failed:\n%1\n"
|
|
|
|
"The message will stay in the 'outbox' folder and will be resent.\n"
|
|
|
|
"Please remove it from there if you do not want the message to "
|
|
|
|
"be resent.\n"
|
|
|
|
"The following transport protocol was used:\n %2")
|
|
|
|
.arg(str + "\n")
|
|
|
|
.arg("sendmail://");
|
|
|
|
KMessageBox::information(0,msg);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mMailerProc)
|
|
|
|
{
|
|
|
|
mMailerProc = new TDEProcess;
|
|
|
|
assert(mMailerProc != 0);
|
|
|
|
connect(mMailerProc,TQ_SIGNAL(processExited(TDEProcess*)),
|
|
|
|
this, TQ_SLOT(sendmailExited(TDEProcess*)));
|
|
|
|
connect(mMailerProc,TQ_SIGNAL(wroteStdin(TDEProcess*)),
|
|
|
|
this, TQ_SLOT(wroteStdin(TDEProcess*)));
|
|
|
|
connect(mMailerProc,TQ_SIGNAL(receivedStderr(TDEProcess*,char*,int)),
|
|
|
|
this, TQ_SLOT(receivedStderr(TDEProcess*, char*, int)));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSendmail::doFinish() {
|
|
|
|
delete mMailerProc;
|
|
|
|
mMailerProc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSendmail::abort()
|
|
|
|
{
|
|
|
|
delete mMailerProc;
|
|
|
|
mMailerProc = 0;
|
|
|
|
mSendOk = false;
|
|
|
|
mMsgStr = 0;
|
|
|
|
idle();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KMSendSendmail::doSend( const TQString & sender, const TQStringList & to, const TQStringList & cc, const TQStringList & bcc, const TQByteArray & message ) {
|
|
|
|
mMailerProc->clearArguments();
|
|
|
|
*mMailerProc << mSender->transportInfo()->host
|
|
|
|
<< "-i" << "-f" << sender
|
|
|
|
<< to << cc << bcc ;
|
|
|
|
|
|
|
|
mMsgStr = message;
|
|
|
|
|
|
|
|
if ( !mMailerProc->start( TDEProcess::NotifyOnExit, TDEProcess::All ) ) {
|
|
|
|
KMessageBox::information( 0, i18n("Failed to execute mailer program %1")
|
|
|
|
.arg( mSender->transportInfo()->host ) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
mMsgPos = mMsgStr.data();
|
|
|
|
mMsgRest = mMsgStr.size();
|
|
|
|
wroteStdin( mMailerProc );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KMSendSendmail::wroteStdin(TDEProcess *proc)
|
|
|
|
{
|
|
|
|
char* str;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
assert(proc!=0);
|
|
|
|
Q_UNUSED( proc );
|
|
|
|
|
|
|
|
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() TDEProcess probably initiates
|
|
|
|
// a race condition.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KMSendSendmail::receivedStderr(TDEProcess *proc, char *buffer, int buflen)
|
|
|
|
{
|
|
|
|
assert(proc!=0);
|
|
|
|
Q_UNUSED( proc );
|
|
|
|
mLastErrorMessage.replace(mLastErrorMessage.length(), buflen, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KMSendSendmail::sendmailExited(TDEProcess *proc)
|
|
|
|
{
|
|
|
|
assert(proc!=0);
|
|
|
|
mSendOk = (proc->normalExit() && proc->exitStatus()==0);
|
|
|
|
if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
|
|
|
|
mMsgStr = 0;
|
|
|
|
emit idle();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=============================================================================
|
|
|
|
//=============================================================================
|
|
|
|
KMSendSMTP::KMSendSMTP(KMSender *sender)
|
|
|
|
: KMSendProc(sender),
|
|
|
|
mInProcess(false),
|
|
|
|
mJob(0),
|
|
|
|
mSlave(0)
|
|
|
|
{
|
|
|
|
TDEIO::Scheduler::connect(TQ_SIGNAL(slaveError(TDEIO::Slave *, int,
|
|
|
|
const TQString &)), this, TQ_SLOT(slaveError(TDEIO::Slave *, int,
|
|
|
|
const TQString &)));
|
|
|
|
}
|
|
|
|
|
|
|
|
KMSendSMTP::~KMSendSMTP()
|
|
|
|
{
|
|
|
|
if (mJob) mJob->kill();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KMSendSMTP::doSend( const TQString & sender, const TQStringList & to, const TQStringList & cc, const TQStringList & bcc, const TQByteArray & message ) {
|
|
|
|
TQString query = "headers=0&from=";
|
|
|
|
query += KURL::encode_string( sender );
|
|
|
|
|
|
|
|
TQStringList::ConstIterator it;
|
|
|
|
|
|
|
|
for ( it = to.begin(); it != to.end(); ++it )
|
|
|
|
query += "&to=" + KURL::encode_string(*it);
|
|
|
|
for ( it = cc.begin(); it != cc.end(); ++it )
|
|
|
|
query += "&cc=" + KURL::encode_string(*it);
|
|
|
|
for ( it = bcc.begin(); it != bcc.end(); ++it )
|
|
|
|
query += "&bcc=" + KURL::encode_string(*it);
|
|
|
|
|
|
|
|
KMTransportInfo * ti = mSender->transportInfo();
|
|
|
|
|
|
|
|
if ( ti->specifyHostname )
|
|
|
|
query += "&hostname=" + KURL::encode_string( ti->localHostname );
|
|
|
|
|
|
|
|
if ( !kmkernel->msgSender()->sendQuotedPrintable() )
|
|
|
|
query += "&body=8bit";
|
|
|
|
|
|
|
|
KURL destination;
|
|
|
|
|
|
|
|
destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
|
|
|
|
destination.setHost(ti->host);
|
|
|
|
destination.setPort(ti->port.toUShort());
|
|
|
|
|
|
|
|
if (ti->auth)
|
|
|
|
{
|
|
|
|
TQMapIterator<TQString,TQString> tpc = mSender->mPasswdCache.find( ti->name );
|
|
|
|
TQString tpwd = ( tpc != mSender->mPasswdCache.end() )?(*tpc):TQString();
|
|
|
|
|
|
|
|
if ( ti->passwd().isEmpty() )
|
|
|
|
ti->setPasswd( tpwd );
|
|
|
|
|
|
|
|
if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
|
|
|
|
ti->authType != "GSSAPI" )
|
|
|
|
{
|
|
|
|
bool b = false;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
KCursorSaver idle(KBusyPtr::idle());
|
|
|
|
TQString passwd = ti->passwd();
|
|
|
|
result = TDEIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
|
|
|
|
&b, i18n("You need to supply a username and a password to use this "
|
|
|
|
"SMTP server."), false, TQString(), ti->name, TQString());
|
|
|
|
|
|
|
|
if ( result != TQDialog::Accepted )
|
|
|
|
{
|
|
|
|
abort();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (int id = KMTransportInfo::findTransport(ti->name)) {
|
|
|
|
ti->setPasswd( passwd );
|
|
|
|
ti->writeConfig(id);
|
|
|
|
|
|
|
|
// save the password into the cache
|
|
|
|
mSender->mPasswdCache[ti->name] = passwd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
destination.setUser(ti->user);
|
|
|
|
destination.setPass(ti->passwd());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mSlave || !mInProcess)
|
|
|
|
{
|
|
|
|
TDEIO::MetaData slaveConfig;
|
|
|
|
slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
|
|
|
|
if (ti->auth) slaveConfig.insert("sasl", ti->authType);
|
|
|
|
mSlave = TDEIO::Scheduler::getConnectedSlave(destination, slaveConfig);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mSlave)
|
|
|
|
{
|
|
|
|
abort();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// dotstuffing is now done by the slave (see setting of metadata)
|
|
|
|
mMessage = message;
|
|
|
|
mMessageLength = mMessage.size();
|
|
|
|
mMessageOffset = 0;
|
|
|
|
|
|
|
|
if ( mMessageLength )
|
|
|
|
// allow +5% for subsequent LF->CRLF and dotstuffing (an average
|
|
|
|
// over 2G-lines gives an average line length of 42-43):
|
|
|
|
query += "&size=" + TQString::number( tqRound( mMessageLength * 1.05 ) );
|
|
|
|
|
|
|
|
destination.setPath("/send");
|
|
|
|
destination.setQuery( query );
|
|
|
|
|
|
|
|
mJob = TDEIO::put( destination, -1, false, false, false );
|
|
|
|
if ( !mJob ) {
|
|
|
|
abort();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
|
|
|
|
TDEIO::Scheduler::assignJobToSlave(mSlave, mJob);
|
|
|
|
connect(mJob, TQ_SIGNAL(result(TDEIO::Job *)), this, TQ_SLOT(result(TDEIO::Job *)));
|
|
|
|
connect(mJob, TQ_SIGNAL(dataReq(TDEIO::Job *, TQByteArray &)),
|
|
|
|
this, TQ_SLOT(dataReq(TDEIO::Job *, TQByteArray &)));
|
|
|
|
mSendOk = true;
|
|
|
|
mInProcess = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSMTP::cleanup() {
|
|
|
|
if(mJob)
|
|
|
|
{
|
|
|
|
mJob->kill(true);
|
|
|
|
mJob = 0;
|
|
|
|
mSlave = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mSlave)
|
|
|
|
{
|
|
|
|
TDEIO::Scheduler::disconnectSlave(mSlave);
|
|
|
|
mSlave = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mInProcess = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSMTP::abort() {
|
|
|
|
cleanup();
|
|
|
|
emit idle();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSMTP::doFinish() {
|
|
|
|
cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSMTP::dataReq(TDEIO::Job *, TQByteArray &array)
|
|
|
|
{
|
|
|
|
// Send it by 32K chuncks
|
|
|
|
const int chunkSize = TQMIN( mMessageLength - mMessageOffset, 32*1024 );
|
|
|
|
if ( chunkSize > 0 ) {
|
|
|
|
array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
|
|
|
|
mMessageOffset += chunkSize;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
array.resize(0);
|
|
|
|
mMessage.resize(0);
|
|
|
|
}
|
|
|
|
mSender->emitProgressInfo( mMessageOffset );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSMTP::result(TDEIO::Job *_job)
|
|
|
|
{
|
|
|
|
if (!mJob) return;
|
|
|
|
mJob = 0;
|
|
|
|
|
|
|
|
if(_job->error())
|
|
|
|
{
|
|
|
|
mSendOk = false;
|
|
|
|
if (_job->error() == TDEIO::ERR_SLAVE_DIED) mSlave = 0;
|
|
|
|
failed(_job->errorString());
|
|
|
|
abort();
|
|
|
|
} else {
|
|
|
|
emit idle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMSendSMTP::slaveError(TDEIO::Slave *aSlave, int error, const TQString &errorMsg)
|
|
|
|
{
|
|
|
|
if (aSlave == mSlave)
|
|
|
|
{
|
|
|
|
if (error == TDEIO::ERR_SLAVE_DIED) mSlave = 0;
|
|
|
|
mSendOk = false;
|
|
|
|
mJob = 0;
|
|
|
|
failed(TDEIO::buildErrorString(error, errorMsg));
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "kmsender.moc"
|
|
|
|
#include "kmsender_p.moc"
|