imapaccountbase.cpp
00001 00024 #ifdef HAVE_CONFIG_H 00025 #include <config.h> 00026 #endif 00027 00028 #include "imapaccountbase.h" 00029 using KMail::SieveConfig; 00030 00031 #include "accountmanager.h" 00032 using KMail::AccountManager; 00033 #include "kmfolder.h" 00034 #include "broadcaststatus.h" 00035 using KPIM::BroadcastStatus; 00036 #include "kmmainwin.h" 00037 #include "kmfolderimap.h" 00038 #include "kmmainwidget.h" 00039 #include "kmmainwin.h" 00040 #include "kmmsgpart.h" 00041 #include "acljobs.h" 00042 #include "kmfoldercachedimap.h" 00043 #include "bodyvisitor.h" 00044 using KMail::BodyVisitor; 00045 #include "imapjob.h" 00046 using KMail::ImapJob; 00047 #include "protocols.h" 00048 #include "progressmanager.h" 00049 using KPIM::ProgressManager; 00050 #include "kmfoldermgr.h" 00051 #include "listjob.h" 00052 00053 #include <tdeapplication.h> 00054 #include <kdebug.h> 00055 #include <tdeconfig.h> 00056 #include <tdelocale.h> 00057 #include <tdemessagebox.h> 00058 using TDEIO::MetaData; 00059 #include <tdeio/passdlg.h> 00060 using TDEIO::PasswordDialog; 00061 #include <tdeio/scheduler.h> 00062 #include <tdeio/slave.h> 00063 #include <mimelib/bodypart.h> 00064 #include <mimelib/body.h> 00065 #include <mimelib/headers.h> 00066 #include <mimelib/message.h> 00067 //using TDEIO::Scheduler; // use FQN below 00068 00069 #include <tqregexp.h> 00070 #include <tqstylesheet.h> 00071 00072 namespace KMail { 00073 00074 static const unsigned short int imapDefaultPort = 143; 00075 00076 // 00077 // 00078 // Ctor and Dtor 00079 // 00080 // 00081 00082 ImapAccountBase::ImapAccountBase( AccountManager * parent, const TQString & name, uint id ) 00083 : NetworkAccount( parent, name, id ), 00084 mIdleTimer( 0, "mIdleTimer" ), 00085 mNoopTimer( 0, "mNoopTimer" ), 00086 mTotal( 0 ), 00087 mCountUnread( 0 ), 00088 mCountLastUnread( 0 ), 00089 mAutoExpunge( true ), 00090 mHiddenFolders( false ), 00091 mOnlySubscribedFolders( false ), 00092 mOnlyLocallySubscribedFolders( false ), 00093 mLoadOnDemand( true ), 00094 mListOnlyOpenFolders( false ), 00095 mProgressEnabled( false ), 00096 mErrorDialogIsActive( false ), 00097 mPasswordDialogIsActive( false ), 00098 mACLSupport( true ), 00099 mAnnotationSupport( true ), 00100 mQuotaSupport( true ), 00101 mSlaveConnected( false ), 00102 mSlaveConnectionError( false ), 00103 mCheckingSingleFolder( false ), 00104 mListDirProgressItem( 0 ) 00105 { 00106 mPort = imapDefaultPort; 00107 mBodyPartList.setAutoDelete(true); 00108 TDEIO::Scheduler::connect(TQT_SIGNAL(slaveError(TDEIO::Slave *, int, const TQString &)), 00109 this, TQT_SLOT(slotSchedulerSlaveError(TDEIO::Slave *, int, const TQString &))); 00110 TDEIO::Scheduler::connect(TQT_SIGNAL(slaveConnected(TDEIO::Slave *)), 00111 this, TQT_SLOT(slotSchedulerSlaveConnected(TDEIO::Slave *))); 00112 connect(&mNoopTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotNoopTimeout())); 00113 connect(&mIdleTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotIdleTimeout())); 00114 } 00115 00116 ImapAccountBase::~ImapAccountBase() { 00117 kdWarning( mSlave, 5006 ) 00118 << "slave should have been destroyed by subclass!" << endl; 00119 } 00120 00121 void ImapAccountBase::init() { 00122 mAutoExpunge = true; 00123 mHiddenFolders = false; 00124 mOnlySubscribedFolders = false; 00125 mOnlyLocallySubscribedFolders = false; 00126 mLoadOnDemand = true; 00127 mListOnlyOpenFolders = false; 00128 mProgressEnabled = false; 00129 } 00130 00131 void ImapAccountBase::pseudoAssign( const KMAccount * a ) { 00132 NetworkAccount::pseudoAssign( a ); 00133 00134 const ImapAccountBase * i = dynamic_cast<const ImapAccountBase*>( a ); 00135 if ( !i ) return; 00136 00137 setAutoExpunge( i->autoExpunge() ); 00138 setHiddenFolders( i->hiddenFolders() ); 00139 setOnlySubscribedFolders( i->onlySubscribedFolders() ); 00140 setOnlyLocallySubscribedFolders( i->onlyLocallySubscribedFolders() ); 00141 setLoadOnDemand( i->loadOnDemand() ); 00142 setListOnlyOpenFolders( i->listOnlyOpenFolders() ); 00143 setNamespaces( i->namespaces() ); 00144 setNamespaceToDelimiter( i->namespaceToDelimiter() ); 00145 localBlacklistFromStringList( i->locallyBlacklistedFolders() ); 00146 } 00147 00148 unsigned short int ImapAccountBase::defaultPort() const { 00149 return imapDefaultPort; 00150 } 00151 00152 TQString ImapAccountBase::protocol() const { 00153 return useSSL() ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL; 00154 } 00155 00156 // 00157 // 00158 // Getters and Setters 00159 // 00160 // 00161 00162 void ImapAccountBase::setAutoExpunge( bool expunge ) { 00163 mAutoExpunge = expunge; 00164 } 00165 00166 void ImapAccountBase::setHiddenFolders( bool show ) { 00167 mHiddenFolders = show; 00168 } 00169 00170 void ImapAccountBase::setOnlySubscribedFolders( bool show ) { 00171 mOnlySubscribedFolders = show; 00172 } 00173 00174 void ImapAccountBase::setOnlyLocallySubscribedFolders( bool show ) { 00175 mOnlyLocallySubscribedFolders = show; 00176 } 00177 00178 void ImapAccountBase::setLoadOnDemand( bool load ) { 00179 mLoadOnDemand = load; 00180 } 00181 00182 void ImapAccountBase::setListOnlyOpenFolders( bool only ) { 00183 mListOnlyOpenFolders = only; 00184 } 00185 00186 // 00187 // 00188 // read/write config 00189 // 00190 // 00191 00192 void ImapAccountBase::readConfig( /*const*/ TDEConfig/*Base*/ & config ) { 00193 NetworkAccount::readConfig( config ); 00194 00195 setAutoExpunge( config.readBoolEntry( "auto-expunge", false ) ); 00196 setHiddenFolders( config.readBoolEntry( "hidden-folders", false ) ); 00197 setOnlySubscribedFolders( config.readBoolEntry( "subscribed-folders", false ) ); 00198 setOnlyLocallySubscribedFolders( config.readBoolEntry( "locally-subscribed-folders", false ) ); 00199 setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) ); 00200 setListOnlyOpenFolders( config.readBoolEntry( "listOnlyOpenFolders", false ) ); 00201 mCapabilities = config.readListEntry( "capabilities", TQStringList() ); 00202 // read namespaces 00203 nsMap map; 00204 TQStringList list = config.readListEntry( TQString::number( PersonalNS ) ); 00205 if ( !list.isEmpty() ) 00206 map[PersonalNS] = list.gres( "\"", "" ); 00207 list = config.readListEntry( TQString::number( OtherUsersNS ) ); 00208 if ( !list.isEmpty() ) 00209 map[OtherUsersNS] = list.gres( "\"", "" ); 00210 list = config.readListEntry( TQString::number( SharedNS ) ); 00211 if ( !list.isEmpty() ) 00212 map[SharedNS] = list.gres( "\"", "" ); 00213 setNamespaces( map ); 00214 // read namespace - delimiter 00215 namespaceDelim entries = config.entryMap( config.group() ); 00216 namespaceDelim namespaceToDelimiter; 00217 for ( namespaceDelim::ConstIterator it = entries.begin(); 00218 it != entries.end(); ++it ) { 00219 if ( it.key().startsWith( "Namespace:" ) ) { 00220 TQString key = it.key().right( it.key().length() - 10 ); 00221 namespaceToDelimiter[key] = it.data(); 00222 } 00223 } 00224 setNamespaceToDelimiter( namespaceToDelimiter ); 00225 mOldPrefix = config.readEntry( "prefix" ); 00226 if ( !mOldPrefix.isEmpty() ) { 00227 makeConnection(); 00228 } 00229 localBlacklistFromStringList( config.readListEntry( "locallyUnsubscribedFolders" ) ); 00230 } 00231 00232 void ImapAccountBase::writeConfig( TDEConfig/*Base*/ & config ) /*const*/ { 00233 NetworkAccount::writeConfig( config ); 00234 00235 config.writeEntry( "auto-expunge", autoExpunge() ); 00236 config.writeEntry( "hidden-folders", hiddenFolders() ); 00237 config.writeEntry( "subscribed-folders", onlySubscribedFolders() ); 00238 config.writeEntry( "locally-subscribed-folders", onlyLocallySubscribedFolders() ); 00239 config.writeEntry( "loadondemand", loadOnDemand() ); 00240 config.writeEntry( "listOnlyOpenFolders", listOnlyOpenFolders() ); 00241 config.writeEntry( "capabilities", mCapabilities ); 00242 TQString data; 00243 for ( nsMap::Iterator it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) { 00244 if ( !it.data().isEmpty() ) { 00245 data = "\"" + it.data().join("\",\"") + "\""; 00246 config.writeEntry( TQString::number( it.key() ), data ); 00247 } 00248 } 00249 TQString key; 00250 for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin(); 00251 it != mNamespaceToDelimiter.end(); ++it ) { 00252 key = "Namespace:" + it.key(); 00253 config.writeEntry( key, it.data() ); 00254 } 00255 config.writeEntry( "locallyUnsubscribedFolders", locallyBlacklistedFolders() ); 00256 } 00257 00258 // 00259 // 00260 // Network processing 00261 // 00262 // 00263 00264 MetaData ImapAccountBase::slaveConfig() const { 00265 MetaData m = NetworkAccount::slaveConfig(); 00266 00267 m.insert( "auth", auth() ); 00268 if ( autoExpunge() ) 00269 m.insert( "expunge", "auto" ); 00270 00271 return m; 00272 } 00273 00274 ImapAccountBase::ConnectionState ImapAccountBase::makeConnection() 00275 { 00276 if ( mSlave && mSlaveConnected ) { 00277 return Connected; 00278 } 00279 if ( mPasswordDialogIsActive ) return Connecting; 00280 00281 if( mAskAgain || ( ( passwd().isEmpty() || login().isEmpty() ) && 00282 auth() != "GSSAPI" ) ) { 00283 00284 Q_ASSERT( !mSlave ); // disconnected on 'wrong login' error already, or first try 00285 TQString log = login(); 00286 TQString pass = passwd(); 00287 // We init "store" to true to indicate that we want to have the 00288 // "keep password" checkbox. Then, we set [Passwords]Keep to 00289 // storePasswd(), so that the checkbox in the dialog will be 00290 // init'ed correctly: 00291 TDEConfigGroup passwords( TDEGlobal::config(), "Passwords" ); 00292 passwords.writeEntry( "Keep", storePasswd() ); 00293 TQString msg = i18n("You need to supply a username and a password to " 00294 "access this mailbox."); 00295 mPasswordDialogIsActive = true; 00296 00297 PasswordDialog dlg( msg, log, true /* store pw */, true, KMKernel::self()->mainWin() ); 00298 dlg.setPlainCaption( i18n("Authorization Dialog") ); 00299 dlg.addCommentLine( i18n("Account:"), name() ); 00300 int ret = dlg.exec(); 00301 if (ret != TQDialog::Accepted ) { 00302 mPasswordDialogIsActive = false; 00303 mAskAgain = false; 00304 emit connectionResult( TDEIO::ERR_USER_CANCELED, TQString() ); 00305 return Error; 00306 } 00307 mPasswordDialogIsActive = false; 00308 // The user has been given the chance to change login and 00309 // password, so copy both from the dialog: 00310 setPasswd( dlg.password(), dlg.keepPassword() ); 00311 setLogin( dlg.username() ); 00312 mAskAgain = false; 00313 } 00314 // already waiting for a connection? 00315 if ( mSlave && !mSlaveConnected ) return Connecting; 00316 00317 mSlaveConnected = false; 00318 mSlave = TDEIO::Scheduler::getConnectedSlave( getUrl(), slaveConfig() ); 00319 if ( !mSlave ) { 00320 KMessageBox::error(0, i18n("Could not start process for %1.") 00321 .arg( getUrl().protocol() ) ); 00322 return Error; 00323 } 00324 if ( mSlave->isConnected() ) { 00325 slotSchedulerSlaveConnected( mSlave ); 00326 return Connected; 00327 } 00328 00329 return Connecting; 00330 } 00331 00332 bool ImapAccountBase::handleJobError( TDEIO::Job *job, const TQString& context, bool abortSync ) 00333 { 00334 JobIterator it = findJob( job ); 00335 if ( it != jobsEnd() && (*it).progressItem ) 00336 { 00337 (*it).progressItem->setComplete(); 00338 (*it).progressItem = 0; 00339 } 00340 return handleError( job->error(), job->errorText(), job, context, abortSync ); 00341 } 00342 00343 // Called when we're really all done. 00344 void ImapAccountBase::postProcessNewMail( bool showStatusMsg ) { 00345 setCheckingMail(false); 00346 int newMails = 0; 00347 if ( mCountUnread > 0 && mCountUnread > mCountLastUnread ) { 00348 newMails = mCountUnread - mCountLastUnread; 00349 mCountLastUnread = mCountUnread; 00350 mCountUnread = 0; 00351 checkDone( true, CheckOK ); 00352 } else { 00353 mCountUnread = 0; 00354 checkDone( false, CheckOK ); 00355 } 00356 if ( showStatusMsg ) 00357 BroadcastStatus::instance()->setStatusMsgTransmissionCompleted( 00358 name(), newMails); 00359 } 00360 00361 //----------------------------------------------------------------------------- 00362 void ImapAccountBase::changeSubscription( bool subscribe, const TQString& imapPath, bool quiet ) 00363 { 00364 // change the subscription of the folder 00365 KURL url = getUrl(); 00366 url.setPath(imapPath); 00367 00368 TQByteArray packedArgs; 00369 TQDataStream stream( packedArgs, IO_WriteOnly); 00370 00371 if (subscribe) 00372 stream << (int) 'u' << url; 00373 else 00374 stream << (int) 'U' << url; 00375 00376 // create the TDEIO-job 00377 if ( makeConnection() != Connected ) 00378 return;// ## doesn't handle Connecting 00379 TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false); 00380 TDEIO::Scheduler::assignJobToSlave(mSlave, job); 00381 jobData jd( url.url(), NULL ); 00382 // a bit of a hack to save one slot 00383 if (subscribe) jd.onlySubscribed = true; 00384 else jd.onlySubscribed = false; 00385 jd.quiet = quiet; 00386 insertJob(job, jd); 00387 00388 connect(job, TQT_SIGNAL(result(TDEIO::Job *)), 00389 TQT_SLOT(slotSubscriptionResult(TDEIO::Job *))); 00390 } 00391 00392 //----------------------------------------------------------------------------- 00393 void ImapAccountBase::slotSubscriptionResult( TDEIO::Job * job ) 00394 { 00395 // result of a subscription-job 00396 JobIterator it = findJob( job ); 00397 if ( it == jobsEnd() ) return; 00398 bool onlySubscribed = (*it).onlySubscribed; 00399 TQString path = static_cast<TDEIO::SimpleJob*>(job)->url().path(); 00400 if (job->error()) 00401 { 00402 if ( !(*it).quiet ) 00403 handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' ); 00404 emit subscriptionChangeFailed( job->errorString() ); 00405 // ## emit subscriptionChanged here in case anyone needs it to support continue/cancel 00406 } 00407 else 00408 { 00409 emit subscriptionChanged( path, onlySubscribed ); 00410 if (mSlave) removeJob(job); 00411 } 00412 } 00413 00414 //----------------------------------------------------------------------------- 00415 // TODO imapPath can be removed once parent can be a KMFolderImapBase or whatever 00416 void ImapAccountBase::getUserRights( KMFolder* parent, const TQString& imapPath ) 00417 { 00418 // There isn't much point in asking the server about a user's rights on his own inbox, 00419 // it might not be the effective permissions (at least with Cyrus, one can admin his own inbox, 00420 // even after a SETACL that removes the admin permissions. Other imap servers apparently 00421 // don't even allow removing one's own admin permission, so this code won't hurt either). 00422 if ( imapPath == "/INBOX/" ) { 00423 if ( parent->folderType() == KMFolderTypeImap ) 00424 static_cast<KMFolderImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok ); 00425 else if ( parent->folderType() == KMFolderTypeCachedImap ) 00426 static_cast<KMFolderCachedImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok ); 00427 emit receivedUserRights( parent ); // warning, you need to connect first to get that one 00428 return; 00429 } 00430 00431 KURL url = getUrl(); 00432 url.setPath(imapPath); 00433 00434 ACLJobs::GetUserRightsJob* job = ACLJobs::getUserRights( mSlave, url ); 00435 00436 jobData jd( url.url(), parent ); 00437 jd.cancellable = true; 00438 insertJob(job, jd); 00439 00440 connect(job, TQT_SIGNAL(result(TDEIO::Job *)), 00441 TQT_SLOT(slotGetUserRightsResult(TDEIO::Job *))); 00442 } 00443 00444 void ImapAccountBase::slotGetUserRightsResult( TDEIO::Job* _job ) 00445 { 00446 ACLJobs::GetUserRightsJob* job = static_cast<ACLJobs::GetUserRightsJob *>( _job ); 00447 JobIterator it = findJob( job ); 00448 if ( it == jobsEnd() ) return; 00449 00450 KMFolder* folder = (*it).parent; 00451 if ( job->error() ) { 00452 if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) // that's when the imap server doesn't support ACLs 00453 mACLSupport = false; 00454 else 00455 kdWarning(5006) << "slotGetUserRightsResult: " << job->errorString() << endl; 00456 } else { 00457 #ifndef NDEBUG 00458 //kdDebug(5006) << "User Rights: " << ACLJobs::permissionsToString( job->permissions() ) << endl; 00459 #endif 00460 } 00461 // Store the permissions 00462 if ( folder->folderType() == KMFolderTypeImap ) 00463 static_cast<KMFolderImap*>( folder->storage() )->setUserRights( job->permissions(), 00464 job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok ); 00465 else if ( folder->folderType() == KMFolderTypeCachedImap ) 00466 static_cast<KMFolderCachedImap*>( folder->storage() )->setUserRights( job->permissions(), 00467 job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok ); 00468 00469 if (mSlave) removeJob(job); 00470 emit receivedUserRights( folder ); 00471 } 00472 00473 //----------------------------------------------------------------------------- 00474 void ImapAccountBase::getACL( KMFolder* parent, const TQString& imapPath ) 00475 { 00476 KURL url = getUrl(); 00477 url.setPath(imapPath); 00478 00479 ACLJobs::GetACLJob* job = ACLJobs::getACL( mSlave, url ); 00480 jobData jd( url.url(), parent ); 00481 jd.cancellable = true; 00482 insertJob(job, jd); 00483 00484 connect(job, TQT_SIGNAL(result(TDEIO::Job *)), 00485 TQT_SLOT(slotGetACLResult(TDEIO::Job *))); 00486 } 00487 00488 void ImapAccountBase::slotGetACLResult( TDEIO::Job* _job ) 00489 { 00490 ACLJobs::GetACLJob* job = static_cast<ACLJobs::GetACLJob *>( _job ); 00491 JobIterator it = findJob( job ); 00492 if ( it == jobsEnd() ) return; 00493 00494 KMFolder* folder = (*it).parent; 00495 emit receivedACL( folder, job, job->entries() ); 00496 if (mSlave) removeJob(job); 00497 } 00498 00499 //----------------------------------------------------------------------------- 00500 // Do not remove imapPath, FolderDiaQuotaTab needs to call this with parent==0. 00501 void ImapAccountBase::getStorageQuotaInfo( KMFolder* parent, const TQString& imapPath ) 00502 { 00503 if ( !mSlave ) return; 00504 KURL url = getUrl(); 00505 url.setPath(imapPath); 00506 00507 QuotaJobs::GetStorageQuotaJob* job = QuotaJobs::getStorageQuota( mSlave, url ); 00508 jobData jd( url.url(), parent ); 00509 jd.cancellable = true; 00510 insertJob(job, jd); 00511 00512 connect(job, TQT_SIGNAL(result(TDEIO::Job *)), 00513 TQT_SLOT(slotGetStorageQuotaInfoResult(TDEIO::Job *))); 00514 } 00515 00516 void ImapAccountBase::slotGetStorageQuotaInfoResult( TDEIO::Job* _job ) 00517 { 00518 QuotaJobs::GetStorageQuotaJob* job = static_cast<QuotaJobs::GetStorageQuotaJob *>( _job ); 00519 JobIterator it = findJob( job ); 00520 if ( it == jobsEnd() ) return; 00521 if ( job->error() && job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) 00522 setHasNoQuotaSupport(); 00523 00524 KMFolder* folder = (*it).parent; // can be 0 00525 emit receivedStorageQuotaInfo( folder, job, job->storageQuotaInfo() ); 00526 if (mSlave) removeJob(job); 00527 } 00528 00529 void ImapAccountBase::slotNoopTimeout() 00530 { 00531 if ( mSlave ) { 00532 TQByteArray packedArgs; 00533 TQDataStream stream( packedArgs, IO_WriteOnly ); 00534 00535 stream << ( int ) 'N'; 00536 00537 TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false ); 00538 TDEIO::Scheduler::assignJobToSlave(mSlave, job); 00539 connect( job, TQT_SIGNAL(result( TDEIO::Job * ) ), 00540 this, TQT_SLOT( slotSimpleResult( TDEIO::Job * ) ) ); 00541 } else { 00542 /* Stop the timer, we have disconnected. We have to make sure it is 00543 started again when a new slave appears. */ 00544 mNoopTimer.stop(); 00545 } 00546 } 00547 00548 void ImapAccountBase::slotIdleTimeout() 00549 { 00550 if ( mSlave ) { 00551 TDEIO::Scheduler::disconnectSlave(mSlave); 00552 mSlave = 0; 00553 mSlaveConnected = false; 00554 /* As for the noop timer, we need to make sure this one is started 00555 again when a new slave goes up. */ 00556 mIdleTimer.stop(); 00557 } 00558 } 00559 00560 void ImapAccountBase::slotAbortRequested( KPIM::ProgressItem* item ) 00561 { 00562 if ( item ) 00563 item->setComplete(); 00564 killAllJobs(); 00565 } 00566 00567 00568 //----------------------------------------------------------------------------- 00569 void ImapAccountBase::slotSchedulerSlaveError(TDEIO::Slave *aSlave, int errorCode, 00570 const TQString &errorMsg) 00571 { 00572 if (aSlave != mSlave) return; 00573 handleError( errorCode, errorMsg, 0, TQString(), true ); 00574 if ( mAskAgain ) 00575 if ( makeConnection() != ImapAccountBase::Error ) 00576 return; 00577 00578 if ( !mSlaveConnected ) { 00579 mSlaveConnectionError = true; 00580 resetConnectionList( this ); 00581 if ( mSlave ) 00582 { 00583 TDEIO::Scheduler::disconnectSlave( slave() ); 00584 mSlave = 0; 00585 } 00586 } 00587 emit connectionResult( errorCode, errorMsg ); 00588 } 00589 00590 //----------------------------------------------------------------------------- 00591 void ImapAccountBase::slotSchedulerSlaveConnected(TDEIO::Slave *aSlave) 00592 { 00593 if (aSlave != mSlave) return; 00594 mSlaveConnected = true; 00595 mNoopTimer.start( 60000 ); // make sure we start sending noops 00596 emit connectionResult( 0, TQString() ); // success 00597 00598 if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) { 00599 connect( this, TQT_SIGNAL( namespacesFetched( const ImapAccountBase::nsDelimMap& ) ), 00600 this, TQT_SLOT( slotSaveNamespaces( const ImapAccountBase::nsDelimMap& ) ) ); 00601 getNamespaces(); 00602 } 00603 00604 // get capabilities 00605 TQByteArray packedArgs; 00606 TQDataStream stream( packedArgs, IO_WriteOnly); 00607 stream << (int) 'c'; 00608 TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false ); 00609 TDEIO::Scheduler::assignJobToSlave( mSlave, job ); 00610 connect( job, TQT_SIGNAL(infoMessage(TDEIO::Job*, const TQString&)), 00611 TQT_SLOT(slotCapabilitiesResult(TDEIO::Job*, const TQString&)) ); 00612 } 00613 00614 //----------------------------------------------------------------------------- 00615 void ImapAccountBase::slotCapabilitiesResult( TDEIO::Job*, const TQString& result ) 00616 { 00617 mCapabilities = TQStringList::split(' ', result.lower() ); 00618 kdDebug(5006) << "capabilities:" << mCapabilities << endl; 00619 } 00620 00621 //----------------------------------------------------------------------------- 00622 void ImapAccountBase::getNamespaces() 00623 { 00624 disconnect( this, TQT_SIGNAL( connectionResult(int, const TQString&) ), 00625 this, TQT_SLOT( getNamespaces() ) ); 00626 if ( makeConnection() != Connected || !mSlave ) { 00627 kdDebug(5006) << "getNamespaces - wait for connection" << endl; 00628 if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) { 00629 // when the connection is established slotSchedulerSlaveConnected notifies us 00630 } else { 00631 // getNamespaces was called by someone else 00632 connect( this, TQT_SIGNAL( connectionResult(int, const TQString&) ), 00633 this, TQT_SLOT( getNamespaces() ) ); 00634 } 00635 return; 00636 } 00637 00638 TQByteArray packedArgs; 00639 TQDataStream stream( packedArgs, IO_WriteOnly); 00640 stream << (int) 'n'; 00641 jobData jd; 00642 jd.total = 1; jd.done = 0; jd.cancellable = true; 00643 jd.progressItem = ProgressManager::createProgressItem( 00644 ProgressManager::getUniqueID(), 00645 i18n("Retrieving Namespaces"), 00646 TQString(), true, useSSL() || useTLS() ); 00647 jd.progressItem->setTotalItems( 1 ); 00648 connect ( jd.progressItem, 00649 TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ), 00650 this, 00651 TQT_SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) ); 00652 TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false ); 00653 TDEIO::Scheduler::assignJobToSlave( mSlave, job ); 00654 insertJob( job, jd ); 00655 connect( job, TQT_SIGNAL( infoMessage(TDEIO::Job*, const TQString&) ), 00656 TQT_SLOT( slotNamespaceResult(TDEIO::Job*, const TQString&) ) ); 00657 } 00658 00659 //----------------------------------------------------------------------------- 00660 void ImapAccountBase::slotNamespaceResult( TDEIO::Job* job, const TQString& str ) 00661 { 00662 JobIterator it = findJob( job ); 00663 if ( it == jobsEnd() ) return; 00664 00665 nsDelimMap map; 00666 namespaceDelim nsDelim; 00667 TQStringList ns = TQStringList::split( ",", str ); 00668 for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it ) { 00669 // split, allow empty parts as we can get empty namespaces 00670 TQStringList parts = TQStringList::split( "=", *it, true ); 00671 imapNamespace section = imapNamespace( parts[0].toInt() ); 00672 if ( map.contains( section ) ) { 00673 nsDelim = map[section]; 00674 } else { 00675 nsDelim.clear(); 00676 } 00677 // map namespace to delimiter 00678 nsDelim[parts[1]] = parts[2]; 00679 map[section] = nsDelim; 00680 } 00681 removeJob(it); 00682 00683 kdDebug(5006) << "namespaces fetched" << endl; 00684 emit namespacesFetched( map ); 00685 } 00686 00687 //----------------------------------------------------------------------------- 00688 void ImapAccountBase::slotSaveNamespaces( const ImapAccountBase::nsDelimMap& map ) 00689 { 00690 kdDebug(5006) << "slotSaveNamespaces " << name() << endl; 00691 // extract the needed information 00692 mNamespaces.clear(); 00693 mNamespaceToDelimiter.clear(); 00694 for ( uint i = 0; i < 3; ++i ) { 00695 imapNamespace section = imapNamespace( i ); 00696 namespaceDelim ns = map[ section ]; 00697 namespaceDelim::ConstIterator it; 00698 TQStringList list; 00699 for ( it = ns.begin(); it != ns.end(); ++it ) { 00700 list += it.key(); 00701 mNamespaceToDelimiter[ it.key() ] = it.data(); 00702 } 00703 if ( !list.isEmpty() ) { 00704 mNamespaces[section] = list; 00705 } 00706 } 00707 // see if we need to migrate an old prefix 00708 if ( !mOldPrefix.isEmpty() ) { 00709 migratePrefix(); 00710 } 00711 emit namespacesFetched(); 00712 } 00713 00714 //----------------------------------------------------------------------------- 00715 void ImapAccountBase::migratePrefix() 00716 { 00717 if ( !mOldPrefix.isEmpty() && mOldPrefix != "/" ) { 00718 // strip / 00719 if ( mOldPrefix.startsWith("/") ) { 00720 mOldPrefix = mOldPrefix.right( mOldPrefix.length()-1 ); 00721 } 00722 if ( mOldPrefix.endsWith("/") ) { 00723 mOldPrefix = mOldPrefix.left( mOldPrefix.length()-1 ); 00724 } 00725 TQStringList list = mNamespaces[PersonalNS]; 00726 bool done = false; 00727 for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) { 00728 if ( (*it).startsWith( mOldPrefix ) ) { 00729 // should be ok 00730 done = true; 00731 kdDebug(5006) << "migratePrefix - no migration needed" << endl; 00732 break; 00733 } 00734 } 00735 if ( !done ) { 00736 TQString msg = i18n("KMail has detected a prefix entry in the " 00737 "configuration of the account \"%1\" which is obsolete with the " 00738 "support of IMAP namespaces.").arg( name() ); 00739 if ( list.contains( "" ) ) { 00740 // replace empty entry with the old prefix 00741 list.remove( "" ); 00742 list += mOldPrefix; 00743 mNamespaces[PersonalNS] = list; 00744 if ( mNamespaceToDelimiter.contains( "" ) ) { 00745 TQString delim = mNamespaceToDelimiter[""]; 00746 mNamespaceToDelimiter.remove( "" ); 00747 mNamespaceToDelimiter[mOldPrefix] = delim; 00748 } 00749 kdDebug(5006) << "migratePrefix - replaced empty with " << mOldPrefix << endl; 00750 msg += i18n("The configuration was automatically migrated but you should check " 00751 "your account configuration."); 00752 } else if ( list.count() == 1 ) { 00753 // only one entry in the personal namespace so replace it 00754 TQString old = list.first(); 00755 list.clear(); 00756 list += mOldPrefix; 00757 mNamespaces[PersonalNS] = list; 00758 if ( mNamespaceToDelimiter.contains( old ) ) { 00759 TQString delim = mNamespaceToDelimiter[old]; 00760 mNamespaceToDelimiter.remove( old ); 00761 mNamespaceToDelimiter[mOldPrefix] = delim; 00762 } 00763 kdDebug(5006) << "migratePrefix - replaced single with " << mOldPrefix << endl; 00764 msg += i18n("The configuration was automatically migrated but you should check " 00765 "your account configuration."); 00766 } else { 00767 kdDebug(5006) << "migratePrefix - migration failed" << endl; 00768 msg += i18n("It was not possible to migrate your configuration automatically " 00769 "so please check your account configuration."); 00770 } 00771 KMessageBox::information( kmkernel->getKMMainWidget(), msg ); 00772 } 00773 } else 00774 { 00775 kdDebug(5006) << "migratePrefix - no migration needed" << endl; 00776 } 00777 mOldPrefix = ""; 00778 } 00779 00780 //----------------------------------------------------------------------------- 00781 TQString ImapAccountBase::namespaceForFolder( FolderStorage* storage ) 00782 { 00783 TQString path; 00784 if ( storage->folderType() == KMFolderTypeImap ) { 00785 path = static_cast<KMFolderImap*>( storage )->imapPath(); 00786 } else if ( storage->folderType() == KMFolderTypeCachedImap ) { 00787 path = static_cast<KMFolderCachedImap*>( storage )->imapPath(); 00788 } 00789 00790 nsMap::Iterator it; 00791 for ( it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) 00792 { 00793 TQStringList::Iterator strit; 00794 for ( strit = it.data().begin(); strit != it.data().end(); ++strit ) 00795 { 00796 TQString ns = *strit; 00797 if ( ns.endsWith("/") || ns.endsWith(".") ) { 00798 // strip delimiter for the comparison 00799 ns = ns.left( ns.length()-1 ); 00800 } 00801 // first ignore an empty prefix as it would match always 00802 if ( !ns.isEmpty() && path.find( ns ) != -1 ) { 00803 return (*strit); 00804 } 00805 } 00806 } 00807 return TQString(); 00808 } 00809 00810 //----------------------------------------------------------------------------- 00811 TQString ImapAccountBase::delimiterForNamespace( const TQString& prefix ) 00812 { 00813 //kdDebug(5006) << "delimiterForNamespace " << prefix << endl; 00814 // try to match exactly 00815 if ( mNamespaceToDelimiter.contains(prefix) ) { 00816 return mNamespaceToDelimiter[prefix]; 00817 } 00818 00819 // then try if the prefix is part of a namespace 00820 // exclude empty namespace 00821 for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin(); 00822 it != mNamespaceToDelimiter.end(); ++it ) { 00823 // the namespace definition sometimes contains the delimiter 00824 // make sure we also match this version 00825 TQString stripped = it.key().left( it.key().length() - 1 ); 00826 if ( !it.key().isEmpty() && 00827 ( prefix.contains( it.key() ) || prefix.contains( stripped ) ) ) { 00828 return it.data(); 00829 } 00830 } 00831 // see if we have an empty namespace 00832 // this should always be the case 00833 if ( mNamespaceToDelimiter.contains( "" ) ) { 00834 return mNamespaceToDelimiter[""]; 00835 } 00836 // well, we tried 00837 //kdDebug(5006) << "delimiterForNamespace - not found" << endl; 00838 return TQString(); 00839 } 00840 00841 //----------------------------------------------------------------------------- 00842 TQString ImapAccountBase::delimiterForFolder( FolderStorage* storage ) 00843 { 00844 TQString prefix = namespaceForFolder( storage ); 00845 TQString delim = delimiterForNamespace( prefix ); 00846 return delim; 00847 } 00848 00849 //----------------------------------------------------------------------------- 00850 void ImapAccountBase::slotSimpleResult(TDEIO::Job * job) 00851 { 00852 JobIterator it = findJob( job ); 00853 bool quiet = false; 00854 if (it != mapJobData.end()) { 00855 quiet = (*it).quiet; 00856 if ( !(job->error() && !quiet) ) // the error handler removes in that case 00857 removeJob(it); 00858 } 00859 if (job->error()) { 00860 if (!quiet) 00861 handleJobError(job, TQString() ); 00862 else { 00863 if ( job->error() == TDEIO::ERR_CONNECTION_BROKEN && slave() ) { 00864 // make sure ERR_CONNECTION_BROKEN is properly handled and the slave 00865 // disconnected even when quiet() 00866 TDEIO::Scheduler::disconnectSlave( slave() ); 00867 mSlave = 0; 00868 } 00869 if (job->error() == TDEIO::ERR_SLAVE_DIED) 00870 slaveDied(); 00871 } 00872 } 00873 } 00874 00875 //----------------------------------------------------------------------------- 00876 bool ImapAccountBase::handlePutError( TDEIO::Job* job, jobData& jd, KMFolder* folder ) 00877 { 00878 Q_ASSERT( !jd.msgList.isEmpty() ); 00879 KMMessage* msg = jd.msgList.first(); 00880 // Use double-quotes around the subject to keep the sentence readable, 00881 // but don't use double quotes around the sender since from() might return a double-quoted name already 00882 const TQString subject = msg->subject().isEmpty() ? i18n( "<unknown>" ) : TQString("\"%1\"").arg( msg->subject() ); 00883 const TQString from = msg->from().isEmpty() ? i18n( "<unknown>" ) : msg->from(); 00884 TQString myError = "<p><b>" + i18n("Error while uploading message") 00885 + "</b></p><p>" 00886 + i18n("Could not upload the message dated %1 from <i>%2</i> with subject <i>%3</i> to the server.").arg( msg->dateStr(), TQStyleSheet::escape( from ), TQStyleSheet::escape( subject ) ) 00887 + "</p><p>" 00888 + i18n("The destination folder was: <b>%1</b>.").arg( TQStyleSheet::escape( folder->prettyURL() ) ) 00889 + "</p><p>" 00890 + i18n("The server reported:") + "</p>"; 00891 return handleJobError( job, myError ); 00892 } 00893 00894 TQString ImapAccountBase::prettifyQuotaError( const TQString& _error, TDEIO::Job * job ) 00895 { 00896 TQString error = _error; 00897 if ( error.find( "quota", 0, false ) == -1 ) return error; 00898 // this is a quota error, prettify it a bit 00899 JobIterator it = findJob( job ); 00900 TQString quotaAsString( i18n("No detailed quota information available.") ); 00901 bool readOnly = false; 00902 if (it != mapJobData.end()) { 00903 const KMFolder * const folder = (*it).parent; 00904 if( !folder ) return _error; 00905 const KMFolderCachedImap * const imap = dynamic_cast<const KMFolderCachedImap*>( folder->storage() ); 00906 if ( imap ) { 00907 quotaAsString = imap->quotaInfo().toString(); 00908 } 00909 readOnly = folder->isReadOnly(); 00910 } 00911 error = i18n("The folder is too close to its quota limit. (%1)").arg( quotaAsString ); 00912 if ( readOnly ) { 00913 error += i18n("\nSince you do not have write privileges on this folder, " 00914 "please ask the owner of the folder to free up some space in it."); 00915 } 00916 return error; 00917 } 00918 00919 //----------------------------------------------------------------------------- 00920 bool ImapAccountBase::handleError( int errorCode, const TQString &errorMsg, TDEIO::Job* job, const TQString& context, bool abortSync ) 00921 { 00922 // Copy job's data before a possible killAllJobs 00923 TQStringList errors; 00924 if ( job && job->error() != TDEIO::ERR_SLAVE_DEFINED /*workaround for tdelibs-3.2*/) 00925 errors = job->detailedErrorStrings(); 00926 00927 bool jobsKilled = true; 00928 switch( errorCode ) { 00929 case TDEIO::ERR_SLAVE_DIED: slaveDied(); killAllJobs( true ); break; 00930 case TDEIO::ERR_COULD_NOT_AUTHENTICATE: // bad password 00931 mAskAgain = true; 00932 // fallthrough intended 00933 case TDEIO::ERR_CONNECTION_BROKEN: 00934 case TDEIO::ERR_COULD_NOT_CONNECT: 00935 case TDEIO::ERR_SERVER_TIMEOUT: 00936 // These mean that we'll have to reconnect on the next attempt, so disconnect and set mSlave to 0. 00937 killAllJobs( true ); 00938 break; 00939 case TDEIO::ERR_COULD_NOT_LOGIN: 00940 case TDEIO::ERR_USER_CANCELED: 00941 killAllJobs( false ); 00942 break; 00943 default: 00944 if ( abortSync ) 00945 killAllJobs( false ); 00946 else 00947 jobsKilled = false; 00948 break; 00949 } 00950 00951 // check if we still display an error 00952 if ( !mErrorDialogIsActive && errorCode != TDEIO::ERR_USER_CANCELED ) { 00953 mErrorDialogIsActive = true; 00954 TQString msg = context + '\n' + prettifyQuotaError( TDEIO::buildErrorString( errorCode, errorMsg ), job ); 00955 TQString caption = i18n("Error"); 00956 00957 if ( jobsKilled || errorCode == TDEIO::ERR_COULD_NOT_LOGIN ) { 00958 if ( errorCode == TDEIO::ERR_SERVER_TIMEOUT || errorCode == TDEIO::ERR_CONNECTION_BROKEN ) { 00959 msg = i18n("The connection to the server %1 was unexpectedly closed or timed out. It will be re-established automatically if possible."). 00960 arg( name() ); 00961 KMessageBox::information( TQT_TQWIDGET(kapp->activeWindow()), msg, caption, "kmailConnectionBrokenErrorDialog" ); 00962 // Show it in the status bar, in case the user has ticked "don't show again" 00963 if ( errorCode == TDEIO::ERR_CONNECTION_BROKEN ) 00964 KPIM::BroadcastStatus::instance()->setStatusMsg( 00965 i18n( "The connection to account %1 was broken." ).arg( name() ) ); 00966 else if ( errorCode == TDEIO::ERR_SERVER_TIMEOUT ) 00967 KPIM::BroadcastStatus::instance()->setStatusMsg( 00968 i18n( "The connection to account %1 timed out." ).arg( name() ) ); 00969 } else { 00970 if ( !errors.isEmpty() ) 00971 KMessageBox::detailedError( TQT_TQWIDGET(kapp->activeWindow()), msg, errors.join("\n").prepend("<qt>"), caption ); 00972 else 00973 KMessageBox::error( TQT_TQWIDGET(kapp->activeWindow()), msg, caption ); 00974 } 00975 } else { // i.e. we have a chance to continue, ask the user about it 00976 if ( errors.count() >= 3 ) { // there is no detailedWarningContinueCancel... (#86517) 00977 TQString error = prettifyQuotaError( errors[1], job ); 00978 msg = TQString( "<qt>") + context + error + '\n' + errors[2]; 00979 caption = errors[0]; 00980 } 00981 int ret = KMessageBox::warningContinueCancel( TQT_TQWIDGET(kapp->activeWindow()), msg, caption ); 00982 if ( ret == KMessageBox::Cancel ) { 00983 jobsKilled = true; 00984 killAllJobs( false ); 00985 } 00986 } 00987 mErrorDialogIsActive = false; 00988 } else { 00989 if ( mErrorDialogIsActive ) 00990 kdDebug(5006) << "suppressing error:" << errorMsg << endl; 00991 } 00992 if ( job && !jobsKilled ) 00993 removeJob( job ); 00994 return !jobsKilled; // jobsKilled==false -> continue==true 00995 } 00996 00997 //----------------------------------------------------------------------------- 00998 void ImapAccountBase::cancelMailCheck() 00999 { 01000 TQMap<TDEIO::Job*, jobData>::Iterator it = mapJobData.begin(); 01001 while ( it != mapJobData.end() ) { 01002 kdDebug(5006) << "cancelMailCheck: job is cancellable: " << (*it).cancellable << endl; 01003 if ( (*it).cancellable ) { 01004 it.key()->kill(); 01005 TQMap<TDEIO::Job*, jobData>::Iterator rmit = it; 01006 ++it; 01007 mapJobData.remove( rmit ); 01008 // We killed a job -> this kills the slave 01009 mSlave = 0; 01010 } else 01011 ++it; 01012 } 01013 01014 for( TQPtrListIterator<FolderJob> it( mJobList ); it.current(); ++it ) { 01015 if ( it.current()->isCancellable() ) { 01016 FolderJob* job = it.current(); 01017 job->setPassiveDestructor( true ); 01018 mJobList.remove( job ); 01019 delete job; 01020 } else 01021 ++it; 01022 } 01023 } 01024 01025 //----------------------------------------------------------------------------- 01026 void ImapAccountBase::processNewMailInFolder( KMFolder* folder, FolderListType type /*= Single*/ ) 01027 { 01028 if ( mFoldersQueuedForChecking.contains( folder ) ) 01029 return; 01030 mFoldersQueuedForChecking.append( folder ); 01031 mCheckingSingleFolder = ( type == Single ); 01032 if ( checkingMail() ) 01033 { 01034 disconnect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), 01035 this, TQT_SLOT( slotCheckQueuedFolders() ) ); 01036 connect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), 01037 this, TQT_SLOT( slotCheckQueuedFolders() ) ); 01038 } else { 01039 slotCheckQueuedFolders(); 01040 } 01041 } 01042 01043 //----------------------------------------------------------------------------- 01044 void ImapAccountBase::slotCheckQueuedFolders() 01045 { 01046 disconnect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), 01047 this, TQT_SLOT( slotCheckQueuedFolders() ) ); 01048 01049 TQValueList<TQGuardedPtr<KMFolder> > mSaveList = mMailCheckFolders; 01050 mMailCheckFolders = mFoldersQueuedForChecking; 01051 if ( kmkernel->acctMgr() ) 01052 kmkernel->acctMgr()->singleCheckMail(this, true); 01053 mMailCheckFolders = mSaveList; 01054 mFoldersQueuedForChecking.clear(); 01055 } 01056 01057 //----------------------------------------------------------------------------- 01058 bool ImapAccountBase::checkingMail( KMFolder *folder ) 01059 { 01060 if (checkingMail() && mFoldersQueuedForChecking.contains(folder)) 01061 return true; 01062 return false; 01063 } 01064 01065 //----------------------------------------------------------------------------- 01066 void ImapAccountBase::handleBodyStructure( TQDataStream & stream, KMMessage * msg, 01067 const AttachmentStrategy *as ) 01068 { 01069 mBodyPartList.clear(); 01070 mCurrentMsg = msg; 01071 // first delete old parts as we construct our own 01072 msg->deleteBodyParts(); 01073 // make the parts and fill the mBodyPartList 01074 constructParts( stream, 1, 0, 0, msg->asDwMessage() ); 01075 if ( mBodyPartList.count() == 1 ) // we directly set the body later, at partsToLoad below 01076 msg->deleteBodyParts(); 01077 01078 if ( !as ) 01079 { 01080 kdWarning(5006) << k_funcinfo << " - found no attachment strategy!" << endl; 01081 return; 01082 } 01083 01084 // see what parts have to loaded according to attachmentstrategy 01085 BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as ); 01086 visitor->visit( mBodyPartList ); 01087 TQPtrList<KMMessagePart> parts = visitor->partsToLoad(); 01088 delete visitor; 01089 TQPtrListIterator<KMMessagePart> it( parts ); 01090 KMMessagePart *part; 01091 int partsToLoad = 0; 01092 // check how many parts we have to load 01093 while ( (part = it.current()) != 0 ) 01094 { 01095 ++it; 01096 if ( part->loadPart() ) 01097 { 01098 ++partsToLoad; 01099 } 01100 } 01101 // if the only body part is not text, part->loadPart() would return false 01102 // and that part is never loaded, so make sure it loads. 01103 // it seems that TEXT does load the single body part even if it is not text/* 01104 if ( mBodyPartList.count() == 1 && partsToLoad == 0 ) 01105 partsToLoad = 1; // this causes the next test to succeed, and loads the whole message 01106 01107 if ( (mBodyPartList.count() * 0.5) < partsToLoad ) 01108 { 01109 // more than 50% of the parts have to be loaded anyway so it is faster 01110 // to load the message completely 01111 kdDebug(5006) << "Falling back to normal mode" << endl; 01112 FolderJob *job = msg->parent()->createJob( 01113 msg, FolderJob::tGetMessage, 0, "TEXT" ); 01114 job->start(); 01115 return; 01116 } 01117 it.toFirst(); 01118 while ( (part = it.current()) != 0 ) 01119 { 01120 ++it; 01121 kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier() 01122 << " (" << part->originalContentTypeStr() << ")" << endl; 01123 if ( part->loadHeaders() ) 01124 { 01125 kdDebug(5006) << "load HEADER" << endl; 01126 FolderJob *job = msg->parent()->createJob( 01127 msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" ); 01128 job->start(); 01129 } 01130 if ( part->loadPart() ) 01131 { 01132 kdDebug(5006) << "load Part" << endl; 01133 FolderJob *job = msg->parent()->createJob( 01134 msg, FolderJob::tGetMessage, 0, part->partSpecifier() ); 01135 job->start(); 01136 } 01137 } 01138 } 01139 01140 //----------------------------------------------------------------------------- 01141 void ImapAccountBase::constructParts( TQDataStream & stream, int count, KMMessagePart* parentKMPart, 01142 DwBodyPart * parent, const DwMessage * dwmsg ) 01143 { 01144 int children; 01145 for (int i = 0; i < count; i++) 01146 { 01147 stream >> children; 01148 KMMessagePart* part = new KMMessagePart( stream ); 01149 part->setParent( parentKMPart ); 01150 mBodyPartList.append( part ); 01151 kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier() 01152 << " of type " << part->originalContentTypeStr() << endl; 01153 DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part ); 01154 01155 if ( parent ) 01156 { 01157 // add to parent body 01158 parent->Body().AddBodyPart( dwpart ); 01159 dwpart->Parse(); 01160 // kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent 01161 // << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl; 01162 } else if ( part->partSpecifier() != "0" && 01163 !part->partSpecifier().endsWith(".HEADER") ) 01164 { 01165 // add to message 01166 dwmsg->Body().AddBodyPart( dwpart ); 01167 dwpart->Parse(); 01168 // kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent 01169 // << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl; 01170 } else 01171 dwpart = 0; 01172 01173 if ( !parentKMPart ) 01174 parentKMPart = part; 01175 01176 if (children > 0) 01177 { 01178 DwBodyPart* newparent = dwpart; 01179 const DwMessage* newmsg = dwmsg; 01180 if ( part->originalContentTypeStr() == "MESSAGE/RFC822" && dwpart && 01181 dwpart->Body().Message() ) 01182 { 01183 // set the encapsulated message as the new message 01184 newparent = 0; 01185 newmsg = dwpart->Body().Message(); 01186 } 01187 KMMessagePart* newParentKMPart = part; 01188 if ( part->partSpecifier().endsWith(".HEADER") ) // we don't want headers as parent 01189 newParentKMPart = parentKMPart; 01190 01191 constructParts( stream, children, newParentKMPart, newparent, newmsg ); 01192 } 01193 } 01194 } 01195 01196 //----------------------------------------------------------------------------- 01197 void ImapAccountBase::setImapStatus( KMFolder* folder, const TQString& path, const TQCString& flags ) 01198 { 01199 // set the status on the server, the uids are integrated in the path 01200 kdDebug(5006) << "setImapStatus path=" << path << " to: " << flags << endl; 01201 KURL url = getUrl(); 01202 url.setPath(path); 01203 01204 TQByteArray packedArgs; 01205 TQDataStream stream( packedArgs, IO_WriteOnly); 01206 01207 stream << (int) 'S' << url << flags; 01208 01209 if ( makeConnection() != Connected ) 01210 return; // can't happen with dimap 01211 01212 TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false); 01213 TDEIO::Scheduler::assignJobToSlave(slave(), job); 01214 ImapAccountBase::jobData jd( url.url(), folder ); 01215 jd.path = path; 01216 insertJob(job, jd); 01217 connect(job, TQT_SIGNAL(result(TDEIO::Job *)), 01218 TQT_SLOT(slotSetStatusResult(TDEIO::Job *))); 01219 } 01220 01221 void ImapAccountBase::setImapSeenStatus(KMFolder * folder, const TQString & path, bool seen) 01222 { 01223 KURL url = getUrl(); 01224 url.setPath(path); 01225 01226 TQByteArray packedArgs; 01227 TQDataStream stream( packedArgs, IO_WriteOnly); 01228 01229 stream << (int) 's' << url << seen; 01230 01231 if ( makeConnection() != Connected ) 01232 return; // can't happen with dimap 01233 01234 TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false); 01235 TDEIO::Scheduler::assignJobToSlave(slave(), job); 01236 ImapAccountBase::jobData jd( url.url(), folder ); 01237 jd.path = path; 01238 insertJob(job, jd); 01239 connect(job, TQT_SIGNAL(result(TDEIO::Job *)), 01240 TQT_SLOT(slotSetStatusResult(TDEIO::Job *))); 01241 } 01242 01243 //----------------------------------------------------------------------------- 01244 void ImapAccountBase::slotSetStatusResult(TDEIO::Job * job) 01245 { 01246 ImapAccountBase::JobIterator it = findJob(job); 01247 if ( it == jobsEnd() ) return; 01248 int errorCode = job->error(); 01249 KMFolder * const parent = (*it).parent; 01250 const TQString path = (*it).path; 01251 if (errorCode && errorCode != TDEIO::ERR_CANNOT_OPEN_FOR_WRITING) 01252 { 01253 bool cont = handleJobError( job, i18n( "Error while uploading status of messages to server: " ) + '\n' ); 01254 emit imapStatusChanged( parent, path, cont ); 01255 } 01256 else 01257 { 01258 emit imapStatusChanged( parent, path, true ); 01259 removeJob(it); 01260 } 01261 } 01262 01263 //----------------------------------------------------------------------------- 01264 void ImapAccountBase::setFolder(KMFolder* folder, bool addAccount) 01265 { 01266 if (folder) 01267 { 01268 folder->setSystemLabel(name()); 01269 folder->setId(id()); 01270 } 01271 NetworkAccount::setFolder(folder, addAccount); 01272 } 01273 01274 //----------------------------------------------------------------------------- 01275 void ImapAccountBase::removeJob( JobIterator& it ) 01276 { 01277 if( (*it).progressItem ) { 01278 (*it).progressItem->setComplete(); 01279 (*it).progressItem = 0; 01280 } 01281 mapJobData.remove( it ); 01282 } 01283 01284 //----------------------------------------------------------------------------- 01285 void KMail::ImapAccountBase::removeJob( TDEIO::Job* job ) 01286 { 01287 mapJobData.remove( job ); 01288 } 01289 01290 //----------------------------------------------------------------------------- 01291 KPIM::ProgressItem* ImapAccountBase::listDirProgressItem() 01292 { 01293 if ( !mListDirProgressItem ) 01294 { 01295 mListDirProgressItem = ProgressManager::createProgressItem( 01296 "ListDir" + name(), 01297 TQStyleSheet::escape( name() ), 01298 i18n("retrieving folders"), 01299 true, 01300 useSSL() || useTLS() ); 01301 connect ( mListDirProgressItem, 01302 TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ), 01303 this, 01304 TQT_SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) ); 01305 // Start with a guessed value of the old folder count plus 5%. As long 01306 // as the list of folders doesn't constantly change, that should be good 01307 // enough. 01308 unsigned int count = folderCount(); 01309 mListDirProgressItem->setTotalItems( count + (unsigned int)(count*0.05) ); 01310 } 01311 return mListDirProgressItem; 01312 } 01313 01314 //----------------------------------------------------------------------------- 01315 unsigned int ImapAccountBase::folderCount() const 01316 { 01317 if ( !rootFolder() || !rootFolder()->folder() || !rootFolder()->folder()->child() ) 01318 return 0; 01319 return kmkernel->imapFolderMgr()->folderCount( rootFolder()->folder()->child() ); 01320 } 01321 01322 //------------------------------------------------------------------------------ 01323 TQString ImapAccountBase::addPathToNamespace( const TQString& prefix ) 01324 { 01325 TQString myPrefix = prefix; 01326 if ( !myPrefix.startsWith( "/" ) ) { 01327 myPrefix = "/" + myPrefix; 01328 } 01329 if ( !myPrefix.endsWith( "/" ) ) { 01330 myPrefix += "/"; 01331 } 01332 01333 return myPrefix; 01334 } 01335 01336 //------------------------------------------------------------------------------ 01337 bool ImapAccountBase::isNamespaceFolder( TQString& name ) 01338 { 01339 TQStringList ns = mNamespaces[OtherUsersNS]; 01340 ns += mNamespaces[SharedNS]; 01341 ns += mNamespaces[PersonalNS]; 01342 TQString nameWithDelimiter; 01343 for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it ) 01344 { 01345 nameWithDelimiter = name + delimiterForNamespace( *it ); 01346 if ( *it == name || *it == nameWithDelimiter ) 01347 return true; 01348 } 01349 return false; 01350 } 01351 01352 //------------------------------------------------------------------------------ 01353 ImapAccountBase::nsDelimMap ImapAccountBase::namespacesWithDelimiter() 01354 { 01355 nsDelimMap map; 01356 nsMap::ConstIterator it; 01357 for ( uint i = 0; i < 3; ++i ) 01358 { 01359 imapNamespace section = imapNamespace( i ); 01360 TQStringList namespaces = mNamespaces[section]; 01361 namespaceDelim nsDelim; 01362 TQStringList::Iterator lit; 01363 for ( lit = namespaces.begin(); lit != namespaces.end(); ++lit ) 01364 { 01365 nsDelim[*lit] = delimiterForNamespace( *lit ); 01366 } 01367 map[section] = nsDelim; 01368 } 01369 return map; 01370 } 01371 01372 //------------------------------------------------------------------------------ 01373 TQString ImapAccountBase::createImapPath( const TQString& parent, 01374 const TQString& folderName ) 01375 { 01376 kdDebug(5006) << "createImapPath parent="<<parent<<", folderName="<<folderName<<endl; 01377 TQString newName = parent; 01378 // strip / at the end 01379 if ( newName.endsWith("/") ) { 01380 newName = newName.left( newName.length() - 1 ); 01381 } 01382 // add correct delimiter 01383 TQString delim = delimiterForNamespace( newName ); 01384 // should not happen... 01385 if ( delim.isEmpty() ) { 01386 delim = "/"; 01387 } 01388 if ( !newName.isEmpty() && 01389 !newName.endsWith( delim ) && !folderName.startsWith( delim ) ) { 01390 newName = newName + delim; 01391 } 01392 newName = newName + folderName; 01393 // add / at the end 01394 if ( !newName.endsWith("/") ) { 01395 newName = newName + "/"; 01396 } 01397 01398 return newName; 01399 } 01400 01401 //------------------------------------------------------------------------------ 01402 TQString ImapAccountBase::createImapPath( FolderStorage* parent, 01403 const TQString& folderName ) 01404 { 01405 TQString path; 01406 if ( parent->folderType() == KMFolderTypeImap ) { 01407 path = static_cast<KMFolderImap*>( parent )->imapPath(); 01408 } else if ( parent->folderType() == KMFolderTypeCachedImap ) { 01409 path = static_cast<KMFolderCachedImap*>( parent )->imapPath(); 01410 } else { 01411 // error 01412 return path; 01413 } 01414 01415 return createImapPath( path, folderName ); 01416 } 01417 01418 01419 bool ImapAccountBase::locallySubscribedTo( const TQString& imapPath ) 01420 { 01421 return mLocalSubscriptionBlackList.find( imapPath ) == mLocalSubscriptionBlackList.end(); 01422 } 01423 01424 void ImapAccountBase::changeLocalSubscription( const TQString& imapPath, bool subscribe ) 01425 { 01426 if ( subscribe ) { 01427 // find in blacklist and remove from it 01428 mLocalSubscriptionBlackList.erase( imapPath ); 01429 } else { 01430 // blacklist 01431 mLocalSubscriptionBlackList.insert( imapPath ); 01432 } 01433 } 01434 01435 01436 TQStringList ImapAccountBase::locallyBlacklistedFolders() const 01437 { 01438 TQStringList list; 01439 std::set<TQString>::const_iterator it = mLocalSubscriptionBlackList.begin(); 01440 std::set<TQString>::const_iterator end = mLocalSubscriptionBlackList.end(); 01441 for ( ; it != end ; ++it ) 01442 list.append( *it ); 01443 return list; 01444 } 01445 01446 void ImapAccountBase::localBlacklistFromStringList( const TQStringList &list ) 01447 { 01448 for( TQStringList::ConstIterator it = list.constBegin( ); it != list.constEnd( ); ++it ) 01449 mLocalSubscriptionBlackList.insert( *it ); 01450 } 01451 01452 } // namespace KMail 01453 01454 #include "imapaccountbase.moc"