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.
878 lines
28 KiB
878 lines
28 KiB
// KMAcctExpPop.cpp |
|
// Authors: Don Sanders, (based on kmacctpop by) |
|
// Stefan Taferner and Markus Wuebben |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include <config.h> |
|
#endif |
|
|
|
#include "kmacctexppop.h" |
|
|
|
#include "broadcaststatus.h" |
|
using KPIM::BroadcastStatus; |
|
#include "progressmanager.h" |
|
#include "kmfoldermgr.h" |
|
#include "kmfiltermgr.h" |
|
#include "kmpopfiltercnfrmdlg.h" |
|
#include "kmkernel.h" |
|
#include "protocols.h" |
|
#include "kmdict.h" |
|
|
|
#include <kdebug.h> |
|
#include <kstandarddirs.h> |
|
#include <klocale.h> |
|
#include <kmessagebox.h> |
|
#include <kmainwindow.h> |
|
#include <kio/scheduler.h> |
|
#include <kio/passdlg.h> |
|
#include <kconfig.h> |
|
using KIO::MetaData; |
|
|
|
static const unsigned short int pop3DefaultPort = 110; |
|
|
|
//----------------------------------------------------------------------------- |
|
KMAcctExpPop::KMAcctExpPop(KMAcctMgr* aOwner, const QString& aAccountName, uint id) |
|
: NetworkAccount(aOwner, aAccountName, id), |
|
headerIt(headersOnServer) |
|
{ |
|
init(); |
|
job = 0; |
|
mSlave = 0; |
|
mPort = defaultPort(); |
|
stage = Idle; |
|
indexOfCurrentMsg = -1; |
|
curMsgStrm = 0; |
|
processingDelay = 2*100; |
|
mProcessing = false; |
|
dataCounter = 0; |
|
mUidsOfSeenMsgsDict.setAutoDelete( false ); |
|
mUidsOfNextSeenMsgsDict.setAutoDelete( false ); |
|
|
|
headersOnServer.setAutoDelete(true); |
|
connect(&processMsgsTimer,SIGNAL(timeout()),SLOT(slotProcessPendingMsgs())); |
|
KIO::Scheduler::connect( |
|
SIGNAL(slaveError(KIO::Slave *, int, const QString &)), |
|
this, SLOT(slotSlaveError(KIO::Slave *, int, const QString &))); |
|
|
|
mHeaderDeleteUids.clear(); |
|
mHeaderDownUids.clear(); |
|
mHeaderLaterUids.clear(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
KMAcctExpPop::~KMAcctExpPop() |
|
{ |
|
if (job) { |
|
job->kill(); |
|
mMsgsPendingDownload.clear(); |
|
processRemainingQueuedMessages(); |
|
saveUidList(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QString KMAcctExpPop::type(void) const |
|
{ |
|
return "pop"; |
|
} |
|
|
|
QString KMAcctExpPop::protocol() const { |
|
return useSSL() ? POP_SSL_PROTOCOL : POP_PROTOCOL; |
|
} |
|
|
|
unsigned short int KMAcctExpPop::defaultPort() const { |
|
return pop3DefaultPort; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::init(void) |
|
{ |
|
NetworkAccount::init(); |
|
|
|
mUsePipelining = FALSE; |
|
mLeaveOnServer = FALSE; |
|
mFilterOnServer = FALSE; |
|
//tz todo |
|
mFilterOnServerCheckSize = 50000; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::pseudoAssign( const KMAccount * a ) { |
|
slotAbortRequested(); |
|
NetworkAccount::pseudoAssign( a ); |
|
|
|
const KMAcctExpPop * p = dynamic_cast<const KMAcctExpPop*>( a ); |
|
if ( !p ) return; |
|
|
|
setUsePipelining( p->usePipelining() ); |
|
setLeaveOnServer( p->leaveOnServer() ); |
|
setFilterOnServer( p->filterOnServer() ); |
|
setFilterOnServerCheckSize( p->filterOnServerCheckSize() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::processNewMail(bool _interactive) |
|
{ |
|
if (stage == Idle) { |
|
|
|
if(mAskAgain || mPasswd.isEmpty() || mLogin.isEmpty()) { |
|
QString passwd = decryptStr(mPasswd); |
|
bool b = FALSE; |
|
if (KIO::PasswordDialog::getNameAndPassword(mLogin, passwd, &b, |
|
i18n("You need to supply a username and a password to access this " |
|
"mailbox."), FALSE, QString::null, mName, i18n("Account:")) |
|
!= QDialog::Accepted) |
|
{ |
|
checkDone( false, CheckAborted ); |
|
return; |
|
} else { |
|
mPasswd = encryptStr(passwd); |
|
mAskAgain = FALSE; |
|
} |
|
} |
|
|
|
QString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" + |
|
mHost + ":" + QString("%1").arg(mPort) ); |
|
KConfig config( seenUidList ); |
|
QStringList uidsOfSeenMsgs = config.readListEntry( "seenUidList" ); |
|
mUidsOfSeenMsgsDict.clear(); |
|
mUidsOfSeenMsgsDict.resize( KMail::nextPrime( ( uidsOfSeenMsgs.count() * 11 ) / 10 ) ); |
|
for ( QStringList::ConstIterator it = uidsOfSeenMsgs.begin(); |
|
it != uidsOfSeenMsgs.end(); ++it ) { |
|
// we use mUidsOfSeenMsgsDict to provide fast random access to the keys, |
|
// so we simply set the values to (const int *)1 |
|
mUidsOfSeenMsgsDict.insert( *it, (const int *)1 ); |
|
} |
|
QStringList downloadLater = config.readListEntry( "downloadLater" ); |
|
for ( QStringList::Iterator it = downloadLater.begin(); it != downloadLater.end(); ++it ) { |
|
mHeaderLaterUids.insert( *it, true ); |
|
} |
|
mUidsOfNextSeenMsgsDict.clear(); |
|
|
|
interactive = _interactive; |
|
mUidlFinished = FALSE; |
|
startJob(); |
|
} |
|
else { |
|
checkDone( false, CheckIgnored ); |
|
return; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::readConfig(KConfig& config) |
|
{ |
|
NetworkAccount::readConfig(config); |
|
|
|
mUsePipelining = config.readNumEntry("pipelining", FALSE); |
|
mLeaveOnServer = config.readNumEntry("leave-on-server", FALSE); |
|
mFilterOnServer = config.readNumEntry("filter-on-server", FALSE); |
|
mFilterOnServerCheckSize = config.readUnsignedNumEntry("filter-os-check-size", 50000); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::writeConfig(KConfig& config) |
|
{ |
|
NetworkAccount::writeConfig(config); |
|
|
|
config.writeEntry("pipelining", mUsePipelining); |
|
config.writeEntry("leave-on-server", mLeaveOnServer); |
|
config.writeEntry("filter-on-server", mFilterOnServer); |
|
config.writeEntry("filter-os-check-size", mFilterOnServerCheckSize); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::setUsePipelining(bool b) |
|
{ |
|
mUsePipelining = b; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::setLeaveOnServer(bool b) |
|
{ |
|
mLeaveOnServer = b; |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
void KMAcctExpPop::setFilterOnServer(bool b) |
|
{ |
|
mFilterOnServer = b; |
|
} |
|
|
|
//--------------------------------------------------------------------------- |
|
void KMAcctExpPop::setFilterOnServerCheckSize(unsigned int aSize) |
|
{ |
|
mFilterOnServerCheckSize = aSize; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::connectJob() { |
|
KIO::Scheduler::assignJobToSlave(mSlave, job); |
|
if (stage != Dele) |
|
connect(job, SIGNAL( data( KIO::Job*, const QByteArray &)), |
|
SLOT( slotData( KIO::Job*, const QByteArray &))); |
|
connect(job, SIGNAL( result( KIO::Job * ) ), |
|
SLOT( slotResult( KIO::Job * ) ) ); |
|
connect(job, SIGNAL(infoMessage( KIO::Job*, const QString & )), |
|
SLOT( slotMsgRetrieved(KIO::Job*, const QString &))); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotCancel() |
|
{ |
|
mMsgsPendingDownload.clear(); |
|
processRemainingQueuedMessages(); |
|
saveUidList(); |
|
slotJobFinished(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotProcessPendingMsgs() |
|
{ |
|
if (mProcessing) // not reentrant |
|
return; |
|
mProcessing = true; |
|
|
|
bool addedOk; |
|
QValueList<KMMessage*>::Iterator cur = msgsAwaitingProcessing.begin(); |
|
QStringList::Iterator curId = msgIdsAwaitingProcessing.begin(); |
|
QStringList::Iterator curUid = msgUidsAwaitingProcessing.begin(); |
|
|
|
while (cur != msgsAwaitingProcessing.end()) { |
|
// note we can actually end up processing events in processNewMsg |
|
// this happens when send receipts is turned on |
|
// hence the check for re-entry at the start of this method. |
|
// -sanders Update processNewMsg should no longer process events |
|
|
|
addedOk = processNewMsg(*cur); //added ok? Error displayed if not. |
|
|
|
if (!addedOk) { |
|
mMsgsPendingDownload.clear(); |
|
msgIdsAwaitingProcessing.clear(); |
|
msgUidsAwaitingProcessing.clear(); |
|
break; |
|
} |
|
else { |
|
idsOfMsgsToDelete.append( *curId ); |
|
mUidsOfNextSeenMsgsDict.insert( *curUid, (const int *)1 ); |
|
} |
|
++cur; |
|
++curId; |
|
++curUid; |
|
} |
|
|
|
msgsAwaitingProcessing.clear(); |
|
msgIdsAwaitingProcessing.clear(); |
|
msgUidsAwaitingProcessing.clear(); |
|
mProcessing = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotAbortRequested() |
|
{ |
|
if (stage == Idle) return; |
|
disconnect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ), |
|
this, SLOT( slotAbortRequested() ) ); |
|
stage = Quit; |
|
if (job) job->kill(); |
|
job = 0; |
|
mSlave = 0; |
|
slotCancel(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::startJob() |
|
{ |
|
// Run the precommand |
|
if (!runPrecommand(precommand())) |
|
{ |
|
KMessageBox::sorry(0, |
|
i18n("Could not execute precommand: %1").arg(precommand()), |
|
i18n("KMail Error Message")); |
|
checkDone( false, CheckError ); |
|
return; |
|
} |
|
// end precommand code |
|
|
|
KURL url = getUrl(); |
|
|
|
if ( !url.isValid() ) { |
|
KMessageBox::error(0, i18n("Source URL is malformed"), |
|
i18n("Kioslave Error Message") ); |
|
return; |
|
} |
|
|
|
mMsgsPendingDownload.clear(); |
|
idsOfMsgs.clear(); |
|
mUidForIdMap.clear(); |
|
idsOfMsgsToDelete.clear(); |
|
//delete any headers if there are some this have to be done because of check again |
|
headersOnServer.clear(); |
|
headers = false; |
|
indexOfCurrentMsg = -1; |
|
|
|
Q_ASSERT( !mMailCheckProgressItem ); |
|
mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem( |
|
"MailCheck" + mName, |
|
mName, |
|
i18n("Preparing transmission from \"%1\"...").arg(mName), |
|
true, // can be canceled |
|
useSSL() || useTLS() ); |
|
connect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ), |
|
this, SLOT( slotAbortRequested() ) ); |
|
|
|
numBytes = 0; |
|
numBytesRead = 0; |
|
stage = List; |
|
mSlave = KIO::Scheduler::getConnectedSlave( url, slaveConfig() ); |
|
if (!mSlave) |
|
{ |
|
slotSlaveError(0, KIO::ERR_CANNOT_LAUNCH_PROCESS, url.protocol()); |
|
return; |
|
} |
|
url.setPath(QString("/index")); |
|
job = KIO::get( url, false, false ); |
|
connectJob(); |
|
} |
|
|
|
MetaData KMAcctExpPop::slaveConfig() const { |
|
MetaData m = NetworkAccount::slaveConfig(); |
|
|
|
m.insert("progress", "off"); |
|
m.insert("pipelining", (mUsePipelining) ? "on" : "off"); |
|
if (mAuth == "PLAIN" || mAuth == "LOGIN" || mAuth == "CRAM-MD5" || |
|
mAuth == "DIGEST-MD5") { |
|
m.insert("auth", "SASL"); |
|
m.insert("sasl", mAuth); |
|
} else if ( mAuth == "*" ) |
|
m.insert("auth", "USER"); |
|
else |
|
m.insert("auth", mAuth); |
|
|
|
return m; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// one message is finished |
|
// add data to a KMMessage |
|
void KMAcctExpPop::slotMsgRetrieved(KIO::Job*, const QString & infoMsg) |
|
{ |
|
if (infoMsg != "message complete") return; |
|
KMMessage *msg = new KMMessage; |
|
msg->setComplete(true); |
|
// Make sure to use LF as line ending to make the processing easier |
|
// when piping through external programs |
|
uint newSize = KMFolder::crlf2lf( curMsgData.data(), curMsgData.size() ); |
|
curMsgData.resize( newSize ); |
|
msg->fromByteArray( curMsgData , true ); |
|
if (stage == Head) |
|
{ |
|
int size = mMsgsPendingDownload[ headerIt.current()->id() ]; |
|
kdDebug(5006) << "Size of Message: " << size << endl; |
|
msg->setMsgLength( size ); |
|
headerIt.current()->setHeader(msg); |
|
++headerIt; |
|
slotGetNextHdr(); |
|
} else { |
|
//kdDebug(5006) << kfuncinfo << "stage == Retr" << endl; |
|
//kdDebug(5006) << "curMsgData.size() = " << curMsgData.size() << endl; |
|
msg->setMsgLength( curMsgData.size() ); |
|
msgsAwaitingProcessing.append(msg); |
|
msgIdsAwaitingProcessing.append(idsOfMsgs[indexOfCurrentMsg]); |
|
msgUidsAwaitingProcessing.append( mUidForIdMap[idsOfMsgs[indexOfCurrentMsg]] ); |
|
slotGetNextMsg(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// finit state machine to cycle trow the stages |
|
void KMAcctExpPop::slotJobFinished() { |
|
QStringList emptyList; |
|
if (stage == List) { |
|
kdDebug(5006) << k_funcinfo << "stage == List" << endl; |
|
// set the initial size of mUidsOfNextSeenMsgsDict to the number of |
|
// messages on the server + 10% |
|
mUidsOfNextSeenMsgsDict.resize( KMail::nextPrime( ( idsOfMsgs.count() * 11 ) / 10 ) ); |
|
KURL url = getUrl(); |
|
url.setPath(QString("/uidl")); |
|
job = KIO::get( url, false, false ); |
|
connectJob(); |
|
stage = Uidl; |
|
} |
|
else if (stage == Uidl) { |
|
kdDebug(5006) << k_funcinfo << "stage == Uidl" << endl; |
|
mUidlFinished = TRUE; |
|
|
|
if ( mLeaveOnServer && mUidForIdMap.isEmpty() && |
|
mUidsOfNextSeenMsgsDict.isEmpty() && !idsOfMsgs.isEmpty() ) { |
|
KMessageBox::sorry(0, i18n("Your POP3 server does not support the UIDL " |
|
"command: this command is required to determine, in a reliable way, " |
|
"which of the mails on the server KMail has already seen before;\n" |
|
"the feature to leave the mails on the server will therefore not " |
|
"work properly.")); |
|
// An attempt to work around buggy pop servers, these seem to be popular. |
|
mUidsOfNextSeenMsgsDict = mUidsOfSeenMsgsDict; |
|
} |
|
|
|
//check if filter on server |
|
if (mFilterOnServer == true) { |
|
QMap<QString, int>::Iterator hids; |
|
for ( hids = mMsgsPendingDownload.begin(); |
|
hids != mMsgsPendingDownload.end(); hids++ ) { |
|
kdDebug(5006) << "Length: " << hids.data() << endl; |
|
//check for mails bigger mFilterOnServerCheckSize |
|
if ( (unsigned int)hids.data() >= mFilterOnServerCheckSize ) { |
|
kdDebug(5006) << "bigger than " << mFilterOnServerCheckSize << endl; |
|
headersOnServer.append(new KMPopHeaders( hids.key(), |
|
mUidForIdMap[hids.key()], |
|
Later));//TODO |
|
//set Action if already known |
|
if( mHeaderDeleteUids.contains( headersOnServer.current()->uid() ) ) { |
|
headersOnServer.current()->setAction(Delete); |
|
} |
|
else if( mHeaderDownUids.contains( headersOnServer.current()->uid() ) ) { |
|
headersOnServer.current()->setAction(Down); |
|
} |
|
else if( mHeaderLaterUids.contains( headersOnServer.current()->uid() ) ) { |
|
headersOnServer.current()->setAction(Later); |
|
} |
|
} |
|
} |
|
// delete the uids so that you don't get them twice in the list |
|
mHeaderDeleteUids.clear(); |
|
mHeaderDownUids.clear(); |
|
mHeaderLaterUids.clear(); |
|
} |
|
// kdDebug(5006) << "Num of Msgs to Filter: " << headersOnServer.count() << endl; |
|
// if there are mails which should be checkedc download the headers |
|
if ((headersOnServer.count() > 0) && (mFilterOnServer == true)) { |
|
headerIt.toFirst(); |
|
KURL url = getUrl(); |
|
QString headerIds; |
|
while (headerIt.current()) |
|
{ |
|
headerIds += headerIt.current()->id(); |
|
if (!headerIt.atLast()) headerIds += ","; |
|
++headerIt; |
|
} |
|
headerIt.toFirst(); |
|
url.setPath(QString("/headers/") + headerIds); |
|
job = KIO::get( url, false, false ); |
|
connectJob(); |
|
slotGetNextHdr(); |
|
stage = Head; |
|
} |
|
else { |
|
stage = Retr; |
|
numMsgs = mMsgsPendingDownload.count(); |
|
numBytesToRead = 0; |
|
QMap<QString, int>::Iterator len; |
|
for ( len = mMsgsPendingDownload.begin(); |
|
len != mMsgsPendingDownload.end(); len++ ) |
|
numBytesToRead += len.data(); |
|
idsOfMsgs = QStringList( mMsgsPendingDownload.keys() ); |
|
KURL url = getUrl(); |
|
url.setPath( "/download/" + idsOfMsgs.join(",") ); |
|
job = KIO::get( url, false, false ); |
|
connectJob(); |
|
slotGetNextMsg(); |
|
processMsgsTimer.start(processingDelay); |
|
} |
|
} |
|
else if (stage == Head) { |
|
kdDebug(5006) << k_funcinfo << "stage == Head" << endl; |
|
|
|
// All headers have been downloaded, check which mail you want to get |
|
// data is in list headersOnServer |
|
|
|
// check if headers apply to a filter |
|
// if set the action of the filter |
|
KMPopFilterAction action; |
|
bool dlgPopup = false; |
|
for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) { |
|
action = (KMPopFilterAction)kmkernel->popFilterMgr()->process(headersOnServer.current()->header()); |
|
//debug todo |
|
switch ( action ) { |
|
case NoAction: |
|
kdDebug(5006) << "PopFilterAction = NoAction" << endl; |
|
break; |
|
case Later: |
|
kdDebug(5006) << "PopFilterAction = Later" << endl; |
|
break; |
|
case Delete: |
|
kdDebug(5006) << "PopFilterAction = Delete" << endl; |
|
break; |
|
case Down: |
|
kdDebug(5006) << "PopFilterAction = Down" << endl; |
|
break; |
|
default: |
|
kdDebug(5006) << "PopFilterAction = default oops!" << endl; |
|
break; |
|
} |
|
switch ( action ) { |
|
case NoAction: |
|
//kdDebug(5006) << "PopFilterAction = NoAction" << endl; |
|
dlgPopup = true; |
|
break; |
|
case Later: |
|
if (kmkernel->popFilterMgr()->showLaterMsgs()) |
|
dlgPopup = true; |
|
default: |
|
headersOnServer.current()->setAction(action); |
|
headersOnServer.current()->setRuleMatched(true); |
|
break; |
|
} |
|
} |
|
|
|
// if there are some messages which are not coverd by a filter |
|
// show the dialog |
|
headers = true; |
|
if (dlgPopup) { |
|
KMPopFilterCnfrmDlg dlg(&headersOnServer, this->name(), kmkernel->popFilterMgr()->showLaterMsgs()); |
|
dlg.exec(); |
|
} |
|
|
|
for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) { |
|
if (headersOnServer.current()->action() == Delete || |
|
headersOnServer.current()->action() == Later) { |
|
//remove entries from the lists when the mails should not be downloaded |
|
//(deleted or downloaded later) |
|
if ( mMsgsPendingDownload.contains( headersOnServer.current()->id() ) ) { |
|
mMsgsPendingDownload.remove( headersOnServer.current()->id() ); |
|
} |
|
if (headersOnServer.current()->action() == Delete) { |
|
mHeaderDeleteUids.insert(headersOnServer.current()->uid(), true); |
|
mUidsOfNextSeenMsgsDict.insert( headersOnServer.current()->uid(), |
|
(const int *)1 ); |
|
idsOfMsgsToDelete.append(headersOnServer.current()->id()); |
|
} |
|
else { |
|
mHeaderLaterUids.insert(headersOnServer.current()->uid(), true); |
|
} |
|
} |
|
else if (headersOnServer.current()->action() == Down) { |
|
mHeaderDownUids.insert(headersOnServer.current()->uid(), true); |
|
} |
|
} |
|
|
|
headersOnServer.clear(); |
|
stage = Retr; |
|
numMsgs = mMsgsPendingDownload.count(); |
|
numBytesToRead = 0; |
|
QMap<QString, int>::Iterator len; |
|
for (len = mMsgsPendingDownload.begin(); |
|
len != mMsgsPendingDownload.end(); len++) |
|
numBytesToRead += len.data(); |
|
idsOfMsgs = QStringList( mMsgsPendingDownload.keys() ); |
|
KURL url = getUrl(); |
|
url.setPath( "/download/" + idsOfMsgs.join(",") ); |
|
job = KIO::get( url, false, false ); |
|
connectJob(); |
|
slotGetNextMsg(); |
|
processMsgsTimer.start(processingDelay); |
|
} |
|
else if (stage == Retr) { |
|
mMailCheckProgressItem->setProgress( 100 ); |
|
processRemainingQueuedMessages(); |
|
|
|
mHeaderDeleteUids.clear(); |
|
mHeaderDownUids.clear(); |
|
mHeaderLaterUids.clear(); |
|
|
|
kmkernel->folderMgr()->syncAllFolders(); |
|
|
|
KURL url = getUrl(); |
|
if (mLeaveOnServer || idsOfMsgsToDelete.isEmpty()) { |
|
stage = Quit; |
|
mMailCheckProgressItem->setStatus( |
|
i18n( "Fetched 1 message from %1. Terminating transmission...", |
|
"Fetched %n messages from %1. Terminating transmission...", |
|
numMsgs ) |
|
.arg( mHost ) ); |
|
url.setPath(QString("/commit")); |
|
job = KIO::get(url, false, false ); |
|
} |
|
else { |
|
stage = Dele; |
|
mMailCheckProgressItem->setStatus( |
|
i18n( "Fetched 1 message from %1. Deleting messages from server...", |
|
"Fetched %n messages from %1. Deleting messages from server...", |
|
numMsgs ) |
|
.arg( mHost ) ); |
|
url.setPath("/remove/" + idsOfMsgsToDelete.join(",")); |
|
kdDebug(5006) << "url: " << url.prettyURL() << endl; |
|
job = KIO::get( url, false, false ); |
|
} |
|
connectJob(); |
|
} |
|
else if (stage == Dele) { |
|
kdDebug(5006) << k_funcinfo << "stage == Dele" << endl; |
|
// remove the uids of all messages which have been deleted |
|
for ( QStringList::ConstIterator it = idsOfMsgsToDelete.begin(); |
|
it != idsOfMsgsToDelete.end(); ++it ) { |
|
mUidsOfNextSeenMsgsDict.remove( mUidForIdMap[*it] ); |
|
} |
|
idsOfMsgsToDelete.clear(); |
|
mMailCheckProgressItem->setStatus( |
|
i18n( "Fetched 1 message from %1. Terminating transmission...", |
|
"Fetched %n messages from %1. Terminating transmission...", |
|
numMsgs ) |
|
.arg( mHost ) ); |
|
KURL url = getUrl(); |
|
url.setPath(QString("/commit")); |
|
job = KIO::get( url, false, false ); |
|
stage = Quit; |
|
connectJob(); |
|
} |
|
else if (stage == Quit) { |
|
kdDebug(5006) << k_funcinfo << "stage == Quit" << endl; |
|
saveUidList(); |
|
job = 0; |
|
if (mSlave) KIO::Scheduler::disconnectSlave(mSlave); |
|
mSlave = 0; |
|
stage = Idle; |
|
if( mMailCheckProgressItem ) { // do this only once... |
|
bool canceled = kmkernel->mailCheckAborted() || mMailCheckProgressItem->canceled(); |
|
int numMessages = canceled ? indexOfCurrentMsg : idsOfMsgs.count(); |
|
BroadcastStatus::instance()->setStatusMsgTransmissionCompleted( |
|
numMessages, numBytes, numBytesRead, numBytesToRead, mLeaveOnServer, mMailCheckProgressItem ); |
|
mMailCheckProgressItem->setComplete(); |
|
mMailCheckProgressItem = 0; |
|
checkDone( ( numMessages > 0 ), canceled ? CheckAborted : CheckOK ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::processRemainingQueuedMessages() |
|
{ |
|
kdDebug(5006) << k_funcinfo << endl; |
|
slotProcessPendingMsgs(); // Force processing of any messages still in the queue |
|
processMsgsTimer.stop(); |
|
|
|
stage = Quit; |
|
kmkernel->folderMgr()->syncAllFolders(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::saveUidList() |
|
{ |
|
kdDebug(5006) << k_funcinfo << endl; |
|
// Don't update the seen uid list unless we successfully got |
|
// a new list from the server |
|
if (!mUidlFinished) return; |
|
|
|
QStringList uidsOfNextSeenMsgs; |
|
QDictIterator<int> it( mUidsOfNextSeenMsgsDict ); |
|
for( ; it.current(); ++it ) |
|
uidsOfNextSeenMsgs.append( it.currentKey() ); |
|
QString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" + |
|
mHost + ":" + QString("%1").arg(mPort) ); |
|
KConfig config( seenUidList ); |
|
config.writeEntry( "seenUidList", uidsOfNextSeenMsgs ); |
|
config.writeEntry( "downloadLater", QStringList( mHeaderLaterUids.keys() ) ); |
|
config.sync(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotGetNextMsg() |
|
{ |
|
QMap<QString, int>::Iterator next = mMsgsPendingDownload.begin(); |
|
|
|
curMsgData.resize(0); |
|
numMsgBytesRead = 0; |
|
curMsgLen = 0; |
|
delete curMsgStrm; |
|
curMsgStrm = 0; |
|
|
|
if ( next != mMsgsPendingDownload.end() ) { |
|
// get the next message |
|
int nextLen = next.data(); |
|
curMsgStrm = new QDataStream( curMsgData, IO_WriteOnly ); |
|
curMsgLen = nextLen; |
|
++indexOfCurrentMsg; |
|
kdDebug(5006) << QString("Length of message about to get %1").arg( nextLen ) << endl; |
|
mMsgsPendingDownload.remove( next.key() ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotData( KIO::Job* job, const QByteArray &data) |
|
{ |
|
if (data.size() == 0) { |
|
kdDebug(5006) << "Data: <End>" << endl; |
|
if ((stage == Retr) && (numMsgBytesRead < curMsgLen)) |
|
numBytesRead += curMsgLen - numMsgBytesRead; |
|
else if (stage == Head){ |
|
kdDebug(5006) << "Head: <End>" << endl; |
|
} |
|
return; |
|
} |
|
|
|
int oldNumMsgBytesRead = numMsgBytesRead; |
|
if (stage == Retr) { |
|
headers = false; |
|
curMsgStrm->writeRawBytes( data.data(), data.size() ); |
|
numMsgBytesRead += data.size(); |
|
if (numMsgBytesRead > curMsgLen) |
|
numMsgBytesRead = curMsgLen; |
|
numBytesRead += numMsgBytesRead - oldNumMsgBytesRead; |
|
dataCounter++; |
|
if (dataCounter % 5 == 0) |
|
{ |
|
QString msg; |
|
if (numBytes != numBytesToRead && mLeaveOnServer) |
|
{ |
|
msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) from %5 " |
|
"(%6 KB remain on the server).") |
|
.arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024) |
|
.arg(numBytesToRead/1024).arg(mHost).arg(numBytes/1024); |
|
} |
|
else |
|
{ |
|
msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) from %5.") |
|
.arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024) |
|
.arg(numBytesToRead/1024).arg(mHost); |
|
} |
|
mMailCheckProgressItem->setStatus( msg ); |
|
mMailCheckProgressItem->setProgress( |
|
(numBytesToRead <= 100) ? 50 // We never know what the server tells us |
|
// This way of dividing is required for > 21MB of mail |
|
: (numBytesRead / (numBytesToRead / 100)) ); |
|
} |
|
return; |
|
} |
|
|
|
if (stage == Head) { |
|
curMsgStrm->writeRawBytes( data.data(), data.size() ); |
|
return; |
|
} |
|
|
|
// otherwise stage is List Or Uidl |
|
QString qdata = data; |
|
qdata = qdata.simplifyWhiteSpace(); // Workaround for Maillennium POP3/UNIBOX |
|
int spc = qdata.find( ' ' ); |
|
if (spc > 0) { |
|
if (stage == List) { |
|
QString length = qdata.mid(spc+1); |
|
if (length.find(' ') != -1) length.truncate(length.find(' ')); |
|
int len = length.toInt(); |
|
numBytes += len; |
|
QString id = qdata.left(spc); |
|
idsOfMsgs.append( id ); |
|
mMsgsPendingDownload.insert( id, len ); |
|
} |
|
else { // stage == Uidl |
|
const QString id = qdata.left(spc); |
|
const QString uid = qdata.mid(spc + 1); |
|
if ( mUidsOfSeenMsgsDict.find( uid ) != 0 ) { |
|
if ( mMsgsPendingDownload.contains( id ) ) { |
|
mMsgsPendingDownload.remove( id ); |
|
} |
|
else |
|
kdDebug(5006) << "KMAcctExpPop::slotData synchronization failure." << endl; |
|
idsOfMsgsToDelete.append( id ); |
|
mUidsOfNextSeenMsgsDict.insert( uid, (const int *)1 ); |
|
} |
|
mUidForIdMap.insert( id, uid ); |
|
} |
|
} |
|
else { |
|
stage = Idle; |
|
if (job) job->kill(); |
|
job = 0; |
|
mSlave = 0; |
|
KMessageBox::error(0, i18n( "Unable to complete LIST operation." ), |
|
i18n("Invalid Response From Server")); |
|
return; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotResult( KIO::Job* ) |
|
{ |
|
if (!job) return; |
|
if ( job->error() ) |
|
{ |
|
if (interactive) { |
|
if (headers) { // nothing to be done for headers |
|
idsOfMsgs.clear(); |
|
} |
|
if (stage == Head && job->error() == KIO::ERR_COULD_NOT_READ) |
|
{ |
|
KMessageBox::error(0, i18n("Your server does not support the " |
|
"TOP command. Therefore it is not possible to fetch the headers " |
|
"of large emails first, before downloading them.")); |
|
slotCancel(); |
|
return; |
|
} |
|
// force the dialog to be shown next time the account is checked |
|
if (!mStorePasswd) mPasswd = ""; |
|
job->showErrorDialog(); |
|
} |
|
slotCancel(); |
|
} |
|
else |
|
slotJobFinished(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotSlaveError(KIO::Slave *aSlave, int error, |
|
const QString &errorMsg) |
|
{ |
|
if (aSlave != mSlave) return; |
|
if (error == KIO::ERR_SLAVE_DIED) mSlave = 0; |
|
|
|
// explicitely disconnect the slave if the connection went down |
|
if ( error == KIO::ERR_CONNECTION_BROKEN && mSlave ) { |
|
KIO::Scheduler::disconnectSlave( mSlave ); |
|
mSlave = 0; |
|
} |
|
|
|
if (interactive) { |
|
KMessageBox::error(kmkernel->mainWin(), KIO::buildErrorString(error, errorMsg)); |
|
} |
|
|
|
|
|
stage = Quit; |
|
if (error == KIO::ERR_COULD_NOT_LOGIN && !mStorePasswd) |
|
mAskAgain = TRUE; |
|
/* We need a timer, otherwise slotSlaveError of the next account is also |
|
executed, if it reuses the slave, because the slave member variable |
|
is changed too early */ |
|
QTimer::singleShot(0, this, SLOT(slotCancel())); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctExpPop::slotGetNextHdr(){ |
|
kdDebug(5006) << "slotGetNextHeader" << endl; |
|
|
|
curMsgData.resize(0); |
|
delete curMsgStrm; |
|
curMsgStrm = 0; |
|
|
|
curMsgStrm = new QDataStream( curMsgData, IO_WriteOnly ); |
|
} |
|
|
|
void KMAcctExpPop::killAllJobs( bool ) { |
|
// must reimpl., but we don't use it yet |
|
} |
|
|
|
#include "kmacctexppop.moc"
|
|
|