kmfoldercachedimap.cpp
00001 00032 #ifdef HAVE_CONFIG_H 00033 #include <config.h> 00034 #endif 00035 00036 #include <errno.h> 00037 00038 #include <tqvaluevector.h> 00039 00040 #include "kmkernel.h" 00041 #include "kmfoldercachedimap.h" 00042 #include "undostack.h" 00043 #include "kmfoldermgr.h" 00044 #include "kmacctcachedimap.h" 00045 #include "accountmanager.h" 00046 using KMail::AccountManager; 00047 #include "kmailicalifaceimpl.h" 00048 #include "kmfolder.h" 00049 #include "kmglobal.h" 00050 #include "acljobs.h" 00051 #include "broadcaststatus.h" 00052 using KPIM::BroadcastStatus; 00053 #include "progressmanager.h" 00054 00055 using KMail::CachedImapJob; 00056 #include "imapaccountbase.h" 00057 using KMail::ImapAccountBase; 00058 #include "listjob.h" 00059 using KMail::ListJob; 00060 00061 #include "kmfolderseldlg.h" 00062 #include "kmcommands.h" 00063 #include "kmmainwidget.h" 00064 00065 #include <kapplication.h> 00066 #include <kmessagebox.h> 00067 #include <klocale.h> 00068 #include <kdebug.h> 00069 #include <kconfig.h> 00070 #include <kio/global.h> 00071 #include <kio/scheduler.h> 00072 #include <tqbuffer.h> 00073 #include <tqbuttongroup.h> 00074 #include <tqcombobox.h> 00075 #include <tqfile.h> 00076 #include <tqhbox.h> 00077 #include <tqlabel.h> 00078 #include <tqlayout.h> 00079 #include <tqradiobutton.h> 00080 #include <tqvaluelist.h> 00081 #include "annotationjobs.h" 00082 #include "quotajobs.h" 00083 using namespace KMail; 00084 #include <globalsettings.h> 00085 00086 #define UIDCACHE_VERSION 1 00087 #define MAIL_LOSS_DEBUGGING 0 00088 00089 static TQString incidencesForToString( KMFolderCachedImap::IncidencesFor r ) { 00090 switch (r) { 00091 case KMFolderCachedImap::IncForNobody: return "nobody"; 00092 case KMFolderCachedImap::IncForAdmins: return "admins"; 00093 case KMFolderCachedImap::IncForReaders: return "readers"; 00094 } 00095 return TQString(); // can't happen 00096 } 00097 00098 static KMFolderCachedImap::IncidencesFor incidencesForFromString( const TQString& str ) { 00099 if ( str == "nobody" ) return KMFolderCachedImap::IncForNobody; 00100 if ( str == "admins" ) return KMFolderCachedImap::IncForAdmins; 00101 if ( str == "readers" ) return KMFolderCachedImap::IncForReaders; 00102 return KMFolderCachedImap::IncForAdmins; // by default 00103 } 00104 00105 DImapTroubleShootDialog::DImapTroubleShootDialog( TQWidget* parent, 00106 const char* name ) 00107 : KDialogBase( Plain, i18n( "Troubleshooting IMAP Cache" ), 00108 Ok | Cancel, Cancel, parent, name, true ), 00109 rc( None ) 00110 { 00111 TQFrame* page = plainPage(); 00112 TQVBoxLayout *topLayout = new TQVBoxLayout( page, 0 ); 00113 // spell "lose" correctly. but don't cause a fuzzy. 00114 TQString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>" 00115 "<p>If you have problems with synchronizing an IMAP " 00116 "folder, you should first try rebuilding the index " 00117 "file. This will take some time to rebuild, but will " 00118 "not cause any problems.</p><p>If that is not enough, " 00119 "you can try refreshing the IMAP cache. If you do this, " 00120 "you will loose all your local changes for this folder " 00121 "and all its subfolders.</p>", 00122 "<p><b>Troubleshooting the IMAP cache.</b></p>" 00123 "<p>If you have problems with synchronizing an IMAP " 00124 "folder, you should first try rebuilding the index " 00125 "file. This will take some time to rebuild, but will " 00126 "not cause any problems.</p><p>If that is not enough, " 00127 "you can try refreshing the IMAP cache. If you do this, " 00128 "you will lose all your local changes for this folder " 00129 "and all its subfolders.</p>" ); 00130 topLayout->addWidget( new TQLabel( txt, page ) ); 00131 00132 mButtonGroup = new TQButtonGroup( 0 ); 00133 00134 mIndexButton = new TQRadioButton( page ); 00135 mIndexButton->setText( i18n( "Rebuild &Index" ) ); 00136 mButtonGroup->insert( mIndexButton ); 00137 topLayout->addWidget( mIndexButton ); 00138 00139 TQHBox *hbox = new TQHBox( page ); 00140 TQLabel *scopeLabel = new TQLabel( i18n( "Scope:" ), hbox ); 00141 scopeLabel->setEnabled( false ); 00142 mIndexScope = new TQComboBox( hbox ); 00143 mIndexScope->insertItem( i18n( "Only current folder" ) ); 00144 mIndexScope->insertItem( i18n( "Current folder and all subfolders" ) ); 00145 mIndexScope->insertItem( i18n( "All folders of this account" ) ); 00146 mIndexScope->setEnabled( false ); 00147 topLayout->addWidget( hbox ); 00148 00149 mCacheButton = new TQRadioButton( page ); 00150 mCacheButton->setText( i18n( "Refresh &Cache" ) ); 00151 mButtonGroup->insert( mCacheButton ); 00152 topLayout->addWidget( mCacheButton ); 00153 00154 enableButtonSeparator( true ); 00155 00156 connect ( mIndexButton, TQT_SIGNAL(toggled(bool)), mIndexScope, TQT_SLOT(setEnabled(bool)) ); 00157 connect ( mIndexButton, TQT_SIGNAL(toggled(bool)), scopeLabel, TQT_SLOT(setEnabled(bool)) ); 00158 connect( mButtonGroup, TQT_SIGNAL( clicked( int ) ), TQT_SLOT( slotChanged() ) ); 00159 connect( this, TQT_SIGNAL( okClicked () ), this, TQT_SLOT( slotDone() ) ); 00160 enableButtonOK( false ); 00161 } 00162 00163 int DImapTroubleShootDialog::run() 00164 { 00165 DImapTroubleShootDialog d; 00166 d.exec(); 00167 return d.rc; 00168 } 00169 00170 void DImapTroubleShootDialog::slotChanged() 00171 { 00172 enableButtonOK( mButtonGroup->selected() != 0 ); 00173 } 00174 00175 void DImapTroubleShootDialog::slotDone() 00176 { 00177 rc = None; 00178 if ( mIndexButton->isOn() ) 00179 rc = mIndexScope->currentItem(); 00180 else if ( mCacheButton->isOn() ) 00181 rc = RefreshCache; 00182 done( Ok ); 00183 } 00184 00185 KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName ) 00186 : KMFolderMaildir( folder, aName ), 00187 mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ), 00188 mSubfolderState( imapNoInformation ), 00189 mIncidencesFor( IncForAdmins ), 00190 mSharedSeenFlags( false ), 00191 mIsSelected( false ), 00192 mCheckFlags( true ), mReadOnly( false ), mAccount( NULL ), uidMapDirty( true ), 00193 uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ), 00194 mFoundAnIMAPDigest( false ), 00195 mUserRights( 0 ), mOldUserRights( 0 ), mUserRightsState( KMail::ACLJobs::NotFetchedYet ), 00196 mACLListState( KMail::ACLJobs::NotFetchedYet ), 00197 mSilentUpload( false ), 00198 /*mHoldSyncs( false ),*/ 00199 mFolderRemoved( false ), 00200 mRecurse( true ), 00201 mQuotaOnly( false ), 00202 mAnnotationFolderTypeChanged( false ), 00203 mIncidencesForChanged( false ), 00204 mSharedSeenFlagsChanged( false ), 00205 mStatusChangedLocally( false ), 00206 mPersonalNamespacesCheckDone( true ), 00207 mQuotaInfo(), mSomeSubFolderCloseToQuotaChanged( false ), mAlarmsBlocked( false ), 00208 mRescueCommandCount( 0 ), 00209 mPermanentFlags( 31 ) // assume standard flags by default (see imap4/imapinfo.h for bit fields values) 00210 { 00211 setUidValidity(""); 00212 // if we fail to read a uid file but there is one, nuke it 00213 if ( readUidCache() == -1 ) { 00214 if ( TQFile::exists( uidCacheLocation() ) ) { 00215 KMessageBox::error( 0, 00216 i18n( "The UID cache file for folder %1 could not be read. There " 00217 "could be a problem with file system permission, or it is corrupted." 00218 ).arg( folder->prettyURL() ) ); 00219 // try to unlink it, in case it was corruped. If it couldn't be read 00220 // because of permissions, this will fail, which is fine 00221 unlink( TQFile::encodeName( uidCacheLocation() ) ); 00222 } 00223 } 00224 00225 mProgress = 0; 00226 } 00227 00228 KMFolderCachedImap::~KMFolderCachedImap() 00229 { 00230 if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() ); 00231 writeConfig(); 00232 } 00233 00234 void KMFolderCachedImap::reallyDoClose( const char* owner ) 00235 { 00236 if( !mFolderRemoved ) { 00237 writeUidCache(); 00238 } 00239 KMFolderMaildir::reallyDoClose( owner ); 00240 } 00241 00242 void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent ) 00243 { 00244 setAccount( parent->account() ); 00245 // Now that we have an account, tell it that this folder was created: 00246 // if this folder was just removed, then we don't really want to remove it from the server. 00247 mAccount->removeDeletedFolder( imapPath() ); 00248 setUserRights( parent->userRights(), parent->userRightsState() ); 00249 } 00250 00251 void KMFolderCachedImap::readConfig() 00252 { 00253 KConfig* config = KMKernel::config(); 00254 KConfigGroupSaver saver( config, "Folder-" + folder()->idString() ); 00255 if( mImapPath.isEmpty() ) mImapPath = config->readEntry( "ImapPath" ); 00256 if( TQString( name() ).upper() == "INBOX" && mImapPath == "/INBOX/" ) 00257 { 00258 folder()->setLabel( i18n( "inbox" ) ); 00259 // for the icon 00260 folder()->setSystemFolder( true ); 00261 } 00262 mNoContent = config->readBoolEntry( "NoContent", false ); 00263 mReadOnly = config->readBoolEntry( "ReadOnly", false ); 00264 if ( !config->readEntry( "FolderAttributes" ).isEmpty() ) 00265 mFolderAttributes = config->readEntry( "FolderAttributes" ); 00266 00267 if ( mAnnotationFolderType != "FROMSERVER" ) { 00268 mAnnotationFolderType = config->readEntry( "Annotation-FolderType" ); 00269 // if there is an annotation, it has to be XML 00270 if ( !mAnnotationFolderType.isEmpty() && !mAnnotationFolderType.startsWith( "mail" ) ) 00271 kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML ); 00272 // kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath ) 00273 // << " readConfig: mAnnotationFolderType=" << mAnnotationFolderType << endl; 00274 } 00275 mIncidencesFor = incidencesForFromString( config->readEntry( "IncidencesFor" ) ); 00276 mAlarmsBlocked = config->readBoolEntry( "AlarmsBlocked", false ); 00277 // kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath ) 00278 // << " readConfig: mIncidencesFor=" << mIncidencesFor << endl; 00279 mSharedSeenFlags = config->readBoolEntry( "SharedSeenFlags", false ); 00280 00281 mUserRights = config->readNumEntry( "UserRights", 0 ); 00282 mUserRightsState = static_cast<KMail::ACLJobs::ACLFetchState>( 00283 config->readNumEntry( "UserRightsState", KMail::ACLJobs::NotFetchedYet ) ); 00284 mOldUserRights = mUserRights; 00285 00286 int storageQuotaUsage = config->readNumEntry( "StorageQuotaUsage", -1 ); 00287 int storageQuotaLimit = config->readNumEntry( "StorageQuotaLimit", -1 ); 00288 TQString storageQuotaRoot = config->readEntry( "StorageQuotaRoot", TQString() ); 00289 if ( !storageQuotaRoot.isNull() ) { // isEmpty() means we know there is no quota set 00290 mQuotaInfo.setName( "STORAGE" ); 00291 mQuotaInfo.setRoot( storageQuotaRoot ); 00292 00293 if ( storageQuotaUsage > -1 ) 00294 mQuotaInfo.setCurrent( storageQuotaUsage ); 00295 if ( storageQuotaLimit > -1 ) 00296 mQuotaInfo.setMax( storageQuotaLimit ); 00297 } 00298 00299 KMFolderMaildir::readConfig(); 00300 00301 mStatusChangedLocally = 00302 config->readBoolEntry( "StatusChangedLocally", false ); 00303 TQStringList uidsChanged = config->readListEntry( "UIDStatusChangedLocally" ); 00304 for ( TQStringList::iterator it = uidsChanged.begin(); it != uidsChanged.end(); it++ ) { 00305 mUIDsOfLocallyChangedStatuses.insert( ( *it ).toUInt() ); 00306 } 00307 00308 mAnnotationFolderTypeChanged = config->readBoolEntry( "AnnotationFolderTypeChanged", false ); 00309 mIncidencesForChanged = config->readBoolEntry( "IncidencesForChanged", false ); 00310 mSharedSeenFlagsChanged = config->readBoolEntry( "SharedSeenFlagsChanged", false ); 00311 00312 if ( mImapPath.isEmpty() ) { 00313 mImapPathCreation = config->readEntry("ImapPathCreation"); 00314 } 00315 00316 TQStringList delUids = config->readListEntry( "UIDSDeletedSinceLastSync" ); 00317 #if MAIL_LOSS_DEBUGGING 00318 kdDebug( 5006 ) << "READING IN UIDSDeletedSinceLastSync: " << folder()->prettyURL() << endl << uids << endl; 00319 #endif 00320 for ( TQStringList::iterator it = delUids.begin(); it != delUids.end(); it++ ) { 00321 mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0); 00322 } 00323 } 00324 00325 void KMFolderCachedImap::writeConfig() 00326 { 00327 // don't re-write the config of a removed folder, this has just been deleted in 00328 // the folder manager 00329 if ( mFolderRemoved ) 00330 return; 00331 00332 KConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() ); 00333 configGroup.writeEntry( "ImapPath", mImapPath ); 00334 configGroup.writeEntry( "NoContent", mNoContent ); 00335 configGroup.writeEntry( "ReadOnly", mReadOnly ); 00336 configGroup.writeEntry( "FolderAttributes", mFolderAttributes ); 00337 00338 // StatusChangedLocally is always false, as we use UIDStatusChangedLocally now 00339 configGroup.writeEntry( "StatusChangedLocally", false ); 00340 TQStringList uidsToWrite; 00341 for( std::set<ulong>::iterator it = mUIDsOfLocallyChangedStatuses.begin(); 00342 it != mUIDsOfLocallyChangedStatuses.end(); it++ ) { 00343 uidsToWrite.append( TQString::number( (*it) ) ); 00344 } 00345 configGroup.writeEntry( "UIDStatusChangedLocally", uidsToWrite ); 00346 if ( !mImapPathCreation.isEmpty() ) { 00347 if ( mImapPath.isEmpty() ) { 00348 configGroup.writeEntry( "ImapPathCreation", mImapPathCreation ); 00349 } else { 00350 configGroup.deleteEntry( "ImapPathCreation" ); 00351 } 00352 } 00353 if ( !mDeletedUIDsSinceLastSync.isEmpty() ) { 00354 TQValueList<ulong> uids = mDeletedUIDsSinceLastSync.keys(); 00355 TQStringList uidstrings; 00356 for( TQValueList<ulong>::iterator it = uids.begin(); it != uids.end(); it++ ) { 00357 uidstrings.append( TQString::number( (*it) ) ); 00358 } 00359 configGroup.writeEntry( "UIDSDeletedSinceLastSync", uidstrings ); 00360 #if MAIL_LOSS_DEBUGGING 00361 kdDebug( 5006 ) << "WRITING OUT UIDSDeletedSinceLastSync in: " << folder( )->prettyURL( ) << endl << uidstrings << endl; 00362 #endif 00363 } else { 00364 configGroup.deleteEntry( "UIDSDeletedSinceLastSync" ); 00365 } 00366 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); 00367 KMFolderMaildir::writeConfig(); 00368 } 00369 00370 void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig() 00371 { 00372 KConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() ); 00373 if ( !folder()->noContent() ) 00374 { 00375 configGroup.writeEntry( "AnnotationFolderTypeChanged", mAnnotationFolderTypeChanged ); 00376 configGroup.writeEntry( "Annotation-FolderType", mAnnotationFolderType ); 00377 configGroup.writeEntry( "IncidencesForChanged", mIncidencesForChanged ); 00378 configGroup.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor ) ); 00379 configGroup.writeEntry( "AlarmsBlocked", mAlarmsBlocked ); 00380 configGroup.writeEntry( "SharedSeenFlags", mSharedSeenFlags ); 00381 configGroup.writeEntry( "SharedSeenFlagsChanged", mSharedSeenFlagsChanged ); 00382 if ( mUserRightsState != KMail::ACLJobs::FetchFailed ) { // No point in overwriting valid results with invalid ones 00383 configGroup.writeEntry( "UserRights", mUserRights ); 00384 configGroup.writeEntry( "UserRightsState", mUserRightsState ); 00385 } 00386 00387 configGroup.deleteEntry( "StorageQuotaUsage"); 00388 configGroup.deleteEntry( "StorageQuotaRoot"); 00389 configGroup.deleteEntry( "StorageQuotaLimit"); 00390 00391 if ( mQuotaInfo.isValid() ) { 00392 if ( mQuotaInfo.current().isValid() ) { 00393 configGroup.writeEntry( "StorageQuotaUsage", mQuotaInfo.current().toInt() ); 00394 } 00395 if ( mQuotaInfo.max().isValid() ) { 00396 configGroup.writeEntry( "StorageQuotaLimit", mQuotaInfo.max().toInt() ); 00397 } 00398 configGroup.writeEntry( "StorageQuotaRoot", mQuotaInfo.root() ); 00399 } 00400 } 00401 } 00402 00403 int KMFolderCachedImap::create() 00404 { 00405 int rc = KMFolderMaildir::create(); 00406 // FIXME why the below? - till 00407 readConfig(); 00408 mUnreadMsgs = -1; 00409 return rc; 00410 } 00411 00412 void KMFolderCachedImap::remove() 00413 { 00414 mFolderRemoved = true; 00415 00416 TQString part1 = folder()->path() + "/." + dotEscape(name()); 00417 TQString uidCacheFile = part1 + ".uidcache"; 00418 // This is the account folder of an account that was just removed 00419 // When this happens, be sure to delete all traces of the cache 00420 if( TQFile::exists(uidCacheFile) ) 00421 unlink( TQFile::encodeName( uidCacheFile ) ); 00422 00423 FolderStorage::remove(); 00424 } 00425 00426 TQString KMFolderCachedImap::uidCacheLocation() const 00427 { 00428 TQString sLocation(folder()->path()); 00429 if (!sLocation.isEmpty()) sLocation += '/'; 00430 return sLocation + '.' + dotEscape(fileName()) + ".uidcache"; 00431 } 00432 00433 int KMFolderCachedImap::readUidCache() 00434 { 00435 TQFile uidcache( uidCacheLocation() ); 00436 if( uidcache.open( IO_ReadOnly ) ) { 00437 char buf[1024]; 00438 int len = uidcache.readLine( buf, sizeof(buf) ); 00439 if( len > 0 ) { 00440 int cacheVersion; 00441 sscanf( buf, "# KMail-UidCache V%d\n", &cacheVersion ); 00442 if( cacheVersion == UIDCACHE_VERSION ) { 00443 len = uidcache.readLine( buf, sizeof(buf) ); 00444 if( len > 0 ) { 00445 setUidValidity( TQString(TQString::fromLocal8Bit(buf)).stripWhiteSpace() ); 00446 len = uidcache.readLine( buf, sizeof(buf) ); 00447 if( len > 0 ) { 00448 #if MAIL_LOSS_DEBUGGING 00449 kdDebug(5006) << "Reading in last uid from cache: " << TQString::fromLocal8Bit(buf).stripWhiteSpace() << " in " << folder()->prettyURL() << endl; 00450 #endif 00451 // load the last known highest uid from the on disk cache 00452 setLastUid( TQString(TQString::fromLocal8Bit(buf)).stripWhiteSpace().toULong() ); 00453 return 0; 00454 } 00455 } 00456 } 00457 } 00458 } 00459 return -1; 00460 } 00461 00462 int KMFolderCachedImap::writeUidCache() 00463 { 00464 if( uidValidity().isEmpty() || uidValidity() == "INVALID" ) { 00465 // No info from the server yet, remove the file. 00466 if( TQFile::exists( uidCacheLocation() ) ) 00467 return unlink( TQFile::encodeName( uidCacheLocation() ) ); 00468 return 0; 00469 } 00470 #if MAIL_LOSS_DEBUGGING 00471 kdDebug(5006) << "Writing out UID cache lastuid: " << lastUid() << " in: " << folder()->prettyURL() << endl; 00472 #endif 00473 TQFile uidcache( uidCacheLocation() ); 00474 if( uidcache.open( IO_WriteOnly ) ) { 00475 TQTextStream str( &uidcache ); 00476 str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl; 00477 str << uidValidity() << endl; 00478 str << lastUid() << endl; 00479 uidcache.flush(); 00480 if ( uidcache.status() == IO_Ok ) { 00481 // fsync( uidcache.handle() ); /* this is probably overkill */ 00482 uidcache.close(); 00483 if ( uidcache.status() == IO_Ok ) 00484 return 0; 00485 } 00486 } 00487 KMessageBox::error( 0, 00488 i18n( "The UID cache file for folder %1 could not be written. There " 00489 "could be a problem with file system permission." ).arg( folder()->prettyURL() ) ); 00490 00491 return -1; 00492 } 00493 00494 void KMFolderCachedImap::reloadUidMap() 00495 { 00496 //kdDebug(5006) << "Reloading Uid Map " << endl; 00497 uidMap.clear(); 00498 open("reloadUdi"); 00499 for( int i = 0; i < count(); ++i ) { 00500 KMMsgBase *msg = getMsgBase( i ); 00501 if( !msg ) continue; 00502 ulong uid = msg->UID(); 00503 //kdDebug(5006) << "Inserting: " << i << " with uid: " << uid << endl; 00504 uidMap.insert( uid, i ); 00505 } 00506 close("reloadUdi"); 00507 uidMapDirty = false; 00508 } 00509 00510 KMMessage* KMFolderCachedImap::take(int idx) 00511 { 00512 uidMapDirty = true; 00513 rememberDeletion( idx ); 00514 return KMFolderMaildir::take(idx); 00515 } 00516 00517 void KMFolderCachedImap::takeTemporarily( int idx ) 00518 { 00519 KMFolderMaildir::take( idx ); 00520 } 00521 00522 // Add a message without clearing it's X-UID field. 00523 int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail, 00524 int* index_return ) 00525 { 00526 // Possible optimization: Only dirty if not filtered below 00527 ulong uid = msg->UID(); 00528 if( uid != 0 ) { 00529 uidMapDirty = true; 00530 } 00531 00532 KMFolderOpener openThis(folder(), "KMFolderCachedImap::addMsgInternal"); 00533 int rc = openThis.openResult(); 00534 if ( rc ) { 00535 kdDebug(5006) << k_funcinfo << "open: " << rc << " of folder: " << label() << endl; 00536 return rc; 00537 } 00538 00539 // Add the message 00540 rc = KMFolderMaildir::addMsg(msg, index_return); 00541 00542 if( newMail && ( imapPath() == "/INBOX/" || 00543 ( ( mUserRights != ACLJobs::Ok || userRights() & ACLJobs::Administer) 00544 && (contentsType() == ContentsTypeMail || GlobalSettings::self()->filterGroupwareFolders()) ) ) ) 00545 { 00546 // This is a new message. Filter it - maybe 00547 bool filter = false; 00548 if ( GlobalSettings::filterSourceFolders().isEmpty() ) { 00549 if ( imapPath() == "/INBOX/" ) 00550 filter = true; 00551 } else { 00552 if ( GlobalSettings::filterSourceFolders().contains( folder()->id() ) ) 00553 filter = true; 00554 } 00555 if ( filter ) 00556 mAccount->processNewMsg( msg ); 00557 } 00558 00559 return rc; 00560 } 00561 00562 /* Reimplemented from KMFolderMaildir */ 00563 int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return) 00564 { 00565 if ( !canAddMsgNow( msg, index_return ) ) return 0; 00566 // Add it to storage 00567 int rc = KMFolderMaildir::addMsgInternal(msg, index_return, true /*stripUID*/); 00568 return rc; 00569 } 00570 00571 void KMFolderCachedImap::rememberDeletion( int idx ) 00572 { 00573 KMMsgBase *msg = getMsgBase( idx ); 00574 assert(msg); 00575 long uid = msg->UID(); 00576 assert(uid>=0); 00577 mDeletedUIDsSinceLastSync.insert(uid, 0); 00578 kdDebug(5006) << "Explicit delete of UID " << uid << " at index: " << idx << " in " << folder()->prettyURL() << endl; 00579 } 00580 00581 /* Reimplemented from KMFolderMaildir */ 00582 void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet) 00583 { 00584 if ( contentsType() != ContentsTypeMail ) { 00585 kdDebug(5006) << k_funcinfo << "Deleting message with idx " << idx << " in folder " << label() << endl; 00586 } 00587 uidMapDirty = true; 00588 rememberDeletion( idx ); 00589 // Remove it from disk 00590 KMFolderMaildir::removeMsg(idx,imapQuiet); 00591 } 00592 00593 bool KMFolderCachedImap::canRemoveFolder() const { 00594 // If this has subfolders it can't be removed 00595 if( folder() && folder()->child() && folder()->child()->count() > 0 ) 00596 return false; 00597 00598 #if 0 00599 // No special condition here, so let base class decide 00600 return KMFolderMaildir::canRemoveFolder(); 00601 #endif 00602 return true; 00603 } 00604 00605 /* Reimplemented from KMFolderDir */ 00606 int KMFolderCachedImap::rename( const TQString& aName, 00607 KMFolderDir* /*aParent*/ ) 00608 { 00609 if ( account() == 0 || imapPath().isEmpty() ) { 00610 // This can happen when creating a folder and then renaming it without syncing before, 00611 // see https://issues.kolab.org/issue3658 00612 TQString err = i18n("You must synchronize with the server before renaming IMAP folders."); 00613 KMessageBox::error( 0, err ); 00614 return -1; 00615 } 00616 00617 TQString oldName = mAccount->renamedFolder( imapPath() ); 00618 if ( oldName.isEmpty() ) oldName = name(); 00619 if ( aName == oldName ) 00620 // Stupid user trying to rename it to it's old name :) 00621 return 0; 00622 00623 // Make the change appear to the user with setLabel, but we'll do the change 00624 // on the server during the next sync. The name() is the name at the time of 00625 // the last sync. Only rename if the new one is different. If it's the same, 00626 // don't rename, but also make sure the rename is reset, in the case of 00627 // A -> B -> A renames. 00628 if ( name() != aName ) 00629 mAccount->addRenamedFolder( imapPath(), folder()->label(), aName ); 00630 else 00631 mAccount->removeRenamedFolder( imapPath() ); 00632 00633 folder()->setLabel( aName ); 00634 emit nameChanged(); // for kmailicalifaceimpl 00635 00636 return 0; 00637 } 00638 00639 KMFolder* KMFolderCachedImap::trashFolder() const 00640 { 00641 TQString trashStr = account()->trash(); 00642 return kmkernel->dimapFolderMgr()->findIdString( trashStr ); 00643 } 00644 00645 void KMFolderCachedImap::setLastUid( ulong uid ) 00646 { 00647 #if MAIL_LOSS_DEBUGGING 00648 kdDebug(5006) << "Setting mLastUid to: " << uid << " in " << folder()->prettyURL() << endl; 00649 #endif 00650 mLastUid = uid; 00651 if( uidWriteTimer == -1 ) 00652 // Write in one minute 00653 uidWriteTimer = startTimer( 60000 ); 00654 } 00655 00656 void KMFolderCachedImap::timerEvent( TQTimerEvent* ) 00657 { 00658 killTimer( uidWriteTimer ); 00659 uidWriteTimer = -1; 00660 if ( writeUidCache() == -1 ) 00661 unlink( TQFile::encodeName( uidCacheLocation() ) ); 00662 } 00663 00664 ulong KMFolderCachedImap::lastUid() 00665 { 00666 return mLastUid; 00667 } 00668 00669 KMMsgBase* KMFolderCachedImap::findByUID( ulong uid ) 00670 { 00671 bool mapReloaded = false; 00672 if( uidMapDirty ) { 00673 reloadUidMap(); 00674 mapReloaded = true; 00675 } 00676 00677 TQMap<ulong,int>::Iterator it = uidMap.find( uid ); 00678 if( it != uidMap.end() ) { 00679 KMMsgBase *msg = getMsgBase( *it ); 00680 #if MAIL_LOSS_DEBUGGING 00681 kdDebug(5006) << "Folder: " << folder()->prettyURL() << endl; 00682 kdDebug(5006) << "UID " << uid << " is supposed to be in the map" << endl; 00683 kdDebug(5006) << "UID's index is to be " << *it << endl; 00684 kdDebug(5006) << "There is a message there? " << (msg != 0) << endl; 00685 if ( msg ) { 00686 kdDebug(5006) << "Its UID is: " << msg->UID() << endl; 00687 } 00688 #endif 00689 00690 if( msg && msg->UID() == uid ) 00691 return msg; 00692 kdDebug(5006) << "########## Didn't find uid: " << uid << "in cache athough it's supposed to be there!" << endl; 00693 } else { 00694 #if MAIL_LOSS_DEBUGGING 00695 kdDebug(5006) << "Didn't find uid: " << uid << "in cache!" << endl; 00696 #endif 00697 } 00698 // Not found by now 00699 // if( mapReloaded ) 00700 // Not here then 00701 return 0; 00702 // There could be a problem in the maps. Rebuild them and try again 00703 reloadUidMap(); 00704 it = uidMap.find( uid ); 00705 if( it != uidMap.end() ) 00706 // Since the uid map is just rebuilt, no need for the sanity check 00707 return getMsgBase( *it ); 00708 #if MAIL_LOSS_DEBUGGING 00709 else 00710 kdDebug(5006) << "Reloaded, but stil didn't find uid: " << uid << endl; 00711 #endif 00712 // Then it's not here 00713 return 0; 00714 } 00715 00716 // This finds and sets the proper account for this folder if it has 00717 // not been done 00718 KMAcctCachedImap *KMFolderCachedImap::account() const 00719 { 00720 if( (KMAcctCachedImap *)mAccount == 0 && kmkernel && kmkernel->acctMgr() ) { 00721 // Find the account 00722 mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->findByName( name() ) ); 00723 } 00724 00725 return mAccount; 00726 } 00727 00728 void KMFolderCachedImap::slotTroubleshoot() 00729 { 00730 const int rc = DImapTroubleShootDialog::run(); 00731 00732 if( rc == DImapTroubleShootDialog::RefreshCache ) { 00733 // Refresh cache 00734 if( !account() ) { 00735 KMessageBox::sorry( 0, i18n("No account setup for this folder.\n" 00736 "Please try running a sync before this.") ); 00737 return; 00738 } 00739 TQString str = i18n("Are you sure you want to refresh the IMAP cache of " 00740 "the folder %1 and all its subfolders?\nThis will " 00741 "remove all changes you have done locally to your " 00742 "folders.").arg( label() ); 00743 TQString s1 = i18n("Refresh IMAP Cache"); 00744 TQString s2 = i18n("&Refresh"); 00745 if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) == 00746 KMessageBox::Continue ) 00747 account()->invalidateIMAPFolders( this ); 00748 } else { 00749 // Rebuild index file 00750 switch ( rc ) { 00751 case DImapTroubleShootDialog::ReindexAll: 00752 { 00753 KMFolderCachedImap *rootStorage = dynamic_cast<KMFolderCachedImap*>( account()->rootFolder() ); 00754 if ( rootStorage ) 00755 rootStorage->createIndexFromContentsRecursive(); 00756 break; 00757 } 00758 case DImapTroubleShootDialog::ReindexCurrent: 00759 createIndexFromContents(); 00760 break; 00761 case DImapTroubleShootDialog::ReindexRecursive: 00762 createIndexFromContentsRecursive(); 00763 break; 00764 default: 00765 return; 00766 } 00767 KMessageBox::information( 0, i18n( "The index of this folder has been " 00768 "recreated." ) ); 00769 writeIndex(); 00770 kmkernel->getKMMainWidget()->folderSelected(); 00771 } 00772 } 00773 00774 void KMFolderCachedImap::serverSync( bool recurse, bool quotaOnly ) 00775 { 00776 if( mSyncState != SYNC_STATE_INITIAL ) { 00777 if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset it to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ), TQString(), i18n("Reset && Sync"), KStdGuiItem::cancel() ) == KMessageBox::Yes ) { 00778 mSyncState = SYNC_STATE_INITIAL; 00779 } else return; 00780 } 00781 00782 mRecurse = recurse; 00783 mQuotaOnly = quotaOnly; 00784 assert( account() ); 00785 00786 ProgressItem *progressItem = mAccount->mailCheckProgressItem(); 00787 if ( progressItem ) { 00788 progressItem->reset(); 00789 progressItem->setTotalItems( 100 ); 00790 } 00791 mProgress = 0; 00792 00793 #if 0 00794 if( mHoldSyncs ) { 00795 // All done for this folder. 00796 account()->mailCheckProgressItem()->setProgress( 100 ); 00797 mProgress = 100; // all done 00798 newState( mProgress, i18n("Synchronization skipped")); 00799 mSyncState = SYNC_STATE_INITIAL; 00800 emit folderComplete( this, true ); 00801 return; 00802 } 00803 #endif 00804 mTentativeHighestUid = 0; // reset, last sync could have been canceled 00805 00806 serverSyncInternal(); 00807 } 00808 00809 TQString KMFolderCachedImap::state2String( int state ) const 00810 { 00811 switch( state ) { 00812 case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL"; 00813 case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS"; 00814 case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES"; 00815 case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS"; 00816 case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS"; 00817 case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS"; 00818 case SYNC_STATE_LIST_NAMESPACES: return "SYNC_STATE_LIST_NAMESPACES"; 00819 case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2"; 00820 case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS"; 00821 case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES"; 00822 case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES"; 00823 case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES"; 00824 case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES"; 00825 case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX"; 00826 case SYNC_STATE_TEST_ANNOTATIONS: return "SYNC_STATE_TEST_ANNOTATIONS"; 00827 case SYNC_STATE_GET_ANNOTATIONS: return "SYNC_STATE_GET_ANNOTATIONS"; 00828 case SYNC_STATE_SET_ANNOTATIONS: return "SYNC_STATE_SET_ANNOTATIONS"; 00829 case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS"; 00830 case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS"; 00831 case SYNC_STATE_GET_QUOTA: return "SYNC_STATE_GET_QUOTA"; 00832 case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS"; 00833 case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS"; 00834 case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER"; 00835 case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY"; 00836 case SYNC_STATE_CLOSE: return "SYNC_STATE_CLOSE"; 00837 case SYNC_STATE_GET_SUBFOLDER_QUOTA: return "SYNC_STATE_GET_SUBFOLDER_QUOTA"; 00838 default: return "Unknown state"; 00839 } 00840 } 00841 00842 /* 00843 Progress calculation: each step is assigned a span. Initially the total is 100. 00844 But if we skip a step, don't increase the progress. 00845 This leaves more room for the step a with variable size (get_messages) 00846 connecting 5 00847 getuserrights 5 00848 rename 5 00849 check_uidvalidity 5 00850 create_subfolders 5 00851 put_messages 10 (but it can take a very long time, with many messages....) 00852 upload_flags 5 00853 list_subfolders 5 00854 list_subfolders2 0 (all local) 00855 delete_subfolders 5 00856 list_messages 10 00857 delete_messages 10 00858 expunge_messages 5 00859 get_messages variable (remaining-5) i.e. minimum 15. 00860 check_annotations 0 (rare) 00861 set_annotations 0 (rare) 00862 get_annotations 2 00863 set_acls 0 (rare) 00864 get_acls 3 00865 00866 noContent folders have only a few of the above steps 00867 (permissions, and all subfolder stuff), so its steps should be given more span 00868 00869 */ 00870 00871 // While the server synchronization is running, mSyncState will hold 00872 // the state that should be executed next 00873 void KMFolderCachedImap::serverSyncInternal() 00874 { 00875 // This is used to stop processing when we're about to exit 00876 // and the current job wasn't cancellable. 00877 // For user-requested abort, we'll use signalAbortRequested instead. 00878 if( kmkernel->mailCheckAborted() ) { 00879 resetSyncState(); 00880 emit folderComplete( this, false ); 00881 return; 00882 } 00883 00884 //kdDebug(5006) << label() << ": " << state2String( mSyncState ) << endl; 00885 switch( mSyncState ) { 00886 case SYNC_STATE_INITIAL: 00887 { 00888 mProgress = 0; 00889 foldersForDeletionOnServer.clear(); 00890 newState( mProgress, i18n("Synchronizing")); 00891 00892 open("cachedimap"); 00893 if ( !noContent() ) 00894 mAccount->addLastUnreadMsgCount( this, countUnread() ); 00895 00896 // Connect to the server (i.e. prepare the slave) 00897 ImapAccountBase::ConnectionState cs = mAccount->makeConnection(); 00898 if ( cs == ImapAccountBase::Error ) { 00899 // Cancelled by user, or slave can't start 00900 // kdDebug(5006) << "makeConnection said Error, aborting." << endl; 00901 // We stop here. We're already in SYNC_STATE_INITIAL for the next time. 00902 newState( mProgress, i18n( "Error connecting to server %1" ).arg( mAccount->host() ) ); 00903 close("cachedimap"); 00904 emit folderComplete(this, false); 00905 break; 00906 } else if ( cs == ImapAccountBase::Connecting ) { 00907 mAccount->setAnnotationCheckPassed( false ); 00908 // kdDebug(5006) << "makeConnection said Connecting, waiting for signal." << endl; 00909 newState( mProgress, i18n("Connecting to %1").arg( mAccount->host() ) ); 00910 // We'll wait for the connectionResult signal from the account. 00911 connect( mAccount, TQT_SIGNAL( connectionResult(int, const TQString&) ), 00912 this, TQT_SLOT( slotConnectionResult(int, const TQString&) ) ); 00913 break; 00914 } else { 00915 // Connected 00916 // kdDebug(5006) << "makeConnection said Connected, proceeding." << endl; 00917 mSyncState = SYNC_STATE_GET_USERRIGHTS; 00918 // Fall through to next state 00919 } 00920 } 00921 00922 00923 case SYNC_STATE_GET_USERRIGHTS: 00924 00925 // Now we have started the sync, emit changed() so that the folder tree can update the status 00926 emit syncStateChanged(); 00927 //kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl; 00928 00929 mSyncState = SYNC_STATE_RENAME_FOLDER; 00930 00931 if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) { 00932 // Check the user's own rights. We do this every time in case they changed. 00933 mOldUserRights = mUserRights; 00934 newState( mProgress, i18n("Checking permissions")); 00935 connect( mAccount, TQT_SIGNAL( receivedUserRights( KMFolder* ) ), 00936 this, TQT_SLOT( slotReceivedUserRights( KMFolder* ) ) ); 00937 mAccount->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case 00938 break; 00939 } 00940 00941 else if ( !mQuotaOnly && noContent() && mAccount->hasACLSupport() ) { 00942 // This is a no content folder. The server would simply say that mailbox does not exist when 00943 // querying the rights for it. So pretend we have no rights. 00944 mUserRights = 0; 00945 mUserRightsState = KMail::ACLJobs::Ok; 00946 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); 00947 } 00948 00949 case SYNC_STATE_RENAME_FOLDER: 00950 { 00951 mSyncState = SYNC_STATE_CHECK_UIDVALIDITY; 00952 // Returns the new name if the folder was renamed, empty otherwise. 00953 bool isResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( folder() ); 00954 TQString newName = mAccount->renamedFolder( imapPath() ); 00955 if ( !newName.isEmpty() && !folder()->isSystemFolder() && !isResourceFolder ) { 00956 newState( mProgress, i18n("Renaming folder") ); 00957 CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this ); 00958 connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), this, TQT_SLOT( slotIncreaseProgress() ) ); 00959 connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( slotRenameFolderFinished() ) ); 00960 job->start(); 00961 break; 00962 } 00963 } 00964 00965 case SYNC_STATE_CHECK_UIDVALIDITY: 00966 mSyncState = SYNC_STATE_CREATE_SUBFOLDERS; 00967 if( !mQuotaOnly && !noContent() ) { 00968 checkUidValidity(); 00969 break; 00970 } 00971 // Else carry on 00972 00973 case SYNC_STATE_CREATE_SUBFOLDERS: 00974 mSyncState = SYNC_STATE_PUT_MESSAGES; 00975 if ( !mQuotaOnly ) { 00976 createNewFolders(); 00977 break; 00978 } 00979 00980 case SYNC_STATE_PUT_MESSAGES: 00981 mSyncState = SYNC_STATE_UPLOAD_FLAGS; 00982 if( !mQuotaOnly && !noContent() ) { 00983 uploadNewMessages(); 00984 break; 00985 } 00986 // Else carry on 00987 case SYNC_STATE_UPLOAD_FLAGS: 00988 mSyncState = SYNC_STATE_LIST_NAMESPACES; 00989 if( !mQuotaOnly && !noContent() ) { 00990 // We haven't downloaded messages yet, so we need to build the map. 00991 if( uidMapDirty ) 00992 reloadUidMap(); 00993 // Upload flags, unless we know from the ACL that we're not allowed 00994 // to do that or they did not change locally 00995 if ( mUserRightsState != KMail::ACLJobs::Ok || 00996 ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) { 00997 if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) { 00998 uploadFlags(); 00999 break; 01000 } else { 01001 //kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl; 01002 } 01003 } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) { 01004 if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) { 01005 uploadSeenFlags(); 01006 break; 01007 } 01008 } 01009 } 01010 // Else carry on 01011 01012 case SYNC_STATE_LIST_NAMESPACES: 01013 if ( !mQuotaOnly && this == mAccount->rootFolder() ) { 01014 listNamespaces(); 01015 break; 01016 } 01017 mSyncState = SYNC_STATE_LIST_SUBFOLDERS; 01018 // Else carry on 01019 01020 case SYNC_STATE_LIST_SUBFOLDERS: 01021 newState( mProgress, i18n("Retrieving folderlist")); 01022 mSyncState = SYNC_STATE_LIST_SUBFOLDERS2; 01023 if ( !mQuotaOnly ) { 01024 if( !listDirectory() ) { 01025 mSyncState = SYNC_STATE_INITIAL; 01026 KMessageBox::error(0, i18n("Error while retrieving the folderlist")); 01027 } 01028 break; 01029 } 01030 01031 case SYNC_STATE_LIST_SUBFOLDERS2: 01032 mSyncState = SYNC_STATE_DELETE_SUBFOLDERS; 01033 mProgress += 10; 01034 if ( !mQuotaOnly ) { 01035 newState( mProgress, i18n("Retrieving subfolders")); 01036 listDirectory2(); 01037 break; 01038 } 01039 01040 case SYNC_STATE_DELETE_SUBFOLDERS: 01041 mSyncState = SYNC_STATE_LIST_MESSAGES; 01042 if( !mQuotaOnly && !foldersForDeletionOnServer.isEmpty() ) { 01043 newState( mProgress, i18n("Deleting folders from server")); 01044 CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer, 01045 CachedImapJob::tDeleteFolders, this ); 01046 connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), this, TQT_SLOT( slotIncreaseProgress() ) ); 01047 connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( slotFolderDeletionOnServerFinished() ) ); 01048 job->start(); 01049 break; 01050 } 01051 // Not needed, the next step emits newState very quick 01052 //newState( mProgress, i18n("No folders to delete from server")); 01053 // Carry on 01054 01055 case SYNC_STATE_LIST_MESSAGES: 01056 mSyncState = SYNC_STATE_DELETE_MESSAGES; 01057 if( !mQuotaOnly && !noContent() ) { 01058 newState( mProgress, i18n("Retrieving message list")); 01059 listMessages(); 01060 break; 01061 } 01062 // Else carry on 01063 01064 case SYNC_STATE_DELETE_MESSAGES: 01065 mSyncState = SYNC_STATE_EXPUNGE_MESSAGES; 01066 if( !mQuotaOnly && !noContent() ) { 01067 if( deleteMessages() ) { 01068 // Fine, we will continue with the next state 01069 } else { 01070 // No messages to delete, skip to GET_MESSAGES 01071 newState( mProgress, i18n("No messages to delete...")); 01072 mSyncState = SYNC_STATE_GET_MESSAGES; 01073 serverSyncInternal(); 01074 } 01075 break; 01076 } 01077 // Else carry on 01078 01079 case SYNC_STATE_EXPUNGE_MESSAGES: 01080 mSyncState = SYNC_STATE_GET_MESSAGES; 01081 if( !mQuotaOnly && !noContent() ) { 01082 newState( mProgress, i18n("Expunging deleted messages")); 01083 CachedImapJob *job = new CachedImapJob( TQString(), 01084 CachedImapJob::tExpungeFolder, this ); 01085 connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), this, TQT_SLOT( slotIncreaseProgress() ) ); 01086 connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( serverSyncInternal() ) ); 01087 job->start(); 01088 break; 01089 } 01090 // Else carry on 01091 01092 case SYNC_STATE_GET_MESSAGES: 01093 mSyncState = SYNC_STATE_HANDLE_INBOX; 01094 if( !mQuotaOnly && !noContent() ) { 01095 if( !mMsgsForDownload.isEmpty() ) { 01096 newState( mProgress, i18n("Retrieving one new message","Retrieving %n new messages",mMsgsForDownload.size())); 01097 CachedImapJob *job = new CachedImapJob( mMsgsForDownload, 01098 CachedImapJob::tGetMessage, 01099 this ); 01100 connect( job, TQT_SIGNAL( progress(unsigned long, unsigned long) ), 01101 this, TQT_SLOT( slotProgress(unsigned long, unsigned long) ) ); 01102 connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( slotUpdateLastUid() ) ); 01103 connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( serverSyncInternal() ) ); 01104 job->start(); 01105 mMsgsForDownload.clear(); 01106 break; 01107 } else { 01108 newState( mProgress, i18n("No new messages from server")); 01109 /* There were no messages to download, but it could be that we uploaded some 01110 which we didn't need to download again because we already knew the uid. 01111 Now that we are sure there is nothing to download, and everything that had 01112 to be deleted on the server has been deleted, adjust our local notion of the 01113 highes uid seen thus far. */ 01114 slotUpdateLastUid(); 01115 if( mLastUid == 0 && uidWriteTimer == -1 ) { 01116 // This is probably a new and empty folder. Write the UID cache 01117 if ( writeUidCache() == -1 ) { 01118 resetSyncState(); 01119 emit folderComplete( this, false ); 01120 return; 01121 } 01122 } 01123 } 01124 } 01125 01126 // Else carry on 01127 01128 case SYNC_STATE_HANDLE_INBOX: 01129 // Wrap up the 'download emails' stage. We always end up at 95 here. 01130 mProgress = 95; 01131 mSyncState = SYNC_STATE_TEST_ANNOTATIONS; 01132 01133 #define KOLAB_FOLDERTEST "/vendor/kolab/folder-test" 01134 case SYNC_STATE_TEST_ANNOTATIONS: 01135 mSyncState = SYNC_STATE_GET_ANNOTATIONS; 01136 // The first folder with user rights to write annotations 01137 if( !mQuotaOnly && !mAccount->annotationCheckPassed() && 01138 ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) 01139 && !imapPath().isEmpty() && imapPath() != "/" ) { 01140 kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl; 01141 newState( mProgress, i18n("Checking annotation support")); 01142 01143 KURL url = mAccount->getUrl(); 01144 url.setPath( imapPath() ); 01145 KMail::AnnotationList annotations; // to be set 01146 01147 KMail::AnnotationAttribute attr( KOLAB_FOLDERTEST, "value.shared", "true" ); 01148 annotations.append( attr ); 01149 01150 kdDebug(5006) << "Setting test attribute to "<< url << endl; 01151 KIO::Job* job = AnnotationJobs::multiSetAnnotation( mAccount->slave(), 01152 url, annotations ); 01153 ImapAccountBase::jobData jd( url.url(), folder() ); 01154 jd.cancellable = true; // we can always do so later 01155 mAccount->insertJob(job, jd); 01156 connect(job, TQT_SIGNAL(result(KIO::Job *)), 01157 TQT_SLOT(slotTestAnnotationResult(KIO::Job *))); 01158 break; 01159 } 01160 01161 case SYNC_STATE_GET_ANNOTATIONS: { 01162 #define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type" 01163 #define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for" 01164 #define KOLAB_SHAREDSEEN "/vendor/cmu/cyrus-imapd/sharedseen" 01165 //#define KOLAB_FOLDERTYPE "/comment" //for testing, while cyrus-imap doesn't support /vendor/* 01166 mSyncState = SYNC_STATE_SET_ANNOTATIONS; 01167 01168 bool needToGetInitialAnnotations = false; 01169 if ( !mQuotaOnly && !noContent() ) { 01170 // for a folder we didn't create ourselves: get annotation from server 01171 if ( mAnnotationFolderType == "FROMSERVER" ) { 01172 needToGetInitialAnnotations = true; 01173 mAnnotationFolderType = TQString(); 01174 } else { 01175 updateAnnotationFolderType(); 01176 } 01177 } 01178 01179 // First retrieve the annotation, so that we know we have to set it if it's not set. 01180 // On the other hand, if the user changed the contentstype, there's no need to get first. 01181 if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() && 01182 ( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) { 01183 TQStringList annotations; // list of annotations to be fetched 01184 if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() ) 01185 annotations << KOLAB_FOLDERTYPE; 01186 if ( !mIncidencesForChanged ) 01187 annotations << KOLAB_INCIDENCESFOR; 01188 if ( !mSharedSeenFlagsChanged ) 01189 annotations << KOLAB_SHAREDSEEN; 01190 if ( !annotations.isEmpty() ) { 01191 newState( mProgress, i18n("Retrieving annotations")); 01192 KURL url = mAccount->getUrl(); 01193 url.setPath( imapPath() ); 01194 AnnotationJobs::MultiGetAnnotationJob* job = 01195 AnnotationJobs::multiGetAnnotation( mAccount->slave(), url, annotations ); 01196 ImapAccountBase::jobData jd( url.url(), folder() ); 01197 jd.cancellable = true; 01198 mAccount->insertJob(job, jd); 01199 01200 connect( job, TQT_SIGNAL(annotationResult(const TQString&, const TQString&, bool)), 01201 TQT_SLOT(slotAnnotationResult(const TQString&, const TQString&, bool)) ); 01202 connect( job, TQT_SIGNAL(result(KIO::Job *)), 01203 TQT_SLOT(slotGetAnnotationResult(KIO::Job *)) ); 01204 break; 01205 } 01206 } 01207 } // case 01208 case SYNC_STATE_SET_ANNOTATIONS: 01209 01210 mSyncState = SYNC_STATE_SET_ACLS; 01211 if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() && 01212 ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) { 01213 newState( mProgress, i18n("Setting annotations")); 01214 KURL url = mAccount->getUrl(); 01215 url.setPath( imapPath() ); 01216 KMail::AnnotationList annotations; // to be set 01217 if ( mAnnotationFolderTypeChanged && !mAnnotationFolderType.isEmpty() ) { 01218 KMail::AnnotationAttribute attr( KOLAB_FOLDERTYPE, "value.shared", mAnnotationFolderType ); 01219 annotations.append( attr ); 01220 kdDebug(5006) << "Setting folder-type annotation for " << label() << " to " << mAnnotationFolderType << endl; 01221 } 01222 if ( mIncidencesForChanged ) { 01223 const TQString val = incidencesForToString( mIncidencesFor ); 01224 KMail::AnnotationAttribute attr( KOLAB_INCIDENCESFOR, "value.shared", val ); 01225 annotations.append( attr ); 01226 kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl; 01227 } 01228 if ( mSharedSeenFlagsChanged ) { 01229 const TQString val = mSharedSeenFlags ? "true" : "false"; 01230 KMail::AnnotationAttribute attr( KOLAB_SHAREDSEEN, "value.shared", val ); 01231 annotations.append( attr ); 01232 kdDebug(5006) << k_funcinfo << "Setting sharedseen annotation for " << label() << " to " << val << endl; 01233 } 01234 if ( !annotations.isEmpty() ) { 01235 KIO::Job* job = 01236 AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations ); 01237 ImapAccountBase::jobData jd( url.url(), folder() ); 01238 jd.cancellable = true; // we can always do so later 01239 mAccount->insertJob(job, jd); 01240 01241 connect(job, TQT_SIGNAL(annotationChanged( const TQString&, const TQString&, const TQString& ) ), 01242 TQT_SLOT( slotAnnotationChanged( const TQString&, const TQString&, const TQString& ) )); 01243 connect(job, TQT_SIGNAL(result(KIO::Job *)), 01244 TQT_SLOT(slotSetAnnotationResult(KIO::Job *))); 01245 break; 01246 } 01247 } 01248 01249 case SYNC_STATE_SET_ACLS: 01250 mSyncState = SYNC_STATE_GET_ACLS; 01251 01252 if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() && 01253 ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) { 01254 bool hasChangedACLs = false; 01255 ACLList::ConstIterator it = mACLList.begin(); 01256 for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) { 01257 hasChangedACLs = (*it).changed; 01258 } 01259 if ( hasChangedACLs ) { 01260 newState( mProgress, i18n("Setting permissions")); 01261 KURL url = mAccount->getUrl(); 01262 url.setPath( imapPath() ); 01263 KIO::Job* job = KMail::ACLJobs::multiSetACL( mAccount->slave(), url, mACLList ); 01264 ImapAccountBase::jobData jd( url.url(), folder() ); 01265 mAccount->insertJob(job, jd); 01266 01267 connect(job, TQT_SIGNAL(result(KIO::Job *)), 01268 TQT_SLOT(slotMultiSetACLResult(KIO::Job *))); 01269 connect(job, TQT_SIGNAL(aclChanged( const TQString&, int )), 01270 TQT_SLOT(slotACLChanged( const TQString&, int )) ); 01271 break; 01272 } 01273 } 01274 01275 case SYNC_STATE_GET_ACLS: 01276 mSyncState = SYNC_STATE_FIND_SUBFOLDERS; 01277 01278 if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) { 01279 newState( mProgress, i18n( "Retrieving permissions" ) ); 01280 mAccount->getACL( folder(), mImapPath ); 01281 connect( mAccount, TQT_SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )), 01282 this, TQT_SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) ); 01283 break; 01284 } 01285 case SYNC_STATE_FIND_SUBFOLDERS: 01286 { 01287 mSyncState = SYNC_STATE_SYNC_SUBFOLDERS; 01288 mSomeSubFolderCloseToQuotaChanged = false; 01289 buildSubFolderList(); 01290 } 01291 01292 // Carry on 01293 case SYNC_STATE_SYNC_SUBFOLDERS: 01294 syncNextSubFolder( false ); 01295 break; 01296 case SYNC_STATE_GET_SUBFOLDER_QUOTA: 01297 01298 // Sync the subfolders again, so that the quota information is updated for all. This state is 01299 // only entered if the close to quota property of one subfolder changed in the previous state. 01300 syncNextSubFolder( true ); 01301 break; 01302 case SYNC_STATE_GET_QUOTA: 01303 mSyncState = SYNC_STATE_CLOSE; 01304 if( !noContent() && mAccount->hasQuotaSupport() ) { 01305 mProgress = 98; 01306 newState( mProgress, i18n("Getting quota information")); 01307 KURL url = mAccount->getUrl(); 01308 url.setPath( imapPath() ); 01309 KIO::Job* job = KMail::QuotaJobs::getStorageQuota( mAccount->slave(), url ); 01310 ImapAccountBase::jobData jd( url.url(), folder() ); 01311 mAccount->insertJob(job, jd); 01312 connect( job, TQT_SIGNAL( storageQuotaResult( const QuotaInfo& ) ), 01313 TQT_SLOT( slotStorageQuotaResult( const QuotaInfo& ) ) ); 01314 connect( job, TQT_SIGNAL(result(KIO::Job *)), 01315 TQT_SLOT(slotQuotaResult(KIO::Job *)) ); 01316 break; 01317 } 01318 case SYNC_STATE_CLOSE: 01319 { 01320 mProgress = 100; // all done 01321 newState( mProgress, i18n("Synchronization done")); 01322 KURL url = mAccount->getUrl(); 01323 url.setPath( imapPath() ); 01324 kmkernel->iCalIface().folderSynced( folder(), url ); 01325 01326 mSyncState = SYNC_STATE_INITIAL; 01327 mAccount->addUnreadMsgCount( this, countUnread() ); // before closing 01328 close( "cachedimap" ); 01329 emit syncStateChanged(); 01330 emit folderComplete( this, true ); 01331 } 01332 break; 01333 01334 default: 01335 kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state " 01336 << mSyncState << endl; 01337 } 01338 } 01339 01340 void KMFolderCachedImap::syncNextSubFolder( bool secondSync ) 01341 { 01342 if( mCurrentSubfolder ) { 01343 disconnectSubFolderSignals(); 01344 } 01345 01346 if( mSubfoldersForSync.isEmpty() ) { 01347 01348 // Sync finished, and a close to quota property of an subfolder changed, therefore go into 01349 // the SYNC_STATE_GET_SUBFOLDER_QUOTA state and sync again 01350 if ( mSomeSubFolderCloseToQuotaChanged && mRecurse && !secondSync ) { 01351 buildSubFolderList(); 01352 mSyncState = SYNC_STATE_GET_SUBFOLDER_QUOTA; 01353 serverSyncInternal(); 01354 } 01355 01356 else { 01357 01358 // Quota checking has to come after syncing subfolder, otherwise the quota information would 01359 // be outdated, since the subfolders can change in size during the syncing. 01360 // https://issues.kolab.org/issue4066 01361 mSyncState = SYNC_STATE_GET_QUOTA; 01362 serverSyncInternal(); 01363 } 01364 } else { 01365 mCurrentSubfolder = mSubfoldersForSync.front(); 01366 mSubfoldersForSync.pop_front(); 01367 if ( mCurrentSubfolder ) { 01368 connect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), 01369 this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); 01370 connect( mCurrentSubfolder, TQT_SIGNAL( closeToQuotaChanged() ), 01371 this, TQT_SLOT( slotSubFolderCloseToQuotaChanged() ) ); 01372 01373 //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl; 01374 assert( !mCurrentSubfolder->imapPath().isEmpty() ); 01375 mCurrentSubfolder->setAccount( account() ); 01376 const bool recurse = mCurrentSubfolder->noChildren() ? false : true; 01377 const bool quotaOnly = secondSync || mQuotaOnly; 01378 mCurrentSubfolder->serverSync( recurse, quotaOnly ); 01379 } 01380 else { 01381 // mCurrentSubfolder is empty, probably because it was deleted while syncing. Go on with the 01382 // next subfolder instead. 01383 syncNextSubFolder( secondSync ); 01384 } 01385 } 01386 } 01387 01388 void KMFolderCachedImap::buildSubFolderList() 01389 { 01390 mSubfoldersForSync.clear(); 01391 mCurrentSubfolder = 0; 01392 if( folder() && folder()->child() ) { 01393 KMFolderNode *node = folder()->child()->first(); 01394 while( node ) { 01395 if( !node->isDir() ) { 01396 KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 01397 const bool folderIsNew = mNewlyCreatedSubfolders.contains( TQGuardedPtr<KMFolderCachedImap>( storage ) ); 01398 // Only sync folders that have been accepted by the server 01399 if ( !storage->imapPath().isEmpty() 01400 // and that were not just deleted from it 01401 && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) { 01402 if ( mRecurse || folderIsNew ) { 01403 mSubfoldersForSync << storage; 01404 } 01405 } else { 01406 kdDebug(5006) << "Do not add " << storage->label() 01407 << " to synclist" << endl; 01408 } 01409 } 01410 node = folder()->child()->next(); 01411 } 01412 } 01413 01414 mNewlyCreatedSubfolders.clear(); 01415 } 01416 01417 void KMFolderCachedImap::disconnectSubFolderSignals() 01418 { 01419 disconnect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), 01420 this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); 01421 disconnect( mCurrentSubfolder, TQT_SIGNAL( closeToQuotaChanged() ), 01422 this, TQT_SLOT( slotSubFolderCloseToQuotaChanged() ) ); 01423 mCurrentSubfolder = 0; 01424 } 01425 01426 /* Connected to the imap account's connectionResult signal. 01427 Emitted when the slave connected or failed to connect. 01428 */ 01429 void KMFolderCachedImap::slotConnectionResult( int errorCode, const TQString& errorMsg ) 01430 { 01431 disconnect( mAccount, TQT_SIGNAL( connectionResult(int, const TQString&) ), 01432 this, TQT_SLOT( slotConnectionResult(int, const TQString&) ) ); 01433 if ( !errorCode ) { 01434 // Success 01435 mSyncState = SYNC_STATE_GET_USERRIGHTS; 01436 mProgress += 5; 01437 serverSyncInternal(); 01438 } else { 01439 // Error (error message already shown by the account) 01440 newState( mProgress, KIO::buildErrorString( errorCode, errorMsg )); 01441 emit folderComplete(this, false); 01442 } 01443 } 01444 01445 /* find new messages (messages without a UID) */ 01446 TQValueList<unsigned long> KMFolderCachedImap::findNewMessages() 01447 { 01448 TQValueList<unsigned long> result; 01449 for( int i = 0; i < count(); ++i ) { 01450 KMMsgBase *msg = getMsgBase( i ); 01451 if( !msg ) continue; /* what goes on if getMsg() returns 0? */ 01452 if ( msg->UID() == 0 ) 01453 result.append( msg->getMsgSerNum() ); 01454 } 01455 return result; 01456 } 01457 01458 /* Upload new messages to server */ 01459 void KMFolderCachedImap::uploadNewMessages() 01460 { 01461 TQValueList<unsigned long> newMsgs = findNewMessages(); 01462 if( !newMsgs.isEmpty() ) { 01463 if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) { 01464 newState( mProgress, i18n("Uploading messages to server")); 01465 CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this ); 01466 connect( job, TQT_SIGNAL( progress( unsigned long, unsigned long) ), 01467 this, TQT_SLOT( slotPutProgress(unsigned long, unsigned long) ) ); 01468 connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( serverSyncInternal() ) ); 01469 job->start(); 01470 return; 01471 } else { 01472 KMCommand *command = rescueUnsyncedMessages(); 01473 connect( command, TQT_SIGNAL( completed( KMCommand * ) ), 01474 this, TQT_SLOT( serverSyncInternal() ) ); 01475 } 01476 } else { // nothing to upload 01477 if ( mUserRights != mOldUserRights && (mOldUserRights & KMail::ACLJobs::Insert) 01478 && !(mUserRights & KMail::ACLJobs::Insert) ) { 01479 // write access revoked 01480 KMessageBox::information( 0, i18n("<p>Your access rights to folder <b>%1</b> have been restricted, " 01481 "it will no longer be possible to add messages to this folder.</p>").arg( folder()->prettyURL() ), 01482 i18n("Acces rights revoked"), "KMailACLRevocationNotification" ); 01483 } 01484 } 01485 newState( mProgress, i18n("No messages to upload to server")); 01486 serverSyncInternal(); 01487 } 01488 01489 /* Progress info during uploadNewMessages */ 01490 void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total ) 01491 { 01492 // (going from mProgress to mProgress+10) 01493 int progressSpan = 10; 01494 newState( mProgress + (progressSpan * done) / total, TQString() ); 01495 if ( done == total ) // we're done 01496 mProgress += progressSpan; 01497 } 01498 01499 /* Upload message flags to server */ 01500 void KMFolderCachedImap::uploadFlags() 01501 { 01502 if ( !uidMap.isEmpty() ) { 01503 mStatusFlagsJobs = 0; 01504 newState( mProgress, i18n("Uploading status of messages to server")); 01505 01506 // FIXME DUPLICATED FROM KMFOLDERIMAP 01507 TQMap< TQString, TQStringList > groups; 01508 //open(); //already done 01509 for( int i = 0; i < count(); ++i ) { 01510 KMMsgBase* msg = getMsgBase( i ); 01511 if( !msg || msg->UID() == 0 ) 01512 // Either not a valid message or not one that is on the server yet 01513 continue; 01514 if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end() 01515 && !mStatusChangedLocally ) { 01516 // This message has not had its status changed locally 01517 continue; 01518 } 01519 01520 TQString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags); 01521 // Collect uids for each typem of flags. 01522 TQString uid; 01523 uid.setNum( msg->UID() ); 01524 groups[flags].append(uid); 01525 } 01526 TQMapIterator< TQString, TQStringList > dit; 01527 for( dit = groups.begin(); dit != groups.end(); ++dit ) { 01528 TQCString flags = dit.key().latin1(); 01529 TQStringList sets = KMFolderImap::makeSets( (*dit), true ); 01530 mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap.... 01531 // Send off a status setting job for each set. 01532 for( TQStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) { 01533 TQString imappath = imapPath() + ";UID=" + ( *slit ); 01534 mAccount->setImapStatus(folder(), imappath, flags); 01535 } 01536 } 01537 // FIXME END DUPLICATED FROM KMFOLDERIMAP 01538 01539 if ( mStatusFlagsJobs ) { 01540 connect( mAccount, TQT_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ), 01541 this, TQT_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) ); 01542 return; 01543 } 01544 } 01545 newState( mProgress, i18n("No messages to upload to server")); 01546 serverSyncInternal(); 01547 } 01548 01549 void KMFolderCachedImap::uploadSeenFlags() 01550 { 01551 if ( !uidMap.isEmpty() ) { 01552 mStatusFlagsJobs = 0; 01553 newState( mProgress, i18n("Uploading status of messages to server")); 01554 01555 TQValueList<ulong> seenUids, unseenUids; 01556 for( int i = 0; i < count(); ++i ) { 01557 KMMsgBase* msg = getMsgBase( i ); 01558 if( !msg || msg->UID() == 0 ) 01559 // Either not a valid message or not one that is on the server yet 01560 continue; 01561 01562 if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end() 01563 && !mStatusChangedLocally ) { 01564 // This message has not had its status changed locally 01565 continue; 01566 } 01567 01568 if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead ) 01569 seenUids.append( msg->UID() ); 01570 else 01571 unseenUids.append( msg->UID() ); 01572 } 01573 if ( !seenUids.isEmpty() ) { 01574 TQStringList sets = KMFolderImap::makeSets( seenUids, true ); 01575 mStatusFlagsJobs += sets.count(); 01576 for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) { 01577 TQString imappath = imapPath() + ";UID=" + ( *it ); 01578 mAccount->setImapSeenStatus( folder(), imappath, true ); 01579 } 01580 } 01581 if ( !unseenUids.isEmpty() ) { 01582 TQStringList sets = KMFolderImap::makeSets( unseenUids, true ); 01583 mStatusFlagsJobs += sets.count(); 01584 for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) { 01585 TQString imappath = imapPath() + ";UID=" + ( *it ); 01586 mAccount->setImapSeenStatus( folder(), imappath, false ); 01587 } 01588 } 01589 01590 if ( mStatusFlagsJobs ) { 01591 connect( mAccount, TQT_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ), 01592 this, TQT_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) ); 01593 return; 01594 } 01595 } 01596 newState( mProgress, i18n("No messages to upload to server")); 01597 serverSyncInternal(); 01598 } 01599 01600 void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const TQString&, bool cont) 01601 { 01602 if ( mSyncState == SYNC_STATE_INITIAL ){ 01603 //kdDebug(5006) << "IMAP status changed but reset " << endl; 01604 return; // we were reset 01605 } 01606 //kdDebug(5006) << "IMAP status changed for folder: " << folder->prettyURL() << endl; 01607 if ( folder->storage() == this ) { 01608 --mStatusFlagsJobs; 01609 if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting 01610 disconnect( mAccount, TQT_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ), 01611 this, TQT_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) ); 01612 if ( mStatusFlagsJobs == 0 && cont ) { 01613 mProgress += 5; 01614 serverSyncInternal(); 01615 //kdDebug(5006) << "Proceeding with mailcheck." << endl; 01616 } 01617 } 01618 } 01619 01620 // This is not perfect, what if the status didn't really change? Oh well ... 01621 void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle) 01622 { 01623 KMFolderMaildir::setStatus( idx, status, toggle ); 01624 const KMMsgBase *msg = getMsgBase( idx ); 01625 Q_ASSERT( msg ); 01626 if ( msg ) 01627 mUIDsOfLocallyChangedStatuses.insert( msg->UID() ); 01628 } 01629 01630 void KMFolderCachedImap::setStatus(TQValueList<int>& ids, KMMsgStatus status, bool toggle) 01631 { 01632 KMFolderMaildir::setStatus(ids, status, toggle); 01633 for (TQValueList<int>::iterator it = ids.begin(); it != ids.end(); it++ ) { 01634 const KMMsgBase *msg = getMsgBase( *it ); 01635 Q_ASSERT( msg ); 01636 if ( msg ) 01637 mUIDsOfLocallyChangedStatuses.insert( msg->UID() ); 01638 } 01639 } 01640 01641 /* Upload new folders to server */ 01642 void KMFolderCachedImap::createNewFolders() 01643 { 01644 TQValueList<KMFolderCachedImap*> newFolders = findNewFolders(); 01645 //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl; 01646 if( !newFolders.isEmpty() ) { 01647 newState( mProgress, i18n("Creating subfolders on server")); 01648 CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this ); 01649 connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), this, TQT_SLOT( slotIncreaseProgress() ) ); 01650 connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( serverSyncInternal() ) ); 01651 job->start(); 01652 } else { 01653 serverSyncInternal(); 01654 } 01655 } 01656 01657 TQValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders() 01658 { 01659 TQValueList<KMFolderCachedImap*> newFolders; 01660 if( folder() && folder()->child() ) { 01661 KMFolderNode *node = folder()->child()->first(); 01662 while( node ) { 01663 if( !node->isDir() ) { 01664 if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) { 01665 kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! " 01666 << node->name() << " is not an IMAP folder\n"; 01667 node = folder()->child()->next(); 01668 assert(0); 01669 } 01670 KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 01671 if( folder->imapPath().isEmpty() ) { 01672 newFolders << folder; 01673 } 01674 } 01675 node = folder()->child()->next(); 01676 } 01677 } 01678 return newFolders; 01679 } 01680 01681 bool KMFolderCachedImap::deleteMessages() 01682 { 01683 /* Delete messages from cache that are gone from the server */ 01684 TQPtrList<KMMsgBase> msgsForDeletion; 01685 01686 // It is not possible to just go over all indices and remove 01687 // them one by one because the index list can get resized under 01688 // us. So use msg pointers instead 01689 01690 TQStringList uids; 01691 TQMap<ulong,int>::const_iterator it = uidMap.constBegin(); 01692 for( ; it != uidMap.end(); it++ ) { 01693 ulong uid ( it.key() ); 01694 if( uid!=0 && !uidsOnServer.find( uid ) ) { 01695 uids << TQString::number( uid ); 01696 msgsForDeletion.append( getMsgBase( *it ) ); 01697 } 01698 } 01699 01700 if( !msgsForDeletion.isEmpty() ) { 01701 if ( contentsType() != ContentsTypeMail ) { 01702 kdDebug(5006) << k_funcinfo << label() << " Going to locally delete " << msgsForDeletion.count() 01703 << " messages, with the uids " << uids.join( "," ) << endl; 01704 } 01705 #if MAIL_LOSS_DEBUGGING 01706 if ( KMessageBox::warningYesNo( 01707 0, i18n( "<qt><p>Mails on the server in folder <b>%1</b> were deleted. " 01708 "Do you want to delete them locally?<br>UIDs: %2</p></qt>" ) 01709 .arg( folder()->prettyURL() ).arg( uids.join(",") ) ) == KMessageBox::Yes ) 01710 #endif 01711 removeMsg( msgsForDeletion ); 01712 } 01713 01714 if ( mUserRightsState == KMail::ACLJobs::Ok && !( mUserRights & KMail::ACLJobs::Delete ) ) 01715 return false; 01716 01717 /* Delete messages from the server that we dont have anymore */ 01718 if( !uidsForDeletionOnServer.isEmpty() ) { 01719 newState( mProgress, i18n("Deleting removed messages from server")); 01720 TQStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true ); 01721 uidsForDeletionOnServer.clear(); 01722 kdDebug(5006) << "Deleting " << sets.count() << " sets of messages from server folder " << imapPath() << endl; 01723 CachedImapJob *job = new CachedImapJob( sets, CachedImapJob::tDeleteMessage, this ); 01724 connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), 01725 this, TQT_SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) ); 01726 job->start(); 01727 return true; 01728 } else { 01729 01730 // Nothing to delete on the server, make sure the map is clear again. 01731 // Normally this wouldn't be necessary, but there can be stale maps because of 01732 // https://issues.kolab.org/issue3833. 01733 mDeletedUIDsSinceLastSync.clear(); 01734 return false; 01735 } 01736 } 01737 01738 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job ) 01739 { 01740 if ( job->error() ) { 01741 // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages 01742 mSyncState = SYNC_STATE_GET_MESSAGES; 01743 } else { 01744 // deleting on the server went fine, clear the pending deletions cache 01745 mDeletedUIDsSinceLastSync.clear(); 01746 } 01747 mProgress += 10; 01748 serverSyncInternal(); 01749 } 01750 01751 void KMFolderCachedImap::checkUidValidity() { 01752 // IMAP root folders don't seem to have a UID validity setting. 01753 // Also, don't try the uid validity on new folders 01754 if( imapPath().isEmpty() || imapPath() == "/" ) 01755 // Just proceed 01756 serverSyncInternal(); 01757 else { 01758 newState( mProgress, i18n("Checking folder validity")); 01759 CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this ); 01760 connect( job, TQT_SIGNAL(permanentFlags(int)), TQT_SLOT(slotPermanentFlags(int)) ); 01761 connect( job, TQT_SIGNAL( result( KMail::FolderJob* ) ), 01762 this, TQT_SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) ); 01763 job->start(); 01764 } 01765 } 01766 01767 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job ) 01768 { 01769 if ( job->error() ) { // there was an error and the user chose "continue" 01770 // We can't continue doing anything in the same folder though, it would delete all mails. 01771 // But we can continue to subfolders if any. Well we can also try annotation/acl stuff... 01772 mSyncState = SYNC_STATE_HANDLE_INBOX; 01773 } 01774 mProgress += 5; 01775 serverSyncInternal(); 01776 } 01777 01778 void KMFolderCachedImap::slotPermanentFlags(int flags) 01779 { 01780 mPermanentFlags = flags; 01781 } 01782 01783 /* This will only list the messages in a folder. 01784 No directory listing done*/ 01785 void KMFolderCachedImap::listMessages() { 01786 bool groupwareOnly = GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount() 01787 && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id() 01788 && folder()->isSystemFolder() 01789 && mImapPath == "/INBOX/"; 01790 // Don't list messages on the root folder, and skip the inbox, if this is 01791 // the inbox of a groupware-only dimap account 01792 if( imapPath() == "/" || groupwareOnly ) { 01793 serverSyncInternal(); 01794 return; 01795 } 01796 01797 if( !mAccount->slave() ) { // sync aborted 01798 resetSyncState(); 01799 emit folderComplete( this, false ); 01800 return; 01801 } 01802 uidsOnServer.clear(); 01803 uidsOnServer.resize( count() * 2 ); 01804 uidsForDeletionOnServer.clear(); 01805 mMsgsForDownload.clear(); 01806 mUidsForDownload.clear(); 01807 // listing is only considered successful if saw a syntactically correct imapdigest 01808 mFoundAnIMAPDigest = false; 01809 01810 CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this ); 01811 connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), 01812 this, TQT_SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) ); 01813 job->start(); 01814 } 01815 01816 void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job) 01817 { 01818 getMessagesResult(job, true); 01819 } 01820 01821 // Connected to the listMessages job in CachedImapJob 01822 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const TQByteArray & data) 01823 { 01824 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 01825 if ( it == mAccount->jobsEnd() ) { // Shouldn't happen 01826 kdDebug(5006) << "could not find job!?!?!" << endl; 01827 // be sure to reset the sync state, if the listing was partial we would 01828 // otherwise delete not-listed mail locally, and on the next sync on the server 01829 // as well 01830 mSyncState = SYNC_STATE_HANDLE_INBOX; 01831 serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */ 01832 return; 01833 } 01834 (*it).cdata += TQCString(data, data.size() + 1); 01835 int pos = (*it).cdata.find("\r\n--IMAPDIGEST"); 01836 if (pos > 0) { 01837 int a = (*it).cdata.find("\r\nX-uidValidity:"); 01838 if (a != -1) { 01839 int b = (*it).cdata.find("\r\n", a + 17); 01840 setUidValidity((*it).cdata.mid(a + 17, b - a - 17)); 01841 } 01842 a = (*it).cdata.find("\r\nX-Access:"); 01843 // Only trust X-Access (i.e. the imap select info) if we don't know mUserRights. 01844 // The latter is more accurate (checked on every sync) whereas X-Access is only 01845 // updated when selecting the folder again, which might not happen if using 01846 // RMB / Check Mail in this folder. We don't need two (potentially conflicting) 01847 // sources for the readonly setting, in any case. 01848 if (a != -1 && mUserRightsState != KMail::ACLJobs::Ok ) { 01849 int b = (*it).cdata.find("\r\n", a + 12); 01850 const TQString access = (*it).cdata.mid(a + 12, b - a - 12); 01851 setReadOnly( access == "Read only" ); 01852 } 01853 (*it).cdata.remove(0, pos); 01854 mFoundAnIMAPDigest = true; 01855 } 01856 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); 01857 // Start with something largish when rebuilding the cache 01858 if ( uidsOnServer.size() == 0 ) 01859 uidsOnServer.resize( KMail::nextPrime( 2000 ) ); 01860 const int v = 42; 01861 while (pos >= 0) { 01862 /* 01863 KMMessage msg; 01864 msg.fromString((*it).cdata.mid(16, pos - 16)); 01865 const int flags = msg.headerField("X-Flags").toInt(); 01866 const ulong size = msg.headerField("X-Length").toULong(); 01867 const ulong uid = msg.UID(); 01868 */ 01869 // The below is optimized for speed, not prettiness. The commented out chunk 01870 // above was the solution copied from kmfolderimap, and it's 15-20% slower. 01871 const TQCString& entry( (*it).cdata ); 01872 const int indexOfUID = entry.find("X-UID", 16); 01873 const int startOfUIDValue = indexOfUID + 7; 01874 const int indexOfLength = entry.find("X-Length", startOfUIDValue ); // we know length comes after UID 01875 const int startOfLengthValue = indexOfLength + 10; 01876 const int indexOfFlags = entry.find("X-Flags", startOfLengthValue ); // we know flags comes last 01877 const int startOfFlagsValue = indexOfFlags + 9; 01878 01879 const int flags = entry.mid( startOfFlagsValue, entry.find( '\r', startOfFlagsValue ) - startOfFlagsValue ).toInt(); 01880 const ulong size = entry.mid( startOfLengthValue, entry.find( '\r', startOfLengthValue ) - startOfLengthValue ).toULong(); 01881 const ulong uid = entry.mid( startOfUIDValue, entry.find( '\r', startOfUIDValue ) - startOfUIDValue ).toULong(); 01882 01883 const bool deleted = ( flags & 8 ); 01884 if ( !deleted ) { 01885 if( uid != 0 ) { 01886 if ( uidsOnServer.count() == uidsOnServer.size() ) { 01887 uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) ); 01888 //kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl; 01889 } 01890 uidsOnServer.insert( uid, &v ); 01891 } 01892 bool redownload = false; 01893 if ( uid <= lastUid() ) { 01894 /* 01895 * If this message UID is not present locally, then it must 01896 * have been deleted by the user, so we delete it on the 01897 * server also. If we don't have delete permissions on the server, 01898 * re-download the message, it must have vanished by some error, or 01899 * while we still thought we were allowed to delete (ACL change). 01900 * 01901 * This relies heavily on lastUid() being correct at all times. 01902 */ 01903 // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl; 01904 KMMsgBase *existingMessage = findByUID(uid); 01905 if( !existingMessage ) { 01906 #if MAIL_LOSS_DEBUGGING 01907 kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should delete it" << endl; 01908 #endif 01909 // double check we deleted it since the last sync 01910 if ( mDeletedUIDsSinceLastSync.contains(uid) ) { 01911 if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & KMail::ACLJobs::Delete ) ) { 01912 #if MAIL_LOSS_DEBUGGING 01913 kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl; 01914 #endif 01915 uidsForDeletionOnServer << uid; 01916 } else { 01917 redownload = true; 01918 } 01919 } else { 01920 kdDebug(5006) << "WARNING: ####### " << endl; 01921 kdDebug(5006) << "Message locally missing but not deleted in folder: " << folder()->prettyURL() << endl; 01922 kdDebug(5006) << "The missing UID: " << uid << ". It will be redownloaded " << endl; 01923 redownload = true; 01924 } 01925 01926 } else { 01927 // if this is a read only folder, ignore status updates from the server 01928 // since we can't write our status back our local version is what has to 01929 // be considered correct. 01930 if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) { 01931 /* The message is OK, update flags */ 01932 KMFolderImap::flagsToStatus( existingMessage, flags, false, mReadOnly ? INT_MAX : mPermanentFlags ); 01933 } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) { 01934 KMFolderImap::seenFlagToStatus( existingMessage, flags ); 01935 } 01936 } 01937 // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl; 01938 } 01939 if ( uid > lastUid() || redownload ) { 01940 #if MAIL_LOSS_DEBUGGING 01941 kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should download it" << endl; 01942 #endif 01943 // The message is new since the last sync, but we might have just uploaded it, in which case 01944 // the uid map already contains it. 01945 if ( !uidMap.contains( uid ) ) { 01946 mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size); 01947 if( imapPath() == "/INBOX/" ) 01948 mUidsForDownload << uid; 01949 } 01950 // Remember the highest uid and once the download is completed, update mLastUid 01951 if ( uid > mTentativeHighestUid ) { 01952 #if MAIL_LOSS_DEBUGGING 01953 kdDebug(5006) << "Setting the tentative highest UID to: " << uid << endl; 01954 #endif 01955 mTentativeHighestUid = uid; 01956 } 01957 } 01958 } 01959 (*it).cdata.remove(0, pos); 01960 (*it).done++; 01961 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); 01962 } 01963 } 01964 01965 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet ) 01966 { 01967 mProgress += 10; 01968 if ( !job->error() && !mFoundAnIMAPDigest ) { 01969 kdWarning(5006) << "######## Folderlisting did not complete, but there was no error! " 01970 "Aborting sync of folder: " << folder()->prettyURL() << endl; 01971 #if MAIL_LOSS_DEBUGGING 01972 kmkernel->emergencyExit( i18n("Folder listing failed in interesting ways." ) ); 01973 #endif 01974 } 01975 if( job->error() ) { // error listing messages but the user chose to continue 01976 mContentState = imapNoInformation; 01977 mSyncState = SYNC_STATE_HANDLE_INBOX; // be sure not to continue in this folder 01978 } else { 01979 if( lastSet ) { // always true here (this comes from online-imap...) 01980 mContentState = imapFinished; 01981 mUIDsOfLocallyChangedStatuses.clear(); // we are up to date again 01982 mStatusChangedLocally = false; 01983 } 01984 } 01985 serverSyncInternal(); 01986 } 01987 01988 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total) 01989 { 01990 int progressSpan = 100 - 5 - mProgress; 01991 int additionalProgress = ( total == 0 ) ? 01992 progressSpan : 01993 ( progressSpan * done ) / total; 01994 01995 // Progress info while retrieving new emails 01996 // (going from mProgress to mProgress+progressSpan) 01997 newState( mProgress + additionalProgress, TQString() ); 01998 } 01999 02000 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount) 02001 { 02002 assert( aAccount->isA("KMAcctCachedImap") ); 02003 mAccount = aAccount; 02004 if( imapPath()=="/" ) aAccount->setFolder( folder() ); 02005 02006 // Folder was renamed in a previous session, and the user didn't sync yet 02007 TQString newName = mAccount->renamedFolder( imapPath() ); 02008 if ( !newName.isEmpty() ) 02009 folder()->setLabel( newName ); 02010 02011 if( !folder() || !folder()->child() || !folder()->child()->count() ) return; 02012 for( KMFolderNode* node = folder()->child()->first(); node; 02013 node = folder()->child()->next() ) 02014 if (!node->isDir()) 02015 static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount); 02016 } 02017 02018 void KMFolderCachedImap::listNamespaces() 02019 { 02020 ImapAccountBase::ListType type = ImapAccountBase::List; 02021 if ( mAccount->onlySubscribedFolders() ) 02022 type = ImapAccountBase::ListSubscribed; 02023 02024 kdDebug(5006) << "listNamespaces " << mNamespacesToList << endl; 02025 if ( mNamespacesToList.isEmpty() ) { 02026 mSyncState = SYNC_STATE_DELETE_SUBFOLDERS; 02027 mPersonalNamespacesCheckDone = true; 02028 02029 TQStringList ns = mAccount->namespaces()[ImapAccountBase::OtherUsersNS]; 02030 ns += mAccount->namespaces()[ImapAccountBase::SharedNS]; 02031 mNamespacesToCheck = ns.count(); 02032 for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it ) 02033 { 02034 if ( (*it).isEmpty() ) { 02035 // ignore empty listings as they have been listed before 02036 --mNamespacesToCheck; 02037 continue; 02038 } 02039 KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) ); 02040 job->setHonorLocalSubscription( true ); 02041 connect( job, TQT_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&, 02042 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)), 02043 this, TQT_SLOT(slotCheckNamespace(const TQStringList&, const TQStringList&, 02044 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&))); 02045 job->start(); 02046 } 02047 if ( mNamespacesToCheck == 0 ) { 02048 serverSyncInternal(); 02049 } 02050 return; 02051 } 02052 mPersonalNamespacesCheckDone = false; 02053 02054 TQString ns = mNamespacesToList.front(); 02055 mNamespacesToList.pop_front(); 02056 02057 mSyncState = SYNC_STATE_LIST_SUBFOLDERS2; 02058 newState( mProgress, i18n("Retrieving folders for namespace %1").arg(ns)); 02059 KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, 02060 mAccount->addPathToNamespace( ns ) ); 02061 job->setNamespace( ns ); 02062 job->setHonorLocalSubscription( true ); 02063 connect( job, TQT_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&, 02064 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)), 02065 this, TQT_SLOT(slotListResult(const TQStringList&, const TQStringList&, 02066 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&))); 02067 job->start(); 02068 } 02069 02070 void KMFolderCachedImap::slotCheckNamespace( const TQStringList& subfolderNames, 02071 const TQStringList& subfolderPaths, 02072 const TQStringList& subfolderMimeTypes, 02073 const TQStringList& subfolderAttributes, 02074 const ImapAccountBase::jobData& jobData ) 02075 { 02076 Q_UNUSED( subfolderPaths ); 02077 Q_UNUSED( subfolderMimeTypes ); 02078 Q_UNUSED( subfolderAttributes ); 02079 --mNamespacesToCheck; 02080 kdDebug(5006) << "slotCheckNamespace " << subfolderNames << ",remain=" << 02081 mNamespacesToCheck << endl; 02082 02083 // get a correct foldername: 02084 // strip / and make sure it does not contain the delimiter 02085 TQString name = jobData.path.mid( 1, jobData.path.length()-2 ); 02086 name.remove( mAccount->delimiterForNamespace( name ) ); 02087 if ( name.isEmpty() ) { 02088 // should not happen 02089 kdWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl; 02090 return; 02091 } 02092 02093 folder()->createChildFolder(); 02094 KMFolderNode *node = 0; 02095 for ( node = folder()->child()->first(); node; 02096 node = folder()->child()->next()) 02097 { 02098 if ( !node->isDir() && node->name() == name ) 02099 break; 02100 } 02101 if ( !subfolderNames.isEmpty() ) { 02102 if ( node ) { 02103 // folder exists so we have nothing to do - it will be listed later 02104 kdDebug(5006) << "found namespace folder " << name << endl; 02105 } else 02106 { 02107 // create folder 02108 kdDebug(5006) << "create namespace folder " << name << endl; 02109 KMFolder* newFolder = folder()->child()->createFolder( name, false, 02110 KMFolderTypeCachedImap ); 02111 if ( newFolder ) { 02112 KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>( newFolder->storage() ); 02113 f->setImapPath( mAccount->addPathToNamespace( name ) ); 02114 f->setNoContent( true ); 02115 f->setAccount( mAccount ); 02116 f->close("cachedimap"); 02117 kmkernel->dimapFolderMgr()->contentsChanged(); 02118 } 02119 } 02120 } else { 02121 if ( node ) { 02122 kdDebug(5006) << "delete namespace folder " << name << endl; 02123 KMFolder* fld = static_cast<KMFolder*>(node); 02124 kmkernel->dimapFolderMgr()->remove( fld ); 02125 } 02126 } 02127 02128 if ( mNamespacesToCheck == 0 ) { 02129 // all namespaces are done so continue with the next step 02130 serverSyncInternal(); 02131 } 02132 } 02133 02134 // This lists the subfolders on the server 02135 // and (in slotListResult) takes care of folders that have been removed on the server 02136 bool KMFolderCachedImap::listDirectory() 02137 { 02138 if( !mAccount->slave() ) { // sync aborted 02139 resetSyncState(); 02140 emit folderComplete( this, false ); 02141 return false; 02142 } 02143 mSubfolderState = imapInProgress; 02144 02145 // get the folders 02146 ImapAccountBase::ListType type = ImapAccountBase::List; 02147 if ( mAccount->onlySubscribedFolders() ) 02148 type = ImapAccountBase::ListSubscribed; 02149 KMail::ListJob* job = new KMail::ListJob( mAccount, type, this ); 02150 job->setHonorLocalSubscription( true ); 02151 connect( job, TQT_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&, 02152 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)), 02153 this, TQT_SLOT(slotListResult(const TQStringList&, const TQStringList&, 02154 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&))); 02155 job->start(); 02156 02157 return true; 02158 } 02159 02160 void KMFolderCachedImap::slotListResult( const TQStringList& folderNames, 02161 const TQStringList& folderPaths, 02162 const TQStringList& folderMimeTypes, 02163 const TQStringList& folderAttributes, 02164 const ImapAccountBase::jobData& jobData ) 02165 { 02166 Q_UNUSED( jobData ); 02167 //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths=" 02168 //<< folderPaths << " mimeTypes=" << folderMimeTypes << endl; 02169 mSubfolderNames = folderNames; 02170 mSubfolderPaths = folderPaths; 02171 mSubfolderMimeTypes = folderMimeTypes; 02172 mSubfolderState = imapFinished; 02173 mSubfolderAttributes = folderAttributes; 02174 //kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl; 02175 02176 folder()->createChildFolder(); 02177 KMFolderNode *node = folder()->child()->first(); 02178 bool root = ( this == mAccount->rootFolder() ); 02179 02180 TQPtrList<KMFolder> toRemove; 02181 bool emptyList = ( root && mSubfolderNames.empty() ); 02182 if ( !emptyList ) { 02183 while (node) { 02184 if (!node->isDir() ) { 02185 KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 02186 02187 if ( mSubfolderNames.findIndex(node->name()) == -1 ) { 02188 TQString name = node->name(); 02189 // as more than one namespace can be listed in the root folder we need to make sure 02190 // that the folder is within the current namespace 02191 bool isInNamespace = ( jobData.curNamespace.isEmpty() || 02192 jobData.curNamespace == mAccount->namespaceForFolder( f ) ); 02193 // ignore some cases 02194 bool ignore = root && ( f->imapPath() == "/INBOX/" || 02195 mAccount->isNamespaceFolder( name ) || !isInNamespace ); 02196 02197 // This subfolder isn't present on the server 02198 if( !f->imapPath().isEmpty() && !ignore ) { 02199 // The folder has an imap path set, so it has been 02200 // on the server before. Delete it locally. 02201 toRemove.append( f->folder() ); 02202 kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl; 02203 } 02204 } else { // folder both local and on server 02205 //kdDebug(5006) << node->name() << " is on the server." << endl; 02206 02210 int index = mSubfolderNames.findIndex( node->name() ); 02211 f->mFolderAttributes = folderAttributes[ index ]; 02212 } 02213 } else { 02214 //kdDebug(5006) << "skipping dir node:" << node->name() << endl; 02215 } 02216 node = folder()->child()->next(); 02217 } 02218 } 02219 02220 for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) { 02221 rescueUnsyncedMessagesAndDeleteFolder( doomed ); 02222 } 02223 02224 mProgress += 5; 02225 02226 // just in case there is nothing to rescue 02227 slotRescueDone( 0 ); 02228 } 02229 02230 // This synchronizes the local folders as needed (creation/deletion). No network communication here. 02231 void KMFolderCachedImap::listDirectory2() 02232 { 02233 TQString path = folder()->path(); 02234 kmkernel->dimapFolderMgr()->quiet(true); 02235 02236 bool root = ( this == mAccount->rootFolder() ); 02237 if ( root && !mAccount->hasInbox() ) 02238 { 02239 KMFolderCachedImap *f = 0; 02240 KMFolderNode *node; 02241 // create the INBOX 02242 for (node = folder()->child()->first(); node; node = folder()->child()->next()) 02243 if (!node->isDir() && node->name() == "INBOX") break; 02244 if (node) { 02245 f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 02246 } else { 02247 KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap); 02248 if ( newFolder ) { 02249 f = static_cast<KMFolderCachedImap*>(newFolder->storage()); 02250 } 02251 } 02252 if ( f ) { 02253 f->setAccount( mAccount ); 02254 f->setImapPath( "/INBOX/" ); 02255 f->folder()->setLabel( i18n("inbox") ); 02256 } 02257 if (!node) { 02258 if ( f ) 02259 f->close("cachedimap"); 02260 kmkernel->dimapFolderMgr()->contentsChanged(); 02261 } 02262 // so we have an INBOX 02263 mAccount->setHasInbox( true ); 02264 } 02265 02266 if ( root && !mSubfolderNames.isEmpty() ) { 02267 KMFolderCachedImap* parent = 02268 findParent( mSubfolderPaths.first(), mSubfolderNames.first() ); 02269 if ( parent ) { 02270 kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to " 02271 << parent->label() << endl; 02272 mSubfolderNames.clear(); 02273 } 02274 } 02275 02276 // Find all subfolders present on server but not on disk 02277 TQValueVector<int> foldersNewOnServer; 02278 for (uint i = 0; i < mSubfolderNames.count(); i++) { 02279 02280 // Find the subdir, if already present 02281 KMFolderCachedImap *f = 0; 02282 KMFolderNode *node = 0; 02283 for (node = folder()->child()->first(); node; 02284 node = folder()->child()->next()) 02285 if (!node->isDir() && node->name() == mSubfolderNames[i]) break; 02286 02287 if (!node) { 02288 // This folder is not present here 02289 // Either it's new on the server, or we just deleted it. 02290 TQString subfolderPath = mSubfolderPaths[i]; 02291 // The code used to look at the uidcache to know if it was "just deleted". 02292 // But this breaks with noContent folders and with shared folders. 02293 // So instead we keep a list in the account. 02294 bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath ); 02295 // That list is saved/restored across sessions, but to avoid any mistake, 02296 // ask for confirmation if the folder was deleted in a previous session 02297 // (could be that the folder was deleted & recreated meanwhile from another client...) 02298 if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) { 02299 locallyDeleted = KMessageBox::warningYesNo( 02300 0, i18n( "<qt><p>It seems that the folder <b>%1</b> was deleted. Do you want to delete it from the server?</p></qt>" ).arg( mSubfolderNames[i] ), TQString(), KStdGuiItem::del(), KStdGuiItem::cancel() ) == KMessageBox::Yes; 02301 } 02302 02303 if ( locallyDeleted ) { 02304 kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl; 02305 foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too 02306 } else { 02307 kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl; 02308 foldersNewOnServer.append( i ); 02309 } 02310 } else { // Folder found locally 02311 if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap ) 02312 f = dynamic_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 02313 if( f ) { 02314 // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath() 02315 // << "\nSetting imapPath " << mSubfolderPaths[i] << endl; 02316 // Write folder settings 02317 f->setAccount(mAccount); 02318 f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory"); 02319 f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest"); 02320 f->setImapPath(mSubfolderPaths[i]); 02321 } 02322 } 02323 } 02324 02325 /* In case we are ignoring non-groupware folders, and this is the groupware 02326 * main account, find out the contents types of folders that have newly 02327 * appeared on the server. Otherwise just create them and finish listing. 02328 * If a folder is already known to be locally unsubscribed, it won't be 02329 * listed at all, on this level, so these are only folders that we are 02330 * seeing for the first time. */ 02331 02332 /* Note: We ask the globalsettings, and not the current state of the 02333 * kmkernel->iCalIface().isEnabled(), since that is false during the 02334 * very first sync, where we already want to filter. */ 02335 if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount() 02336 && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id() 02337 && mAccount->hasAnnotationSupport() 02338 && GlobalSettings::self()->theIMAPResourceEnabled() 02339 && !foldersNewOnServer.isEmpty() ) { 02340 02341 TQStringList paths; 02342 for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) 02343 paths << mSubfolderPaths[ foldersNewOnServer[i] ]; 02344 02345 AnnotationJobs::MultiUrlGetAnnotationJob* job = 02346 AnnotationJobs::multiUrlGetAnnotation( mAccount->slave(), mAccount->getUrl(), paths, KOLAB_FOLDERTYPE ); 02347 ImapAccountBase::jobData jd( TQString(), folder() ); 02348 jd.cancellable = true; 02349 mAccount->insertJob(job, jd); 02350 connect( job, TQT_SIGNAL(result(KIO::Job *)), 02351 TQT_SLOT(slotMultiUrlGetAnnotationResult(KIO::Job *)) ); 02352 02353 } else { 02354 createFoldersNewOnServerAndFinishListing( foldersNewOnServer ); 02355 } 02356 } 02357 02358 void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const TQValueVector<int> foldersNewOnServer ) 02359 { 02360 for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) { 02361 int idx = foldersNewOnServer[i]; 02362 KMFolder* newFolder = folder()->child()->createFolder( mSubfolderNames[idx], false, KMFolderTypeCachedImap); 02363 if (newFolder) { 02364 KMFolderCachedImap *f = dynamic_cast<KMFolderCachedImap*>(newFolder->storage()); 02365 kdDebug(5006) << " ####### Locally creating folder " << mSubfolderNames[idx] <<endl; 02366 f->close("cachedimap"); 02367 f->setAccount(mAccount); 02368 f->mAnnotationFolderType = "FROMSERVER"; 02369 f->setNoContent(mSubfolderMimeTypes[idx] == "inode/directory"); 02370 f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest"); 02371 f->setImapPath(mSubfolderPaths[idx]); 02372 f->mFolderAttributes = mSubfolderAttributes[idx]; 02373 mNewlyCreatedSubfolders.append( TQGuardedPtr<KMFolderCachedImap>( f ) ); 02374 kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl; 02375 //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl; 02376 kmkernel->dimapFolderMgr()->contentsChanged(); 02377 } else { 02378 kdDebug(5006) << "can't create folder " << mSubfolderNames[idx] <<endl; 02379 } 02380 } 02381 02382 kmkernel->dimapFolderMgr()->quiet(false); 02383 emit listComplete(this); 02384 if ( !mPersonalNamespacesCheckDone ) { 02385 // we're not done with the namespaces 02386 mSyncState = SYNC_STATE_LIST_NAMESPACES; 02387 } 02388 serverSyncInternal(); 02389 } 02390 02391 //----------------------------------------------------------------------------- 02392 KMFolderCachedImap* KMFolderCachedImap::findParent( const TQString& path, 02393 const TQString& name ) 02394 { 02395 TQString parent = path.left( path.length() - name.length() - 2 ); 02396 if ( parent.length() > 1 ) 02397 { 02398 // extract name of the parent 02399 parent = parent.right( parent.length() - 1 ); 02400 if ( parent != label() ) 02401 { 02402 KMFolderNode *node = folder()->child()->first(); 02403 // look for a better parent 02404 while ( node ) 02405 { 02406 if ( node->name() == parent ) 02407 { 02408 KMFolder* fld = static_cast<KMFolder*>(node); 02409 KMFolderCachedImap* imapFld = 02410 static_cast<KMFolderCachedImap*>( fld->storage() ); 02411 return imapFld; 02412 } 02413 node = folder()->child()->next(); 02414 } 02415 } 02416 } 02417 return 0; 02418 } 02419 02420 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success) 02421 { 02422 Q_UNUSED(sub); 02423 //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl; 02424 if ( success ) { 02425 serverSyncInternal(); 02426 } 02427 else 02428 { 02429 // success == false means the sync was aborted. 02430 if ( mCurrentSubfolder ) { 02431 Q_ASSERT( sub == mCurrentSubfolder ); 02432 disconnectSubFolderSignals(); 02433 } 02434 02435 // Next step would be to check quota limits and then to close the folder, but don't bother with 02436 // both and close the folder right here, since we aborted. 02437 mSubfoldersForSync.clear(); 02438 mSyncState = SYNC_STATE_INITIAL; 02439 close("cachedimap"); 02440 emit syncStateChanged(); 02441 emit folderComplete( this, false ); 02442 } 02443 } 02444 02445 void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged() 02446 { 02447 if ( !mQuotaOnly ) { 02448 mSomeSubFolderCloseToQuotaChanged = true; 02449 } 02450 } 02451 02452 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const TQByteArray & data) 02453 { 02454 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 02455 if (it == mAccount->jobsEnd()) return; 02456 TQBuffer buff((*it).data); 02457 buff.open(IO_WriteOnly | IO_Append); 02458 buff.writeBlock(data.data(), data.size()); 02459 buff.close(); 02460 } 02461 02462 FolderJob* 02463 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder, 02464 TQString, const AttachmentStrategy* ) const 02465 { 02466 TQPtrList<KMMessage> msgList; 02467 msgList.append( msg ); 02468 CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 ); 02469 job->setParentFolder( this ); 02470 return job; 02471 } 02472 02473 FolderJob* 02474 KMFolderCachedImap::doCreateJob( TQPtrList<KMMessage>& msgList, const TQString& sets, 02475 FolderJob::JobType jt, KMFolder *folder ) const 02476 { 02477 //FIXME: how to handle sets here? 02478 Q_UNUSED( sets ); 02479 CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 ); 02480 job->setParentFolder( this ); 02481 return job; 02482 } 02483 02484 void 02485 KMFolderCachedImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state ) 02486 { 02487 mUserRights = userRights; 02488 mUserRightsState = state; 02489 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); 02490 } 02491 02492 void 02493 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder ) 02494 { 02495 if ( folder->storage() == this ) { 02496 disconnect( mAccount, TQT_SIGNAL( receivedUserRights( KMFolder* ) ), 02497 this, TQT_SLOT( slotReceivedUserRights( KMFolder* ) ) ); 02498 if ( mUserRightsState == KMail::ACLJobs::Ok ) { 02499 setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 ); 02500 } 02501 mProgress += 5; 02502 serverSyncInternal(); 02503 } 02504 } 02505 02506 void 02507 KMFolderCachedImap::setReadOnly( bool readOnly ) 02508 { 02509 if ( readOnly != mReadOnly ) { 02510 mReadOnly = readOnly; 02511 emit readOnlyChanged( folder() ); 02512 } 02513 } 02514 02515 void 02516 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job* job, const KMail::ACLList& aclList ) 02517 { 02518 if ( folder->storage() == this ) { 02519 disconnect( mAccount, TQT_SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )), 02520 this, TQT_SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) ); 02521 mACLListState = job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok; 02522 mACLList = aclList; 02523 serverSyncInternal(); 02524 } 02525 } 02526 02527 void 02528 KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info ) 02529 { 02530 setQuotaInfo( info ); 02531 } 02532 02533 void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info ) 02534 { 02535 if ( info != mQuotaInfo ) { 02536 const bool wasCloseToQuota = isCloseToQuota(); 02537 mQuotaInfo = info; 02538 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); 02539 if ( wasCloseToQuota != isCloseToQuota() ) { 02540 emit closeToQuotaChanged(); 02541 } 02542 emit folderSizeChanged(); 02543 } 02544 } 02545 02546 void 02547 KMFolderCachedImap::setACLList( const ACLList& arr ) 02548 { 02549 mACLList = arr; 02550 mACLListState = KMail::ACLJobs::Ok; 02551 } 02552 02553 void 02554 KMFolderCachedImap::slotMultiSetACLResult(KIO::Job *job) 02555 { 02556 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 02557 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen 02558 if ( (*it).parent != folder() ) return; // Shouldn't happen 02559 02560 if ( job->error() ) 02561 // Display error but don't abort the sync just for this 02562 // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel 02563 job->showErrorDialog(); 02564 else 02565 kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL ); 02566 02567 if (mAccount->slave()) mAccount->removeJob(job); 02568 serverSyncInternal(); 02569 } 02570 02571 void 02572 KMFolderCachedImap::slotACLChanged( const TQString& userId, int permissions ) 02573 { 02574 // The job indicates success in changing the permissions for this user 02575 // -> we note that it's been done. 02576 for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) { 02577 if ( (*it).userId == userId && (*it).permissions == permissions ) { 02578 if ( permissions == -1 ) // deleted 02579 mACLList.erase( it ); 02580 else // added/modified 02581 (*it).changed = false; 02582 return; 02583 } 02584 } 02585 } 02586 02587 // called by KMAcctCachedImap::killAllJobs 02588 void KMFolderCachedImap::resetSyncState() 02589 { 02590 if ( mSyncState == SYNC_STATE_INITIAL ) return; 02591 mSubfoldersForSync.clear(); 02592 mNewlyCreatedSubfolders.clear(); 02593 mSyncState = SYNC_STATE_INITIAL; 02594 close("cachedimap"); 02595 // Don't use newState here, it would revert to mProgress (which is < current value when listing messages) 02596 KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem(); 02597 TQString str = i18n("Aborted"); 02598 if (progressItem) 02599 progressItem->setStatus( str ); 02600 emit statusMsg( str ); 02601 emit syncStateChanged(); 02602 } 02603 02604 void KMFolderCachedImap::slotIncreaseProgress() 02605 { 02606 mProgress += 5; 02607 } 02608 02609 void KMFolderCachedImap::newState( int progress, const TQString& syncStatus ) 02610 { 02611 //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl; 02612 KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem(); 02613 if( progressItem ) 02614 progressItem->setCompletedItems( progress ); 02615 if ( !syncStatus.isEmpty() ) { 02616 TQString str; 02617 // For a subfolder, show the label. But for the main folder, it's already shown. 02618 if ( mAccount->imapFolder() == this ) 02619 str = syncStatus; 02620 else 02621 str = TQString( "%1: %2" ).arg( label() ).arg( syncStatus ); 02622 if( progressItem ) 02623 progressItem->setStatus( str ); 02624 emit statusMsg( str ); 02625 } 02626 if( progressItem ) 02627 progressItem->updateProgress(); 02628 } 02629 02630 void KMFolderCachedImap::setSubfolderState( imapState state ) 02631 { 02632 mSubfolderState = state; 02633 if ( state == imapNoInformation && folder()->child() ) 02634 { 02635 // pass through to childs 02636 KMFolderNode* node; 02637 TQPtrListIterator<KMFolderNode> it( *folder()->child() ); 02638 for ( ; (node = it.current()); ) 02639 { 02640 ++it; 02641 if (node->isDir()) continue; 02642 KMFolder *folder = static_cast<KMFolder*>(node); 02643 static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state ); 02644 } 02645 } 02646 } 02647 02648 void KMFolderCachedImap::setImapPath(const TQString &path) 02649 { 02650 mImapPath = path; 02651 } 02652 02653 static bool isFolderTypeKnownToUs( const TQString &type ) 02654 { 02655 for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) { 02656 FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i ); 02657 if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) 02658 return true; 02659 } 02660 return false; 02661 } 02662 02663 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc) 02664 // It is updated from the folder contents type and whether it's a standard resource folder. 02665 // This happens during the syncing phase and during initFolder for a new folder. 02666 // Don't do it earlier, e.g. from setContentsType: 02667 // on startup, it's too early there to know if this is a standard resource folder. 02668 void KMFolderCachedImap::updateAnnotationFolderType() 02669 { 02670 TQString oldType = mAnnotationFolderType; 02671 TQString oldSubType; 02672 int dot = oldType.find( '.' ); 02673 if ( dot != -1 ) { 02674 oldType.truncate( dot ); 02675 oldSubType = mAnnotationFolderType.mid( dot + 1 ); 02676 } 02677 02678 TQString newType, newSubType; 02679 // We want to store an annotation on the folder only if using the kolab storage. 02680 if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) { 02681 newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType ); 02682 if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) ) 02683 newSubType = "default"; 02684 else if ( oldSubType != "default" ) 02685 newSubType = oldSubType; // preserve unknown subtypes, like drafts etc. 02686 } 02687 02688 // We do not want to overwrite custom folder types (which we treat as mail folders). 02689 // So only overwrite custom folder types if the user changed the folder type himself to something 02690 // other than mail. 02691 const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) || 02692 ( mContentsType != ContentsTypeMail ); 02693 02694 //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl; 02695 if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) { 02696 mAnnotationFolderType = newType + ( newSubType.isEmpty() ? TQString() : "."+newSubType ); 02697 mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync 02698 kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl; 02699 } 02700 // Ensure that further readConfig()s don't lose mAnnotationFolderType 02701 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); 02702 } 02703 02704 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor ) 02705 { 02706 if ( mIncidencesFor != incfor ) { 02707 mIncidencesFor = incfor; 02708 mIncidencesForChanged = true; 02709 } 02710 } 02711 02712 void KMFolderCachedImap::setSharedSeenFlags(bool b) 02713 { 02714 if ( mSharedSeenFlags != b ) { 02715 mSharedSeenFlags = b; 02716 mSharedSeenFlagsChanged = true; 02717 } 02718 } 02719 02720 void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQString& value, bool found) 02721 { 02722 if ( entry == KOLAB_FOLDERTYPE ) { 02723 // There are four cases. 02724 // 1) no content-type on server -> set it 02725 // 2) different content-type on server, locally changed -> set it (we don't even come here) 02726 // 3) different (known) content-type on server, no local change -> get it 02727 // 4) different unknown content-type on server, probably some older version -> set it 02728 if ( found ) { 02729 TQString type = value; 02730 TQString subtype; 02731 int dot = value.find( '.' ); 02732 if ( dot != -1 ) { 02733 type.truncate( dot ); 02734 subtype = value.mid( dot + 1 ); 02735 } 02736 bool foundKnownType = false; 02737 for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) { 02738 FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i ); 02739 if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) { 02740 // Case 3: known content-type on server, get it 02741 //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl; 02742 if ( contentsType != ContentsTypeMail ) 02743 kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML ); 02744 mAnnotationFolderType = value; 02745 if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent() 02746 && GlobalSettings::self()->theIMAPResourceEnabled() 02747 && subtype == "default" ) { 02748 // Truncate subtype if this folder can't be a default resource folder for us, 02749 // although it apparently is for someone else. 02750 mAnnotationFolderType = type; 02751 kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl; 02752 } 02753 setContentsType( contentsType ); 02754 mAnnotationFolderTypeChanged = false; // we changed it, not the user 02755 foundKnownType = true; 02756 02757 // Users don't read events/contacts/etc. in kmail, so mark them all as read. 02758 // This is done in cachedimapjob when getting new messages, but do it here too, 02759 // for the initial set of messages when we didn't know this was a resource folder yet, 02760 // for old folders, etc. 02761 if ( contentsType != ContentsTypeMail ) 02762 markUnreadAsRead(); 02763 02764 break; 02765 } 02766 } 02767 if ( !foundKnownType ) { 02768 //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl; 02769 02770 // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069. 02771 // Treat the content-type as mail until we change it ourselves. 02772 mAnnotationFolderTypeChanged = false; 02773 mAnnotationFolderType = value; 02774 setContentsType( ContentsTypeMail ); 02775 } 02776 02777 // Ensure that further readConfig()s don't lose mAnnotationFolderType 02778 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); 02779 // TODO handle subtype (inbox, drafts, sentitems, junkemail) 02780 } 02781 else if ( !mReadOnly ) { 02782 // Case 1: server doesn't have content-type, set it 02783 //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl; 02784 mAnnotationFolderTypeChanged = true; 02785 } 02786 } else if ( entry == KOLAB_INCIDENCESFOR ) { 02787 if ( found ) { 02788 mIncidencesFor = incidencesForFromString( value ); 02789 Q_ASSERT( mIncidencesForChanged == false ); 02790 } 02791 } else if ( entry == KOLAB_SHAREDSEEN ) { 02792 if ( found ) { 02793 mSharedSeenFlags = value == "true"; 02794 } 02795 } 02796 } 02797 02798 void KMFolderCachedImap::slotGetAnnotationResult( KIO::Job* job ) 02799 { 02800 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 02801 Q_ASSERT( it != mAccount->jobsEnd() ); 02802 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen 02803 Q_ASSERT( (*it).parent == folder() ); 02804 if ( (*it).parent != folder() ) return; // Shouldn't happen 02805 02806 AnnotationJobs::GetAnnotationJob* annjob = static_cast<AnnotationJobs::GetAnnotationJob *>( job ); 02807 if ( annjob->error() ) { 02808 if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) { 02809 // that's when the imap server doesn't support annotations 02810 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML 02811 && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() ) 02812 KMessageBox::error( 0, i18n( "The IMAP server %1 does not have support for IMAP annotations. The XML storage cannot be used on this server; please re-configure KMail differently." ).arg( mAccount->host() ) ); 02813 mAccount->setHasNoAnnotationSupport(); 02814 } 02815 else 02816 kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl; 02817 } 02818 02819 if (mAccount->slave()) mAccount->removeJob(job); 02820 mProgress += 2; 02821 serverSyncInternal(); 02822 } 02823 02824 void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( KIO::Job* job ) 02825 { 02826 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 02827 Q_ASSERT( it != mAccount->jobsEnd() ); 02828 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen 02829 Q_ASSERT( (*it).parent == folder() ); 02830 if ( (*it).parent != folder() ) return; // Shouldn't happen 02831 02832 TQValueVector<int> folders; 02833 AnnotationJobs::MultiUrlGetAnnotationJob* annjob 02834 = static_cast<AnnotationJobs::MultiUrlGetAnnotationJob *>( job ); 02835 if ( annjob->error() ) { 02836 if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) { 02837 // that's when the imap server doesn't support annotations 02838 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML 02839 && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() ) 02840 KMessageBox::error( 0, i18n( "The IMAP server %1 doesn't have support for imap annotations. The XML storage cannot be used on this server, please re-configure KMail differently" ).arg( mAccount->host() ) ); 02841 mAccount->setHasNoAnnotationSupport(); 02842 } 02843 else 02844 kdWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job->errorString() << endl; 02845 } else { 02846 // we got the annotation allright, let's filter out the ones with the wrong type 02847 TQMap<TQString, TQString> annotations = annjob->annotations(); 02848 TQMap<TQString, TQString>::Iterator it = annotations.begin(); 02849 for ( ; it != annotations.end(); ++it ) { 02850 const TQString folderPath = it.key(); 02851 const TQString annotation = it.data(); 02852 kdDebug(5006) << k_funcinfo << "Folder: " << folderPath << " has type: " << annotation << endl; 02853 // we're only interested in the main type 02854 TQString type(annotation); 02855 int dot = annotation.find( '.' ); 02856 if ( dot != -1 ) type.truncate( dot ); 02857 type = type.simplifyWhiteSpace(); 02858 02859 const int idx = mSubfolderPaths.findIndex( folderPath ); 02860 const bool isNoContent = mSubfolderMimeTypes[idx] == "inode/directory"; 02861 if ( ( isNoContent && type.isEmpty() ) 02862 || ( !type.isEmpty() && type != KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail ) ) ) { 02863 folders.append( idx ); 02864 kdDebug(5006) << k_funcinfo << " subscribing to: " << folderPath << endl; 02865 } else { 02866 kdDebug(5006) << k_funcinfo << " automatically unsubscribing from: " << folderPath << endl; 02867 mAccount->changeLocalSubscription( folderPath, false ); 02868 } 02869 } 02870 } 02871 02872 if (mAccount->slave()) mAccount->removeJob(job); 02873 createFoldersNewOnServerAndFinishListing( folders ); 02874 } 02875 02876 void KMFolderCachedImap::slotQuotaResult( KIO::Job* job ) 02877 { 02878 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 02879 Q_ASSERT( it != mAccount->jobsEnd() ); 02880 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen 02881 Q_ASSERT( (*it).parent == folder() ); 02882 if ( (*it).parent != folder() ) return; // Shouldn't happen 02883 02884 QuotaJobs::GetStorageQuotaJob* quotajob = static_cast<QuotaJobs::GetStorageQuotaJob *>( job ); 02885 QuotaInfo empty; 02886 if ( quotajob->error() ) { 02887 if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) { 02888 // that's when the imap server doesn't support quota 02889 mAccount->setHasNoQuotaSupport(); 02890 setQuotaInfo( empty ); 02891 } 02892 else 02893 kdWarning(5006) << "slotGetQuotaResult: " << job->errorString() << endl; 02894 } 02895 02896 if (mAccount->slave()) mAccount->removeJob(job); 02897 mProgress += 2; 02898 serverSyncInternal(); 02899 } 02900 02901 void 02902 KMFolderCachedImap::slotAnnotationChanged( const TQString& entry, const TQString& attribute, const TQString& value ) 02903 { 02904 Q_UNUSED( attribute ); 02905 Q_UNUSED( value ); 02906 //kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl; 02907 if ( entry == KOLAB_FOLDERTYPE ) 02908 mAnnotationFolderTypeChanged = false; 02909 else if ( entry == KOLAB_INCIDENCESFOR ) { 02910 mIncidencesForChanged = false; 02911 // The incidences-for changed, we must trigger the freebusy creation. 02912 // HACK: in theory we would need a new enum value for this. 02913 kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL ); 02914 } else if ( entry == KOLAB_SHAREDSEEN ) { 02915 mSharedSeenFlagsChanged = false; 02916 } 02917 } 02918 02919 void KMFolderCachedImap::slotTestAnnotationResult(KIO::Job *job) 02920 { 02921 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 02922 Q_ASSERT( it != mAccount->jobsEnd() ); 02923 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen 02924 Q_ASSERT( (*it).parent == folder() ); 02925 if ( (*it).parent != folder() ) return; // Shouldn't happen 02926 02927 mAccount->setAnnotationCheckPassed( true ); 02928 if ( job->error() ) { 02929 kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl; 02930 mAccount->setHasNoAnnotationSupport( ); 02931 } else { 02932 kdDebug(5006) << "Test Annotation was passed OK" << endl; 02933 } 02934 if (mAccount->slave()) mAccount->removeJob(job); 02935 serverSyncInternal(); 02936 } 02937 02938 void 02939 KMFolderCachedImap::slotSetAnnotationResult(KIO::Job *job) 02940 { 02941 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 02942 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen 02943 if ( (*it).parent != folder() ) return; // Shouldn't happen 02944 02945 bool cont = true; 02946 if ( job->error() ) { 02947 // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail 02948 if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail ) { 02949 if (mAccount->slave()) mAccount->removeJob(job); 02950 } else { 02951 cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' ); 02952 } 02953 } else { 02954 if (mAccount->slave()) mAccount->removeJob(job); 02955 } 02956 if ( cont ) 02957 serverSyncInternal(); 02958 } 02959 02960 void KMFolderCachedImap::slotUpdateLastUid() 02961 { 02962 if( mTentativeHighestUid != 0 ) { 02963 02964 // Sanity checking: 02965 // By now all new mails should be downloaded, which means 02966 // that iteration over the folder should yield only UIDs 02967 // lower or equal to what we think the highes ist, and the 02968 // highest one as well. If not, our notion of the highest 02969 // uid we've seen thus far is wrong, which is dangerous, so 02970 // don't update the mLastUid, then. 02971 // Not entirely true though, mails might have been moved out 02972 // of the folder already by filters, thus giving us a higher tentative 02973 // uid than we actually observe here. 02974 bool sane = count() == 0; 02975 02976 for (int i=0;i<count(); i++ ) { 02977 ulong uid = getMsgBase(i)->UID(); 02978 if ( uid > mTentativeHighestUid && uid > lastUid() ) { 02979 kdWarning(5006) << "DANGER: Either the server listed a wrong highest uid, " 02980 "or we parsed it wrong. Send email to adam@kde.org, please, and include this log." << endl; 02981 kdWarning(5006) << "uid: " << uid << " mTentativeHighestUid: " << mTentativeHighestUid << endl; 02982 assert( false ); 02983 break; 02984 } else { 02985 sane = true; 02986 } 02987 } 02988 if (sane) { 02989 #if MAIL_LOSS_DEBUGGING 02990 kdDebug(5006) << "Tentative highest UID test was sane, writing out: " << mTentativeHighestUid << endl; 02991 #endif 02992 setLastUid( mTentativeHighestUid ); 02993 } 02994 } 02995 mTentativeHighestUid = 0; 02996 } 02997 02998 bool KMFolderCachedImap::isMoveable() const 02999 { 03000 return ( hasChildren() == HasNoChildren && 03001 !folder()->isSystemFolder() ) ? true : false; 03002 } 03003 03004 void KMFolderCachedImap::slotFolderDeletionOnServerFinished() 03005 { 03006 for ( TQStringList::const_iterator it = foldersForDeletionOnServer.constBegin(); 03007 it != foldersForDeletionOnServer.constEnd(); ++it ) { 03008 KURL url( mAccount->getUrl() ); 03009 url.setPath( *it ); 03010 kmkernel->iCalIface().folderDeletedOnServer( url ); 03011 } 03012 serverSyncInternal(); 03013 } 03014 03015 int KMFolderCachedImap::createIndexFromContentsRecursive() 03016 { 03017 if ( !folder() || !folder()->child() ) 03018 return 0; 03019 03020 KMFolderNode *node = 0; 03021 for( TQPtrListIterator<KMFolderNode> it( *folder()->child() ); (node = it.current()); ++it ) { 03022 if( !node->isDir() ) { 03023 KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 03024 kdDebug() << k_funcinfo << "Re-indexing: " << storage->folder()->label() << endl; 03025 int rv = storage->createIndexFromContentsRecursive(); 03026 if ( rv > 0 ) 03027 return rv; 03028 } 03029 } 03030 03031 return createIndexFromContents(); 03032 } 03033 03034 void KMFolderCachedImap::setAlarmsBlocked( bool blocked ) 03035 { 03036 mAlarmsBlocked = blocked; 03037 } 03038 03039 bool KMFolderCachedImap::alarmsBlocked() const 03040 { 03041 return mAlarmsBlocked; 03042 } 03043 03044 bool KMFolderCachedImap::isCloseToQuota() const 03045 { 03046 bool closeToQuota = false; 03047 if ( mQuotaInfo.isValid() && mQuotaInfo.max().toInt() > 0 ) { 03048 const int ratio = mQuotaInfo.current().toInt() * 100 / mQuotaInfo.max().toInt(); 03049 //kdDebug(5006) << "Quota ratio: " << ratio << "% " << mQuotaInfo.toString() << endl; 03050 closeToQuota = ( ratio > 0 && ratio >= GlobalSettings::closeToQuotaThreshold() ); 03051 } 03052 //kdDebug(5006) << "Folder: " << folder()->prettyURL() << " is over quota: " << closeToQuota << endl; 03053 return closeToQuota; 03054 } 03055 03056 KMCommand* KMFolderCachedImap::rescueUnsyncedMessages() 03057 { 03058 TQValueList<unsigned long> newMsgs = findNewMessages(); 03059 kdDebug() << k_funcinfo << newMsgs << " of " << count() << endl; 03060 if ( newMsgs.isEmpty() ) 03061 return 0; 03062 KMFolder *dest = 0; 03063 bool manualMove = true; 03064 while ( GlobalSettings::autoLostFoundMove() ) { 03065 // find the inbox of this account 03066 KMFolder *inboxFolder = kmkernel->findFolderById( TQString(".%1.directory/INBOX").arg( account()->id() ) ); 03067 if ( !inboxFolder ) { 03068 kdWarning(5006) << k_funcinfo << "inbox not found!" << endl; 03069 break; 03070 } 03071 KMFolderDir *inboxDir = inboxFolder->child(); 03072 if ( !inboxDir && !inboxFolder->storage() ) 03073 break; 03074 assert( inboxFolder->storage()->folderType() == KMFolderTypeCachedImap ); 03075 03076 // create lost+found folder if needed 03077 KMFolderNode *node; 03078 KMFolder *lfFolder = 0; 03079 if ( !(node = inboxDir->hasNamedFolder( i18n("lost+found") )) ) { 03080 kdDebug(5006) << k_funcinfo << "creating lost+found folder" << endl; 03081 KMFolder* folder = kmkernel->dimapFolderMgr()->createFolder( 03082 i18n("lost+found"), false, KMFolderTypeCachedImap, inboxDir ); 03083 if ( !folder || !folder->storage() ) 03084 break; 03085 static_cast<KMFolderCachedImap*>( folder->storage() )->initializeFrom( 03086 static_cast<KMFolderCachedImap*>( inboxFolder->storage() ) ); 03087 folder->storage()->setContentsType( KMail::ContentsTypeMail ); 03088 folder->storage()->writeConfig(); 03089 lfFolder = folder; 03090 } else { 03091 kdDebug(5006) << k_funcinfo << "found lost+found folder" << endl; 03092 lfFolder = dynamic_cast<KMFolder*>( node ); 03093 } 03094 if ( !lfFolder || !lfFolder->createChildFolder() || !lfFolder->storage() ) 03095 break; 03096 03097 // create subfolder for this incident 03098 TQDate today = TQDate::currentDate(); 03099 TQString baseName = folder()->label() + "-" + TQString::number( today.year() ) 03100 + (today.month() < 10 ? "0" : "" ) + TQString::number( today.month() ) 03101 + (today.day() < 10 ? "0" : "" ) + TQString::number( today.day() ); 03102 TQString name = baseName; 03103 int suffix = 0; 03104 while ( (node = lfFolder->child()->hasNamedFolder( name )) ) { 03105 ++suffix; 03106 name = baseName + '-' + TQString::number( suffix ); 03107 } 03108 kdDebug(5006) << k_funcinfo << "creating lost+found folder " << name << endl; 03109 dest = kmkernel->dimapFolderMgr()->createFolder( name, false, KMFolderTypeCachedImap, lfFolder->child() ); 03110 if ( !dest || !dest->storage() ) 03111 break; 03112 static_cast<KMFolderCachedImap*>( dest->storage() )->initializeFrom( 03113 static_cast<KMFolderCachedImap*>( lfFolder->storage() ) ); 03114 dest->storage()->setContentsType( contentsType() ); 03115 dest->storage()->writeConfig(); 03116 03117 KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which " 03118 "have not been uploaded to the server yet, but the folder has been deleted " 03119 "on the server or you do not " 03120 "have sufficient access rights on the folder to upload them.</p>" 03121 "<p>All affected messages will therefore be moved to <b>%2</b> " 03122 "to avoid data loss.</p>").arg( folder()->prettyURL() ).arg( dest->prettyURL() ), 03123 i18n("Insufficient access rights") ); 03124 manualMove = false; 03125 break; 03126 } 03127 03128 if ( manualMove ) { 03129 const TQString msg ( i18n( "<p>There are new messages in this folder (%1), which " 03130 "have not been uploaded to the server yet, but the folder has been deleted " 03131 "on the server or you do not " 03132 "have sufficient access rights on the folder now to upload them. " 03133 "Please contact your administrator to allow upload of new messages " 03134 "to you, or move them out of this folder.</p> " 03135 "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) ); 03136 if ( KMessageBox::warningYesNo( 0, msg, TQString(), i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) { 03137 KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(), 03138 i18n("Move Messages to Folder"), true ); 03139 if ( dlg.exec() ) { 03140 dest = dlg.folder(); 03141 } 03142 } 03143 } 03144 if ( dest ) { 03145 TQPtrList<KMMsgBase> msgs; 03146 for( int i = 0; i < count(); ++i ) { 03147 KMMsgBase *msg = getMsgBase( i ); 03148 if( !msg ) continue; /* what goes on if getMsg() returns 0? */ 03149 if ( msg->UID() == 0 ) 03150 msgs.append( msg ); 03151 } 03152 KMCommand *command = new KMMoveCommand( dest, msgs ); 03153 command->start(); 03154 return command; 03155 } 03156 return 0; 03157 } 03158 03159 void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root ) 03160 { 03161 kdDebug() << k_funcinfo << folder << " " << root << endl; 03162 if ( root ) 03163 mToBeDeletedAfterRescue.append( folder ); 03164 folder->open("cachedimap"); 03165 KMFolderCachedImap* storage = dynamic_cast<KMFolderCachedImap*>( folder->storage() ); 03166 if ( storage ) { 03167 KMCommand *command = storage->rescueUnsyncedMessages(); 03168 if ( command ) { 03169 connect( command, TQT_SIGNAL(completed(KMCommand*)), 03170 TQT_SLOT(slotRescueDone(KMCommand*)) ); 03171 ++mRescueCommandCount; 03172 } else { 03173 // nothing to rescue, close folder 03174 // (we don't need to close it in the other case, it will be deleted anyway) 03175 folder->close("cachedimap"); 03176 } 03177 } 03178 if ( folder->child() ) { 03179 KMFolderNode *node = folder->child()->first(); 03180 while (node) { 03181 if (!node->isDir() ) { 03182 KMFolder *subFolder = static_cast<KMFolder*>( node ); 03183 rescueUnsyncedMessagesAndDeleteFolder( subFolder, false ); 03184 } 03185 node = folder->child()->next(); 03186 } 03187 } 03188 } 03189 03190 void KMFolderCachedImap::slotRescueDone(KMCommand * command) 03191 { 03192 // FIXME: check command result 03193 if ( command ) 03194 --mRescueCommandCount; 03195 if ( mRescueCommandCount > 0 ) 03196 return; 03197 for ( TQValueList<KMFolder*>::ConstIterator it = mToBeDeletedAfterRescue.constBegin(); 03198 it != mToBeDeletedAfterRescue.constEnd(); ++it ) { 03199 kmkernel->dimapFolderMgr()->remove( *it ); 03200 } 03201 mToBeDeletedAfterRescue.clear(); 03202 serverSyncInternal(); 03203 } 03204 03205 void KMFolderCachedImap::slotRenameFolderFinished() 03206 { 03207 // The syncing code assumes the folder was opened by us, and later closes it. So better 03208 // make sure the reference count is correct, since the folder was force-closed by the rename. 03209 // Otherwise bad things can happen, see https://issues.kolab.org/issue3853. 03210 open( "cachedimap" ); 03211 serverSyncInternal(); 03212 } 03213 03214 bool KMFolderCachedImap::canDeleteMessages() const 03215 { 03216 if ( isReadOnly() ) 03217 return false; 03218 if ( mUserRightsState == KMail::ACLJobs::Ok && !(userRights() & ACLJobs::Delete) ) 03219 return false; 03220 return true; 03221 } 03222 03223 bool KMFolderCachedImap::mailCheckInProgress() const 03224 { 03225 return mSyncState != SYNC_STATE_INITIAL; 03226 } 03227 03228 #include "kmfoldercachedimap.moc"