2 * kmfoldercachedimap.cpp
4 * Copyright (c) 2002-2004 Bo Thorsen <bo@sonofthor.dk>
5 * Copyright (c) 2002-2003 Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give
21 * permission to link the code of this program with any edition of
22 * the Qt library by Trolltech AS, Norway (or with modified versions
23 * of Qt that use the same license as Qt), and distribute linked
24 * combinations including the two. You must obey the GNU General
25 * Public License in all respects for all of the code used other than
26 * Qt. If you modify this file, you may extend this exception to
27 * your version of the file, but you are not obligated to do so. If
28 * you do not wish to do so, delete this exception statement from
32 #include "kmfoldercachedimap.h"
38 #include "globalsettings.h"
40 #include "undostack.h"
41 #include "kmfoldermgr.h"
42 #include "kmacctcachedimap.h"
43 #include "accountmanager.h"
44 using KMail::AccountManager
;
45 #include "kmailicalifaceimpl.h"
49 #include "broadcaststatus.h"
50 using KPIM::BroadcastStatus
;
51 #include "progressmanager.h"
52 using KMail::CachedImapJob
;
53 #include "imapaccountbase.h"
54 using KMail::ImapAccountBase
;
57 #include "folderselectiondialog.h"
58 #include "kmcommands.h"
59 #include "annotationjobs.h"
60 #include "quotajobs.h"
61 #include "groupwareadaptor.h"
62 using namespace KMail
;
64 #include <kio/jobuidelegate.h>
65 #include <kio/global.h>
66 #include <kio/scheduler.h>
68 #include <kmessagebox.h>
72 #include <kconfiggroup.h>
76 #include <QTextStream>
77 #include <QTimerEvent>
78 #include <QVBoxLayout>
81 #include <QButtonGroup>
86 #include <QRadioButton>
90 #define UIDCACHE_VERSION 1
92 static QString
incidencesForToString( KMFolderCachedImap::IncidencesFor r
)
95 case KMFolderCachedImap::IncForNobody
:
97 case KMFolderCachedImap::IncForAdmins
:
99 case KMFolderCachedImap::IncForReaders
:
102 return QString(); // can't happen
105 static KMFolderCachedImap::IncidencesFor
incidencesForFromString( const QString
&str
)
107 if ( str
== "nobody" ) {
108 return KMFolderCachedImap::IncForNobody
;
110 if ( str
== "admins" ) {
111 return KMFolderCachedImap::IncForAdmins
;
113 if ( str
== "readers" ) {
114 return KMFolderCachedImap::IncForReaders
;
116 return KMFolderCachedImap::IncForAdmins
; // by default
119 DImapTroubleShootDialog::DImapTroubleShootDialog( QWidget
*parent
)
120 : KDialog( parent
), rc( None
)
122 setCaption( i18n( "Troubleshooting IMAP Cache" ) );
123 setButtons( Ok
| Cancel
);
124 setDefaultButton( Cancel
);
127 QFrame
*page
= new QFrame( this );
128 setMainWidget( page
);
129 QVBoxLayout
*topLayout
= new QVBoxLayout( page
);
130 topLayout
->setSpacing( 0 );
131 QString txt
= i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>"
132 "<p>If you have problems with synchronizing an IMAP "
133 "folder, you should first try rebuilding the index "
134 "file. This will take some time to rebuild, but will "
135 "not cause any problems.</p><p>If that is not enough, "
136 "you can try refreshing the IMAP cache. If you do this, "
137 "you will loose all your local changes for this folder "
138 "and all its subfolders.</p>" );
139 QLabel
*label
= new QLabel(txt
, page
);
140 label
->setWordWrap(true);
141 topLayout
->addWidget( label
);
142 showButtonSeparator( true );
144 QButtonGroup
*group
= new QButtonGroup( 0 );
146 mIndexButton
= new QRadioButton( page
);
147 mIndexButton
->setText( i18n( "Rebuild &Index" ) );
148 group
->addButton( mIndexButton
);
149 topLayout
->addWidget( mIndexButton
);
151 KHBox
*hbox
= new KHBox( page
);
152 QLabel
*scopeLabel
= new QLabel( i18n( "Scope:" ), hbox
);
153 scopeLabel
->setEnabled( false );
154 mIndexScope
= new QComboBox( hbox
);
155 mIndexScope
->addItem( i18n( "Only current folder" ) );
156 mIndexScope
->addItem( i18n( "Current folder and all subfolders" ) );
157 mIndexScope
->addItem( i18n( "All folders of this account" ) );
158 mIndexScope
->setEnabled( false );
159 topLayout
->addWidget( hbox
);
161 mCacheButton
= new QRadioButton( page
);
162 mCacheButton
->setText( i18n( "Refresh &Cache" ) );
163 group
->addButton( mCacheButton
);
164 topLayout
->addWidget( mCacheButton
);
166 connect ( mIndexButton
, SIGNAL( toggled( bool ) ),
167 mIndexScope
, SLOT( setEnabled( bool ) ) );
168 connect ( mIndexButton
, SIGNAL( toggled( bool ) ),
169 scopeLabel
, SLOT( setEnabled( bool ) ) );
171 connect( this, SIGNAL( okClicked () ), this, SLOT( slotDone() ) );
174 int DImapTroubleShootDialog::run()
176 DImapTroubleShootDialog d
;
181 void DImapTroubleShootDialog::slotDone()
184 if ( mIndexButton
->isChecked() ) {
185 rc
= mIndexScope
->currentIndex();
186 } else if ( mCacheButton
->isChecked() ) {
192 KMFolderCachedImap::KMFolderCachedImap( KMFolder
*folder
, const char *aName
)
193 : KMFolderMaildir( folder
, aName
),
194 mSyncState( SYNC_STATE_INITIAL
), mContentState( imapNoInformation
),
195 mSubfolderState( imapNoInformation
),
196 mIncidencesFor( IncForAdmins
),
197 mIsSelected( false ),
198 mCheckFlags( true ), mReadOnly( false ), mAccount( 0 ), uidMapDirty( true ),
199 uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ),
200 mUserRights( 0 ), mOldUserRights( 0 ), mSilentUpload( false ),
201 /*mHoldSyncs( false ),*/
202 mFolderRemoved( false ),
204 mStatusChangedLocally( false ), mAnnotationFolderTypeChanged( false ),
205 mIncidencesForChanged( false ), mPersonalNamespacesCheckDone( true ),
206 mQuotaInfo(), mAlarmsBlocked( false ),
207 mRescueCommandCount( 0 )
209 setUidValidity( "" );
215 KMFolderCachedImap::~KMFolderCachedImap()
217 if ( !mFolderRemoved
) {
222 if ( kmkernel
->undoStack() ) {
223 kmkernel
->undoStack()->folderDestroyed( folder() );
227 void KMFolderCachedImap::initializeFrom( KMFolderCachedImap
*parent
)
229 setAccount( parent
->account() );
232 * Now that we have an account, tell it that this folder was created:
233 * if this folder was just removed, then we don't really want to remove
234 * it from the server.
236 mAccount
->removeDeletedFolder( imapPath() );
237 setUserRights( parent
->userRights() );
240 void KMFolderCachedImap::readConfig()
242 KConfig
*config
= KMKernel::config();
243 KConfigGroup
group( config
, "Folder-" + folder()->idString() );
244 if ( mImapPath
.isEmpty() ) {
245 mImapPath
= group
.readEntry( "ImapPath" );
247 if ( QString( objectName() ).toUpper() == "INBOX" && mImapPath
== "/INBOX/" ) {
248 folder()->setLabel( i18n( "inbox" ) );
250 folder()->setSystemFolder( true );
252 mNoContent
= group
.readEntry( "NoContent", false );
253 mReadOnly
= group
.readEntry( "ReadOnly", false );
255 if ( mAnnotationFolderType
!= "FROMSERVER" ) {
256 mAnnotationFolderType
= group
.readEntry( "Annotation-FolderType" );
257 // if there is an annotation, it has to be XML
258 if ( !mAnnotationFolderType
.isEmpty() &&
259 !mAnnotationFolderType
.startsWith( "mail" ) ) {
260 kmkernel
->iCalIface().setStorageFormat( folder(), StorageXML
);
263 mIncidencesFor
= incidencesForFromString( group
.readEntry( "IncidencesFor" ) );
264 mAlarmsBlocked
= group
.readEntry( "AlarmsBlocked", false );
265 // kDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
266 // << " readConfig: mIncidencesFor=" << mIncidencesFor << endl;
268 mUserRights
= group
.readEntry( "UserRights", 0 ); // default is we don't know
269 mOldUserRights
= mUserRights
;
271 int storageQuotaUsage
= group
.readEntry( "StorageQuotaUsage", -1 );
272 int storageQuotaLimit
= group
.readEntry( "StorageQuotaLimit", -1 );
273 QString storageQuotaRoot
= group
.readEntry( "StorageQuotaRoot", QString() );
274 if ( !storageQuotaRoot
.isNull() ) { // isEmpty() means we know there is no quota set
275 mQuotaInfo
.setName( "STORAGE" );
276 mQuotaInfo
.setRoot( storageQuotaRoot
);
278 if ( storageQuotaUsage
> -1 ) {
279 mQuotaInfo
.setCurrent( storageQuotaUsage
);
281 if ( storageQuotaLimit
> -1 ) {
282 mQuotaInfo
.setMax( storageQuotaLimit
);
286 KMFolderMaildir::readConfig();
288 mStatusChangedLocally
= group
.readEntry( "StatusChangedLocally", false );
289 mAnnotationFolderTypeChanged
= group
.readEntry( "AnnotationFolderTypeChanged", false );
290 mIncidencesForChanged
= group
.readEntry( "IncidencesForChanged", false );
291 if ( mImapPath
.isEmpty() ) {
292 mImapPathCreation
= group
.readEntry("ImapPathCreation");
296 void KMFolderCachedImap::writeConfig()
298 KConfigGroup
configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
299 configGroup
.writeEntry( "ImapPath", mImapPath
);
300 configGroup
.writeEntry( "NoContent", mNoContent
);
301 configGroup
.writeEntry( "ReadOnly", mReadOnly
);
302 configGroup
.writeEntry( "StatusChangedLocally", mStatusChangedLocally
);
303 if ( !mImapPathCreation
.isEmpty() ) {
304 if ( mImapPath
.isEmpty() ) {
305 configGroup
.writeEntry( "ImapPathCreation", mImapPathCreation
);
307 configGroup
.deleteEntry( "ImapPathCreation" );
310 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
311 KMFolderMaildir::writeConfig();
314 void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig()
316 KConfigGroup
configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
317 if ( !folder()->noContent() ) {
318 configGroup
.writeEntry( "AnnotationFolderTypeChanged", mAnnotationFolderTypeChanged
);
319 configGroup
.writeEntry( "Annotation-FolderType", mAnnotationFolderType
);
320 configGroup
.writeEntry( "IncidencesForChanged", mIncidencesForChanged
);
321 configGroup
.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor
) );
322 configGroup
.writeEntry( "AlarmsBlocked", mAlarmsBlocked
);
323 configGroup
.writeEntry( "UserRights", mUserRights
);
325 if ( mQuotaInfo
.isValid() ) {
326 if ( mQuotaInfo
.current().isValid() ) {
327 configGroup
.writeEntry( "StorageQuotaUsage", mQuotaInfo
.current().toInt() );
329 if ( mQuotaInfo
.max().isValid() ) {
330 configGroup
.writeEntry( "StorageQuotaLimit", mQuotaInfo
.max().toInt() );
332 configGroup
.writeEntry( "StorageQuotaRoot", mQuotaInfo
.root() );
334 configGroup
.deleteEntry( "StorageQuotaUsage");
335 configGroup
.deleteEntry( "StorageQuotaRoot");
336 configGroup
.deleteEntry( "StorageQuotaLimit");
341 int KMFolderCachedImap::create()
343 int rc
= KMFolderMaildir::create();
344 // FIXME why the below? - till
350 void KMFolderCachedImap::remove()
352 mFolderRemoved
= true;
354 QString part1
= folder()->path() + "/." + dotEscape( objectName() );
355 QString uidCacheFile
= part1
+ ".uidcache";
356 // This is the account folder of an account that was just removed
357 // When this happens, be sure to delete all traces of the cache
358 if ( QFile::exists( uidCacheFile
) ) {
359 unlink( QFile::encodeName( uidCacheFile
) );
362 FolderStorage::remove();
365 QString
KMFolderCachedImap::uidCacheLocation() const
367 QString
sLocation( folder()->path() );
368 if ( !sLocation
.isEmpty() ) {
371 return sLocation
+ '.' + dotEscape(fileName()) + ".uidcache";
374 int KMFolderCachedImap::readUidCache()
376 QFile
uidcache( uidCacheLocation() );
377 if ( uidcache
.open( QIODevice::ReadOnly
) ) {
379 int len
= uidcache
.readLine( buf
, sizeof( buf
) );
382 sscanf( buf
, "# KMail-UidCache V%d\n", &cacheVersion
);
383 if ( cacheVersion
== UIDCACHE_VERSION
) {
384 len
= uidcache
.readLine( buf
, sizeof( buf
) );
386 setUidValidity( QString::fromLocal8Bit( buf
).trimmed() );
387 len
= uidcache
.readLine( buf
, sizeof( buf
) );
389 // load the last known highest uid from the on disk cache
390 setLastUid( QString::fromLocal8Bit( buf
).trimmed().toULong() );
400 int KMFolderCachedImap::writeUidCache()
402 if ( uidValidity().isEmpty() || uidValidity() == "INVALID" ) {
403 // No info from the server yet, remove the file.
404 if ( QFile::exists( uidCacheLocation() ) ) {
405 unlink( QFile::encodeName( uidCacheLocation() ) );
410 QFile
uidcache( uidCacheLocation() );
411 if ( uidcache
.open( QIODevice::WriteOnly
) ) {
412 QTextStream
str( &uidcache
);
413 str
<< "# KMail-UidCache V" << UIDCACHE_VERSION
<< endl
;
414 str
<< uidValidity() << endl
;
415 str
<< lastUid() << endl
;
417 fsync( uidcache
.handle() ); /* this is probably overkill */
421 return errno
; /* does QFile set errno? */
425 void KMFolderCachedImap::reloadUidMap()
427 kDebug(5006) << "Reloading Uid Map " << endl
;
430 for ( int i
= 0; i
< count(); ++i
) {
431 KMMsgBase
*msg
= getMsgBase( i
);
435 ulong uid
= msg
->UID();
436 uidMap
.insert( uid
, i
);
438 close( "reloadUid" );
442 KMMessage
*KMFolderCachedImap::take( int idx
)
445 return KMFolderMaildir::take( idx
);
448 int KMFolderCachedImap::addMsgInternal( KMMessage
*msg
, bool newMail
, int *index_return
)
450 // Possible optimization: Only dirty if not filtered below
451 ulong uid
= msg
->UID();
457 int rc
= KMFolderMaildir::addMsg( msg
, index_return
);
459 if ( newMail
&& imapPath() == "/INBOX/" ) {
460 // This is a new message. Filter it
461 mAccount
->processNewMsg( msg
);
467 int KMFolderCachedImap::addMsg( KMMessage
*msg
, int *index_return
)
469 if ( !canAddMsgNow( msg
, index_return
) ) {
474 int rc
= KMFolderMaildir::addMsgInternal( msg
, index_return
, true /*stripUID*/);
478 void KMFolderCachedImap::removeMsg( int idx
, bool imapQuiet
)
481 // Remove it from disk
482 KMFolderMaildir::removeMsg( idx
, imapQuiet
);
485 bool KMFolderCachedImap::canRemoveFolder() const
487 // If this has subfolders it can't be removed
488 if ( folder() && folder()->child() && folder()->child()->count() > 0 ) {
494 int KMFolderCachedImap::rename( const QString
&aName
, KMFolderDir
*aParent
)
498 QString oldName
= mAccount
->renamedFolder( imapPath() );
499 if ( oldName
.isEmpty() ) {
500 oldName
= objectName();
503 if ( aName
== oldName
) {
504 // Stupid user trying to rename it to it's old name :)
508 if ( account() == 0 || imapPath().isEmpty() ) {
509 // We don't think any of this can happen anymore
510 QString err
= i18n("You must synchronize with the server before renaming IMAP folders.");
511 KMessageBox::error( 0, err
);
516 * Make the change appear to the user with setLabel, but we'll do the change
517 * on the server during the next sync. The name() is the name at the time of
518 * the last sync. Only rename if the new one is different. If it's the same,
519 * don't rename, but also make sure the rename is reset, in the case of
520 * A -> B -> A renames.
522 if ( objectName() != aName
) {
523 mAccount
->addRenamedFolder( imapPath(), folder()->label(), aName
);
525 mAccount
->removeRenamedFolder( imapPath() );
528 folder()->setLabel( aName
);
529 emit
nameChanged(); // for kmailicalifaceimpl
534 KMFolder
*KMFolderCachedImap::trashFolder() const
536 QString trashStr
= account()->trash();
537 return kmkernel
->dimapFolderMgr()->findIdString( trashStr
);
540 void KMFolderCachedImap::setLastUid( ulong uid
)
543 if ( uidWriteTimer
== -1 ) {
544 // Write in one minute
545 uidWriteTimer
= startTimer( 60000 );
549 void KMFolderCachedImap::timerEvent( QTimerEvent
*e
)
553 killTimer( uidWriteTimer
);
558 ulong
KMFolderCachedImap::lastUid()
563 KMMsgBase
*KMFolderCachedImap::findByUID( ulong uid
)
565 bool mapReloaded
= false;
571 QMap
<ulong
,int>::Iterator it
= uidMap
.find( uid
);
572 if ( it
!= uidMap
.end() ) {
573 KMMsgBase
*msg
= getMsgBase( *it
);
574 if ( msg
&& msg
->UID() == uid
) {
578 kDebug(5006) << "Didn't find uid: " << uid
<< "in cache!" << endl
;
581 // if ( mapReloaded )
585 // There could be a problem in the maps. Rebuild them and try again
587 it
= uidMap
.find( uid
);
588 if ( it
!= uidMap
.end() ) {
589 // Since the uid map is just rebuilt, no need for the sanity check
590 return getMsgBase( *it
);
592 kDebug(5006) << "Reloaded, but stil didn't find uid: " << uid
<< endl
;
595 // Then it's not here
599 KMAcctCachedImap
*KMFolderCachedImap::account() const
601 if ( (KMAcctCachedImap
*)mAccount
== 0 ) {
604 static_cast<KMAcctCachedImap
*>( kmkernel
->acctMgr()->findByName( objectName() ) );
610 void KMFolderCachedImap::slotTroubleshoot()
612 const int rc
= DImapTroubleShootDialog::run();
614 if ( rc
== DImapTroubleShootDialog::RefreshCache
) {
617 KMessageBox::sorry( 0, i18n("No account setup for this folder.\n"
618 "Please try running a sync before this.") );
621 QString str
= i18n("Are you sure you want to refresh the IMAP cache of "
622 "the folder %1 and all its subfolders?\nThis will "
623 "remove all changes you have done locally to your "
624 "folders.", label() );
625 QString s1
= i18n("Refresh IMAP Cache");
626 QString s2
= i18n("&Refresh");
627 if ( KMessageBox::warningContinueCancel( 0, str
, s1
, KGuiItem( s2
) ) ==
628 KMessageBox::Continue
) {
629 account()->invalidateIMAPFolders( this );
632 // Rebuild index file
634 case DImapTroubleShootDialog::ReindexAll
:
636 KMFolderCachedImap
*rootStorage
=
637 dynamic_cast<KMFolderCachedImap
*>( account()->rootFolder() );
639 rootStorage
->createIndexFromContentsRecursive();
643 case DImapTroubleShootDialog::ReindexCurrent
:
644 createIndexFromContents();
646 case DImapTroubleShootDialog::ReindexRecursive
:
647 createIndexFromContentsRecursive();
652 KMessageBox::information( 0, i18n( "The index of this folder has been "
657 void KMFolderCachedImap::serverSync( bool recurse
)
659 if ( mSyncState
!= SYNC_STATE_INITIAL
) {
660 if ( KMessageBox::warningYesNo(
662 i18n("Folder %1 is not in initial sync state (state was %2). "
663 "Do you want to reset it to initial sync state and sync anyway?",
664 imapPath(), int( mSyncState
) ),
665 QString(), KGuiItem( i18n("Reset && Sync") ),
666 KStandardGuiItem::cancel() ) == KMessageBox::Yes
) {
667 mSyncState
= SYNC_STATE_INITIAL
;
676 ProgressItem
*progressItem
= mAccount
->mailCheckProgressItem();
677 if ( progressItem
) {
678 progressItem
->reset();
679 progressItem
->setTotalItems( 100 );
683 mTentativeHighestUid
= 0; // reset, last sync could have been canceled
684 serverSyncInternal();
687 QString
KMFolderCachedImap::state2String( int state
) const
690 case SYNC_STATE_INITIAL
: return "SYNC_STATE_INITIAL";
691 case SYNC_STATE_GET_USERRIGHTS
: return "SYNC_STATE_GET_USERRIGHTS";
692 case SYNC_STATE_PUT_MESSAGES
: return "SYNC_STATE_PUT_MESSAGES";
693 case SYNC_STATE_UPLOAD_FLAGS
: return "SYNC_STATE_UPLOAD_FLAGS";
694 case SYNC_STATE_CREATE_SUBFOLDERS
: return "SYNC_STATE_CREATE_SUBFOLDERS";
695 case SYNC_STATE_LIST_SUBFOLDERS
: return "SYNC_STATE_LIST_SUBFOLDERS";
696 case SYNC_STATE_LIST_NAMESPACES
: return "SYNC_STATE_LIST_NAMESPACES";
697 case SYNC_STATE_LIST_SUBFOLDERS2
: return "SYNC_STATE_LIST_SUBFOLDERS2";
698 case SYNC_STATE_DELETE_SUBFOLDERS
: return "SYNC_STATE_DELETE_SUBFOLDERS";
699 case SYNC_STATE_LIST_MESSAGES
: return "SYNC_STATE_LIST_MESSAGES";
700 case SYNC_STATE_DELETE_MESSAGES
: return "SYNC_STATE_DELETE_MESSAGES";
701 case SYNC_STATE_GET_MESSAGES
: return "SYNC_STATE_GET_MESSAGES";
702 case SYNC_STATE_EXPUNGE_MESSAGES
: return "SYNC_STATE_EXPUNGE_MESSAGES";
703 case SYNC_STATE_HANDLE_INBOX
: return "SYNC_STATE_HANDLE_INBOX";
704 case SYNC_STATE_TEST_ANNOTATIONS
: return "SYNC_STATE_TEST_ANNOTATIONS";
705 case SYNC_STATE_GET_ANNOTATIONS
: return "SYNC_STATE_GET_ANNOTATIONS";
706 case SYNC_STATE_SET_ANNOTATIONS
: return "SYNC_STATE_SET_ANNOTATIONS";
707 case SYNC_STATE_GET_ACLS
: return "SYNC_STATE_GET_ACLS";
708 case SYNC_STATE_SET_ACLS
: return "SYNC_STATE_SET_ACLS";
709 case SYNC_STATE_GET_QUOTA
: return "SYNC_STATE_GET_QUOTA";
710 case SYNC_STATE_FIND_SUBFOLDERS
: return "SYNC_STATE_FIND_SUBFOLDERS";
711 case SYNC_STATE_SYNC_SUBFOLDERS
: return "SYNC_STATE_SYNC_SUBFOLDERS";
712 case SYNC_STATE_RENAME_FOLDER
: return "SYNC_STATE_RENAME_FOLDER";
713 case SYNC_STATE_CHECK_UIDVALIDITY
: return "SYNC_STATE_CHECK_UIDVALIDITY";
714 default: return "Unknown state";
719 Progress calculation: each step is assigned a span. Initially the total is 100.
720 But if we skip a step, don't increase the progress.
721 This leaves more room for the step a with variable size (get_messages)
727 put_messages 10 (but it can take a very long time, with many messages....)
730 list_subfolders2 0 (all local)
735 get_messages variable (remaining-5) i.e. minimum 15.
736 check_annotations 0 (rare)
737 set_annotations 0 (rare)
742 noContent folders have only a few of the above steps
743 (permissions, and all subfolder stuff), so its steps should be given more span
747 // While the server synchronization is running, mSyncState will hold
748 // the state that should be executed next
749 void KMFolderCachedImap::serverSyncInternal()
751 // This is used to stop processing when we're about to exit
752 // and the current job wasn't cancellable.
753 // For user-requested abort, we'll use signalAbortRequested instead.
754 if ( kmkernel
->mailCheckAborted() ) {
756 emit
folderComplete( this, false );
760 switch( mSyncState
) {
761 case SYNC_STATE_INITIAL
:
764 foldersForDeletionOnServer
.clear();
765 newState( mProgress
, i18n("Synchronizing"));
767 open( "cachedimap" );
768 if ( !noContent() ) {
769 mAccount
->addLastUnreadMsgCount( this, countUnread() );
772 // Connect to the server (i.e. prepare the slave)
773 ImapAccountBase::ConnectionState cs
= mAccount
->makeConnection();
774 if ( cs
== ImapAccountBase::Error
) {
775 // Cancelled by user, or slave can't start
776 // We stop here. We're already in SYNC_STATE_INITIAL for the next time.
777 newState( mProgress
, i18n( "Error connecting to server %1", mAccount
->host() ) );
778 close( "cachedimap" );
779 emit
folderComplete( this, false );
781 } else if ( cs
== ImapAccountBase::Connecting
) {
782 mAccount
->setAnnotationCheckPassed( false );
783 newState( mProgress
, i18n("Connecting to %1", mAccount
->host() ) );
785 // We'll wait for the connectionResult signal from the account.
786 connect( mAccount
, SIGNAL( connectionResult(int, const QString
&) ),
787 this, SLOT( slotConnectionResult(int, const QString
&) ) );
791 mSyncState
= SYNC_STATE_GET_USERRIGHTS
;
792 // Fall through to next state
796 case SYNC_STATE_GET_USERRIGHTS
:
797 mSyncState
= SYNC_STATE_RENAME_FOLDER
;
799 if ( !noContent() && mAccount
->hasACLSupport() ) {
800 // Check the user's rights. We do this every time in case they changed.
801 mOldUserRights
= mUserRights
;
802 newState( mProgress
, i18n("Checking permissions"));
803 connect( mAccount
, SIGNAL( receivedUserRights( KMFolder
* ) ),
804 this, SLOT( slotReceivedUserRights( KMFolder
* ) ) );
805 mAccount
->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case
809 case SYNC_STATE_RENAME_FOLDER
:
811 mSyncState
= SYNC_STATE_CHECK_UIDVALIDITY
;
812 // Returns the new name if the folder was renamed, empty otherwise.
813 bool isResourceFolder
= kmkernel
->iCalIface().isStandardResourceFolder( folder() );
814 QString newName
= mAccount
->renamedFolder( imapPath() );
815 if ( !newName
.isEmpty() && !folder()->isSystemFolder() && !isResourceFolder
) {
816 newState( mProgress
, i18n("Renaming folder") );
817 CachedImapJob
*job
= new CachedImapJob( newName
, CachedImapJob::tRenameFolder
, this );
818 connect( job
, SIGNAL( result(KMail::FolderJob
*) ), this, SLOT( slotIncreaseProgress() ) );
819 connect( job
, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
825 case SYNC_STATE_CHECK_UIDVALIDITY
:
826 mSyncState
= SYNC_STATE_CREATE_SUBFOLDERS
;
827 if ( !noContent() ) {
833 case SYNC_STATE_CREATE_SUBFOLDERS
:
834 mSyncState
= SYNC_STATE_PUT_MESSAGES
;
838 case SYNC_STATE_PUT_MESSAGES
:
839 mSyncState
= SYNC_STATE_UPLOAD_FLAGS
;
840 if ( !noContent() ) {
845 case SYNC_STATE_UPLOAD_FLAGS
:
846 mSyncState
= SYNC_STATE_LIST_NAMESPACES
;
847 if ( !noContent() ) {
848 // We haven't downloaded messages yet, so we need to build the map.
853 // Upload flags, unless we know from the ACL that we're not allowed
854 // to do that or they did not change locally
855 if ( mUserRights
<= 0 || ( mUserRights
& KMail::ACLJobs::WriteFlags
) ) {
856 if ( mStatusChangedLocally
) {
864 case SYNC_STATE_LIST_NAMESPACES
:
865 if ( this == mAccount
->rootFolder() ) {
869 mSyncState
= SYNC_STATE_LIST_SUBFOLDERS
;
872 case SYNC_STATE_LIST_SUBFOLDERS
:
873 newState( mProgress
, i18n("Retrieving folderlist"));
874 mSyncState
= SYNC_STATE_LIST_SUBFOLDERS2
;
875 if ( !listDirectory() ) {
876 mSyncState
= SYNC_STATE_INITIAL
;
877 KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
881 case SYNC_STATE_LIST_SUBFOLDERS2
:
882 mSyncState
= SYNC_STATE_DELETE_SUBFOLDERS
;
884 newState( mProgress
, i18n("Retrieving subfolders"));
888 case SYNC_STATE_DELETE_SUBFOLDERS
:
889 mSyncState
= SYNC_STATE_LIST_MESSAGES
;
890 if ( !foldersForDeletionOnServer
.isEmpty() ) {
891 newState( mProgress
, i18n("Deleting folders from server"));
892 CachedImapJob
*job
= new CachedImapJob( foldersForDeletionOnServer
,
893 CachedImapJob::tDeleteFolders
, this );
894 connect( job
, SIGNAL( result(KMail::FolderJob
*) ), this, SLOT( slotIncreaseProgress() ) );
895 connect( job
, SIGNAL( finished() ), this, SLOT( slotFolderDeletionOnServerFinished() ) );
899 // Not needed, the next step emits newState very quick
900 //newState( mProgress, i18n("No folders to delete from server"));
903 case SYNC_STATE_LIST_MESSAGES
:
904 mSyncState
= SYNC_STATE_DELETE_MESSAGES
;
905 if ( !noContent() ) {
906 newState( mProgress
, i18n("Retrieving message list"));
912 case SYNC_STATE_DELETE_MESSAGES
:
913 mSyncState
= SYNC_STATE_EXPUNGE_MESSAGES
;
914 if ( !noContent() ) {
915 if ( deleteMessages() ) {
916 // Fine, we will continue with the next state
918 // No messages to delete, skip to GET_MESSAGES
919 newState( mProgress
, i18n("No messages to delete..."));
920 mSyncState
= SYNC_STATE_GET_MESSAGES
;
921 serverSyncInternal();
927 case SYNC_STATE_EXPUNGE_MESSAGES
:
928 mSyncState
= SYNC_STATE_GET_MESSAGES
;
929 if ( !noContent() ) {
930 newState( mProgress
, i18n("Expunging deleted messages"));
931 CachedImapJob
*job
= new CachedImapJob( QString(),
932 CachedImapJob::tExpungeFolder
, this );
933 connect( job
, SIGNAL( result(KMail::FolderJob
*) ), this, SLOT( slotIncreaseProgress() ) );
934 connect( job
, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
940 case SYNC_STATE_GET_MESSAGES
:
941 mSyncState
= SYNC_STATE_HANDLE_INBOX
;
942 if ( !noContent() ) {
943 if ( !mMsgsForDownload
.isEmpty() ) {
944 newState( mProgress
, i18n("Retrieving new messages"));
945 CachedImapJob
*job
= new CachedImapJob( mMsgsForDownload
,
946 CachedImapJob::tGetMessage
,
948 connect( job
, SIGNAL( progress( unsigned long, unsigned long ) ),
949 this, SLOT( slotProgress( unsigned long, unsigned long ) ) );
950 connect( job
, SIGNAL( finished() ), this, SLOT( slotUpdateLastUid() ) );
951 connect( job
, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
953 mMsgsForDownload
.clear();
956 newState( mProgress
, i18n("No new messages from server"));
958 * There were no messages to download, but it could be that we
959 * uploaded some which we didn't need to download again because we
960 * already knew the uid. Now that we are sure there is nothing to
961 * download, and everything that had to be deleted on the server
962 * has been deleted, adjust our local notion of the highest uid
967 if ( mLastUid
== 0 && uidWriteTimer
== -1 ) {
968 // This is probably a new and empty folder. Write the UID cache
976 case SYNC_STATE_HANDLE_INBOX
:
977 // Wrap up the 'download emails' stage. We always end up at 95 here.
979 mSyncState
= SYNC_STATE_TEST_ANNOTATIONS
;
981 #define KOLAB_FOLDERTEST "/vendor/kolab/folder-test"
982 case SYNC_STATE_TEST_ANNOTATIONS
:
983 mSyncState
= SYNC_STATE_GET_ANNOTATIONS
;
984 // The first folder with user rights to write annotations
985 if ( !mAccount
->annotationCheckPassed() &&
986 ( mUserRights
<= 0 || ( mUserRights
& ACLJobs::Administer
) ) &&
987 !imapPath().isEmpty() && imapPath() != "/" ) {
988 kDebug(5006) << "Setting test attribute on folder: "
989 << folder()->prettyUrl() << endl
;
990 newState( mProgress
, i18n("Checking annotation support"));
992 KUrl url
= mAccount
->getUrl();
993 url
.setPath( imapPath() );
994 KMail::AnnotationList annotations
; // to be set
996 KMail::AnnotationAttribute
attr( KOLAB_FOLDERTEST
, "value.shared", "true" );
997 annotations
.append( attr
);
999 kDebug(5006) << "Setting test attribute to "<< url
<< endl
;
1000 KIO::Job
*job
= AnnotationJobs::multiSetAnnotation( mAccount
->slave(),
1002 ImapAccountBase::jobData
jd( url
.url(), folder() );
1003 jd
.cancellable
= true; // we can always do so later
1004 mAccount
->insertJob( job
, jd
);
1005 connect( job
, SIGNAL( result( KJob
* ) ),
1006 SLOT( slotTestAnnotationResult( KJob
* ) ) );
1010 case SYNC_STATE_GET_ANNOTATIONS
:
1012 #define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type"
1013 #define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for"
1014 //#define KOLAB_FOLDERTYPE "/comment" //for testing, while cyrus-imap doesn't support /vendor/*
1015 mSyncState
= SYNC_STATE_SET_ANNOTATIONS
;
1017 bool needToGetInitialAnnotations
= false;
1018 if ( !noContent() ) {
1019 // for a folder we didn't create ourselves: get annotation from server
1020 if ( mAnnotationFolderType
== "FROMSERVER" ) {
1021 needToGetInitialAnnotations
= true;
1022 mAnnotationFolderType
.clear();
1024 updateAnnotationFolderType();
1029 * First retrieve the annotation, so that we know we have to set it
1030 * if it's not set. On the other hand, if the user changed the
1031 * contentstype, there's no need to get first.
1033 if ( !noContent() && mAccount
->hasAnnotationSupport() &&
1034 ( kmkernel
->iCalIface().isEnabled() || needToGetInitialAnnotations
) ) {
1035 QStringList annotations
; // list of annotations to be fetched
1036 if ( !mAnnotationFolderTypeChanged
|| mAnnotationFolderType
.isEmpty() )
1037 annotations
<< KOLAB_FOLDERTYPE
;
1038 if ( !mIncidencesForChanged
)
1039 annotations
<< KOLAB_INCIDENCESFOR
;
1040 if ( !annotations
.isEmpty() ) {
1041 newState( mProgress
, i18n("Retrieving annotations"));
1042 KUrl url
= mAccount
->getUrl();
1043 url
.setPath( imapPath() );
1044 AnnotationJobs::MultiGetAnnotationJob
*job
=
1045 AnnotationJobs::multiGetAnnotation( mAccount
->slave(), url
, annotations
);
1046 ImapAccountBase::jobData
jd( url
.url(), folder() );
1047 jd
.cancellable
= true;
1048 mAccount
->insertJob(job
, jd
);
1050 connect( job
, SIGNAL(annotationResult(const QString
&, const QString
&, bool)),
1051 SLOT(slotAnnotationResult(const QString
&, const QString
&, bool)) );
1052 connect( job
, SIGNAL(result(KJob
*)),
1053 SLOT(slotGetAnnotationResult(KJob
*)) );
1058 case SYNC_STATE_SET_ANNOTATIONS
:
1060 mSyncState
= SYNC_STATE_SET_ACLS
;
1061 if ( !noContent() && mAccount
->hasAnnotationSupport() &&
1062 ( mUserRights
<= 0 || ( mUserRights
& ACLJobs::Administer
) ) ) {
1063 newState( mProgress
, i18n("Setting annotations"));
1064 KUrl url
= mAccount
->getUrl();
1065 url
.setPath( imapPath() );
1066 KMail::AnnotationList annotations
; // to be set
1067 if ( mAnnotationFolderTypeChanged
&& !mAnnotationFolderType
.isEmpty() ) {
1068 KMail::AnnotationAttribute
attr( KOLAB_FOLDERTYPE
, "value.shared",
1069 mAnnotationFolderType
);
1070 annotations
.append( attr
);
1071 kDebug(5006) << "Setting folder-type annotation for " << label()
1072 << " to " << mAnnotationFolderType
<< endl
;
1074 if ( mIncidencesForChanged
) {
1075 const QString val
= incidencesForToString( mIncidencesFor
);
1076 KMail::AnnotationAttribute
attr( KOLAB_INCIDENCESFOR
, "value.shared",
1078 annotations
.append( attr
);
1079 kDebug(5006) << "Setting incidences-for annotation for " << label()
1080 << " to " << val
<< endl
;
1082 if ( !annotations
.isEmpty() ) {
1084 AnnotationJobs::multiSetAnnotation( mAccount
->slave(), url
,
1086 ImapAccountBase::jobData
jd( url
.url(), folder() );
1087 jd
.cancellable
= true; // we can always do so later
1088 mAccount
->insertJob(job
, jd
);
1090 connect(job
, SIGNAL(annotationChanged( const QString
&, const QString
&, const QString
& ) ),
1091 SLOT( slotAnnotationChanged( const QString
&, const QString
&, const QString
& ) ));
1092 connect(job
, SIGNAL(result(KJob
*)),
1093 SLOT(slotSetAnnotationResult(KJob
*)));
1098 case SYNC_STATE_SET_ACLS
:
1099 mSyncState
= SYNC_STATE_GET_ACLS
;
1101 if ( !noContent() && mAccount
->hasACLSupport() &&
1102 ( mUserRights
<= 0 || ( mUserRights
& ACLJobs::Administer
) ) ) {
1103 bool hasChangedACLs
= false;
1104 ACLList::ConstIterator it
= mACLList
.begin();
1105 for ( ; it
!= mACLList
.end() && !hasChangedACLs
; ++it
) {
1106 hasChangedACLs
= (*it
).changed
;
1108 if ( hasChangedACLs
) {
1109 newState( mProgress
, i18n("Setting permissions"));
1110 KUrl url
= mAccount
->getUrl();
1111 url
.setPath( imapPath() );
1112 KIO::Job
*job
= KMail::ACLJobs::multiSetACL( mAccount
->slave(), url
,
1114 ImapAccountBase::jobData
jd( url
.url(), folder() );
1115 mAccount
->insertJob(job
, jd
);
1117 connect(job
, SIGNAL(result(KJob
*)),
1118 SLOT(slotMultiSetACLResult(KJob
*)));
1119 connect(job
, SIGNAL(aclChanged( const QString
&, int )),
1120 SLOT(slotACLChanged( const QString
&, int )) );
1125 case SYNC_STATE_GET_ACLS
:
1126 mSyncState
= SYNC_STATE_GET_QUOTA
;
1128 if ( !noContent() && mAccount
->hasACLSupport() ) {
1129 newState( mProgress
, i18n( "Retrieving permissions" ) );
1130 mAccount
->getACL( folder(), mImapPath
);
1131 connect( mAccount
, SIGNAL(receivedACL( KMFolder
*, KIO::Job
*, const KMail::ACLList
& )),
1132 this, SLOT(slotReceivedACL( KMFolder
*, KIO::Job
*, const KMail::ACLList
& )) );
1135 case SYNC_STATE_GET_QUOTA
:
1136 // Continue with the subfolders
1137 mSyncState
= SYNC_STATE_FIND_SUBFOLDERS
;
1138 if ( !noContent() && mAccount
->hasQuotaSupport() ) {
1139 newState( mProgress
, i18n("Getting quota information"));
1140 KUrl url
= mAccount
->getUrl();
1141 url
.setPath( imapPath() );
1142 KIO::Job
*job
= KMail::QuotaJobs::getStorageQuota( mAccount
->slave(), url
);
1143 ImapAccountBase::jobData
jd( url
.url(), folder() );
1144 mAccount
->insertJob(job
, jd
);
1145 connect( job
, SIGNAL( storageQuotaResult( const QuotaInfo
& ) ),
1146 SLOT( slotStorageQuotaResult( const QuotaInfo
& ) ) );
1147 connect( job
, SIGNAL(result(KIO::Job
*)),
1148 SLOT(slotQuotaResult(KIO::Job
*)) );
1151 case SYNC_STATE_FIND_SUBFOLDERS
:
1154 newState( mProgress
, i18n("Updating cache file"));
1156 mSyncState
= SYNC_STATE_SYNC_SUBFOLDERS
;
1157 mSubfoldersForSync
.clear();
1158 mCurrentSubfolder
= 0;
1159 if ( folder() && folder()->child() ) {
1160 QList
<KMFolderNode
*>::const_iterator it
;
1161 for ( it
= folder()->child()->begin();
1162 it
!= folder()->child()->end(); ++it
)
1164 KMFolderNode
*node
= *it
;
1165 if ( !node
->isDir() ) {
1166 KMFolderCachedImap
*storage
=
1167 static_cast<KMFolderCachedImap
*>( static_cast<KMFolder
*>( node
)->storage() );
1168 // Only sync folders that have been accepted by the server
1169 if ( !storage
->imapPath().isEmpty() &&
1170 // and that were not just deleted from it
1171 !foldersForDeletionOnServer
.contains( storage
->imapPath() ) ) {
1172 mSubfoldersForSync
<< storage
;
1174 kDebug(5006) << "Do not add " << storage
->label()
1175 << " to synclist" << endl
;
1181 // All done for this folder.
1182 mProgress
= 100; // all done
1183 newState( mProgress
, i18n("Synchronization done"));
1184 KUrl url
= mAccount
->getUrl();
1185 url
.setPath( imapPath() );
1186 kmkernel
->iCalIface().folderSynced( folder(), url
);
1189 if ( !mRecurse
) // "check mail for this folder" only
1190 mSubfoldersForSync
.clear();
1193 case SYNC_STATE_SYNC_SUBFOLDERS
:
1195 if ( mCurrentSubfolder
) {
1196 disconnect( mCurrentSubfolder
, SIGNAL( folderComplete(KMFolderCachedImap
*, bool) ),
1197 this, SLOT( slotSubFolderComplete(KMFolderCachedImap
*, bool) ) );
1198 mCurrentSubfolder
= 0;
1201 if ( mSubfoldersForSync
.isEmpty() ) {
1202 mSyncState
= SYNC_STATE_INITIAL
;
1203 mAccount
->addUnreadMsgCount( this, countUnread() ); // before closing
1204 close( "cachedimap" );
1205 emit
folderComplete( this, true );
1207 mCurrentSubfolder
= mSubfoldersForSync
.front();
1208 mSubfoldersForSync
.pop_front();
1209 connect( mCurrentSubfolder
, SIGNAL( folderComplete(KMFolderCachedImap
*, bool) ),
1210 this, SLOT( slotSubFolderComplete(KMFolderCachedImap
*, bool) ) );
1212 assert( !mCurrentSubfolder
->imapPath().isEmpty() );
1213 mCurrentSubfolder
->setAccount( account() );
1214 bool recurse
= mCurrentSubfolder
->noChildren() ? false : true;
1215 mCurrentSubfolder
->serverSync( recurse
);
1221 kDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
1222 << int(mSyncState
) << endl
;
1226 void KMFolderCachedImap::slotConnectionResult( int errorCode
, const QString
&errorMsg
)
1228 disconnect( mAccount
, SIGNAL( connectionResult(int, const QString
&) ),
1229 this, SLOT( slotConnectionResult(int, const QString
&) ) );
1232 mSyncState
= SYNC_STATE_GET_USERRIGHTS
;
1234 serverSyncInternal();
1236 // Error (error message already shown by the account)
1237 newState( mProgress
, KIO::buildErrorString( errorCode
, errorMsg
) );
1238 emit
folderComplete( this, false );
1242 /* find new messages (messages without a UID) */
1243 QList
<unsigned long> KMFolderCachedImap::findNewMessages()
1245 QList
<unsigned long> result
;
1246 for ( int i
= 0; i
< count(); ++i
) {
1247 KMMsgBase
*msg
= getMsgBase( i
);
1248 if ( !msg
) { // what goes on if getMsg() returns 0?
1251 if ( msg
->UID() == 0 ) {
1252 result
.append( msg
->getMsgSerNum() );
1258 /* Upload new messages to server */
1259 void KMFolderCachedImap::uploadNewMessages()
1261 QList
<unsigned long> newMsgs
= findNewMessages();
1262 if ( !newMsgs
.isEmpty() ) {
1263 if ( mUserRights
<= 0 || ( mUserRights
& ( KMail::ACLJobs::Insert
) ) ) {
1264 newState( mProgress
, i18n("Uploading messages to server"));
1265 CachedImapJob
*job
= new CachedImapJob( newMsgs
, CachedImapJob::tPutMessage
, this );
1266 connect( job
, SIGNAL( progress( unsigned long, unsigned long ) ),
1267 this, SLOT( slotPutProgress( unsigned long, unsigned long ) ) );
1268 connect( job
, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
1272 KMCommand
*command
= rescueUnsyncedMessages();
1273 connect( command
, SIGNAL( completed( KMCommand
* ) ),
1274 this, SLOT( serverSyncInternal() ) );
1276 } else { // nothing to upload
1277 if ( mUserRights
!= mOldUserRights
&&
1278 ( mOldUserRights
& KMail::ACLJobs::Insert
) &&
1279 !( mUserRights
& KMail::ACLJobs::Insert
) ) {
1280 // write access revoked
1281 KMessageBox::information(
1283 i18n("<p>Your access rights to folder <b>%1</b> have been restricted, "
1284 "it will no longer be possible to add messages to this folder.</p>",
1285 folder()->prettyUrl() ),
1286 i18n("Acces rights revoked"), "KMailACLRevocationNotification" );
1289 newState( mProgress
, i18n("No messages to upload to server"));
1290 serverSyncInternal();
1293 /* Progress info during uploadNewMessages */
1294 void KMFolderCachedImap::slotPutProgress( unsigned long done
, unsigned long total
)
1296 // (going from mProgress to mProgress+10)
1297 int progressSpan
= 10;
1298 newState( mProgress
+ ( progressSpan
* done
) / total
, QString() );
1299 if ( done
== total
) { // we're done
1300 mProgress
+= progressSpan
;
1304 /* Upload message flags to server */
1305 void KMFolderCachedImap::uploadFlags()
1307 if ( !uidMap
.isEmpty() ) {
1308 mStatusFlagsJobs
= 0;
1309 newState( mProgress
, i18n("Uploading status of messages to server"));
1311 // FIXME DUPLICATED FROM KMFOLDERIMAP
1312 QMap
< QString
, QStringList
> groups
;
1313 //open(); //already done
1314 for ( int i
= 0; i
< count(); ++i
) {
1315 KMMsgBase
*msg
= getMsgBase( i
);
1316 if ( !msg
|| msg
->UID() == 0 ) {
1317 // Either not a valid message or not one that is on the server yet
1321 QString flags
= KMFolderImap::statusToFlags( msg
->status() );
1322 // Collect uids for each typem of flags.
1324 uid
.setNum( msg
->UID() );
1325 groups
[flags
].append( uid
);
1327 QMap
< QString
, QStringList
>::Iterator dit
;
1328 for ( dit
= groups
.begin(); dit
!= groups
.end(); ++dit
) {
1329 QByteArray flags
= dit
.key().toLatin1();
1330 QStringList sets
= KMFolderImap::makeSets( (*dit
), true );
1331 mStatusFlagsJobs
+= sets
.count(); // ### that's not in kmfolderimap....
1332 // Send off a status setting job for each set.
1333 for ( QStringList::Iterator slit
= sets
.begin(); slit
!= sets
.end(); ++slit
) {
1334 QString imappath
= imapPath() + ";UID=" + ( *slit
);
1335 mAccount
->setImapStatus( folder(), imappath
, flags
);
1338 // FIXME END DUPLICATED FROM KMFOLDERIMAP
1340 if ( mStatusFlagsJobs
) {
1341 connect( mAccount
, SIGNAL( imapStatusChanged( KMFolder
*, const QString
&, bool ) ),
1342 this, SLOT( slotImapStatusChanged( KMFolder
*, const QString
&, bool ) ) );
1346 newState( mProgress
, i18n("No messages to upload to server") );
1347 serverSyncInternal();
1350 void KMFolderCachedImap::slotImapStatusChanged( KMFolder
*folder
, const QString
&, bool cont
)
1352 if ( mSyncState
== SYNC_STATE_INITIAL
) {
1353 kDebug(5006) << "IMAP status changed but reset " << endl
;
1354 return; // we were reset
1356 if ( folder
->storage() == this ) {
1358 if ( mStatusFlagsJobs
== 0 || !cont
) { // done or aborting
1359 disconnect( mAccount
, SIGNAL( imapStatusChanged( KMFolder
*, const QString
&, bool ) ),
1360 this, SLOT( slotImapStatusChanged( KMFolder
*, const QString
&, bool ) ) );
1362 if ( mStatusFlagsJobs
== 0 && cont
) {
1364 serverSyncInternal();
1369 // This is not perfect, what if the status didn't really change? Oh well ...
1370 void KMFolderCachedImap::setStatus( int idx
, const MessageStatus
&status
, bool toggle
)
1372 KMFolderMaildir::setStatus( idx
, status
, toggle
);
1373 mStatusChangedLocally
= true;
1376 void KMFolderCachedImap::setStatus( QList
<int> &ids
, const MessageStatus
&status
, bool toggle
)
1378 KMFolderMaildir::setStatus( ids
, status
, toggle
);
1379 mStatusChangedLocally
= true;
1382 /* Upload new folders to server */
1383 void KMFolderCachedImap::createNewFolders()
1385 QList
<KMFolderCachedImap
*> newFolders
= findNewFolders();
1386 if ( !newFolders
.isEmpty() ) {
1387 newState( mProgress
, i18n("Creating subfolders on server"));
1388 CachedImapJob
*job
= new CachedImapJob( newFolders
, CachedImapJob::tAddSubfolders
, this );
1389 connect( job
, SIGNAL( result(KMail::FolderJob
*) ), this, SLOT( slotIncreaseProgress() ) );
1390 connect( job
, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
1393 serverSyncInternal();
1397 QList
<KMFolderCachedImap
*> KMFolderCachedImap::findNewFolders()
1399 QList
<KMFolderCachedImap
*> newFolders
;
1400 if ( folder() && folder()->child() ) {
1401 QList
<KMFolderNode
*>::const_iterator it
;
1402 for ( it
= folder()->child()->begin(); it
!= folder()->child()->end(); ++it
) {
1403 KMFolderNode
*node
= *it
;
1404 if ( !node
->isDir() ) {
1405 if ( static_cast<KMFolder
*>(node
)->folderType() != KMFolderTypeCachedImap
) {
1406 kError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
1407 << node
->name() << " is not an IMAP folder\n";
1410 KMFolderCachedImap
*folder
=
1411 static_cast<KMFolderCachedImap
*>( static_cast<KMFolder
*>( node
)->storage() );
1412 if ( folder
->imapPath().isEmpty() ) {
1413 newFolders
<< folder
;
1421 bool KMFolderCachedImap::deleteMessages()
1423 if ( mUserRights
> 0 && !( mUserRights
& KMail::ACLJobs::Delete
) ) {
1427 /* Delete messages from cache that are gone from the server */
1428 QList
<KMMessage
*> msgsForDeletion
;
1431 * It is not possible to just go over all indices and remove
1432 * them one by one because the index list can get resized under
1433 * us. So use msg pointers instead.
1435 QMap
<ulong
,int>::const_iterator it
= uidMap
.constBegin();
1436 for ( ; it
!= uidMap
.constEnd(); it
++ ) {
1437 ulong
uid ( it
.key() );
1438 if ( uid
!= 0 && !uidsOnServer
.find( uid
) ) {
1439 msgsForDeletion
.append( getMsg( *it
) );
1443 if ( !msgsForDeletion
.isEmpty() ) {
1444 removeMsg( msgsForDeletion
);
1447 /* Delete messages from the server that we don't have anymore */
1448 if ( !uidsForDeletionOnServer
.isEmpty() ) {
1449 newState( mProgress
, i18n("Deleting removed messages from server"));
1450 QStringList sets
= KMFolderImap::makeSets( uidsForDeletionOnServer
, true );
1451 uidsForDeletionOnServer
.clear();
1452 kDebug(5006) << "Deleting " << sets
.count()
1453 << " sets of messages from server folder " << imapPath() << endl
;
1454 CachedImapJob
*job
=
1455 new CachedImapJob( sets
, CachedImapJob::tDeleteMessage
, this );
1456 connect( job
, SIGNAL( result( KMail::FolderJob
* ) ),
1457 this, SLOT( slotDeleteMessagesResult( KMail::FolderJob
* ) ) );
1465 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob
*job
)
1467 if ( job
->error() ) {
1468 // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages
1469 mSyncState
= SYNC_STATE_GET_MESSAGES
;
1472 serverSyncInternal();
1475 void KMFolderCachedImap::checkUidValidity()
1478 * IMAP root folders don't seem to have a UID validity setting.
1479 * Also, don't try the uid validity on new folders.
1481 if ( imapPath().isEmpty() || imapPath() == "/" ) {
1483 serverSyncInternal();
1485 newState( mProgress
, i18n("Checking folder validity"));
1486 CachedImapJob
*job
= new CachedImapJob( FolderJob::tCheckUidValidity
, this );
1487 connect( job
, SIGNAL( result( KMail::FolderJob
* ) ),
1488 this, SLOT( slotCheckUidValidityResult( KMail::FolderJob
* ) ) );
1493 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob
*job
)
1495 if ( job
->error() ) { // there was an error and the user chose "continue"
1497 * We can't continue doing anything in the same folder though,
1498 * it would delete all mails. But we can continue to subfolders
1499 * if any. Well we can also try annotation/acl stuff...
1501 mSyncState
= SYNC_STATE_HANDLE_INBOX
;
1504 serverSyncInternal();
1507 void KMFolderCachedImap::listMessages()
1509 bool groupwareOnly
=
1510 GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount() &&
1511 GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount
->id() &&
1512 folder()->isSystemFolder() && mImapPath
== "/INBOX/";
1515 * Don't list messages on the root folder, and skip the inbox, if this is
1516 * the inbox of a groupware-only dimap account.
1518 if ( imapPath() == "/" || groupwareOnly
) {
1519 serverSyncInternal();
1523 if ( !mAccount
->slave() ) { // sync aborted
1525 emit
folderComplete( this, false );
1528 uidsOnServer
.clear();
1529 uidsOnServer
.resize( count() * 2 );
1530 uidsForDeletionOnServer
.clear();
1531 mMsgsForDownload
.clear();
1532 mUidsForDownload
.clear();
1534 CachedImapJob
*job
= new CachedImapJob( FolderJob::tListMessages
, this );
1535 connect( job
, SIGNAL( result( KMail::FolderJob
* ) ),
1536 this, SLOT( slotGetLastMessagesResult( KMail::FolderJob
* ) ) );
1540 void KMFolderCachedImap::slotGetLastMessagesResult( KMail::FolderJob
*job
)
1542 getMessagesResult( job
, true );
1545 // Connected to the listMessages job in CachedImapJob
1546 void KMFolderCachedImap::slotGetMessagesData( KIO::Job
*job
, const QByteArray
&data
)
1548 KMAcctCachedImap::JobIterator it
= mAccount
->findJob( job
);
1549 if ( it
== mAccount
->jobsEnd() ) { // Shouldn't happen
1550 kDebug(5006) << "could not find job!?!?!" << endl
;
1552 * Be sure to reset the sync state, if the listing was partial we
1553 * would otherwise delete not-listed mail locally, and on the next
1554 * sync on the server as well.
1556 mSyncState
= SYNC_STATE_HANDLE_INBOX
;
1557 serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
1560 (*it
).cdata
+= data
;
1561 int pos
= (*it
).cdata
.indexOf("\r\n--IMAPDIGEST");
1563 int a
= (*it
).cdata
.indexOf("\r\nX-uidValidity:");
1565 int b
= (*it
).cdata
.indexOf("\r\n", a
+ 17);
1566 setUidValidity((*it
).cdata
.mid(a
+ 17, b
- a
- 17));
1568 a
= (*it
).cdata
.indexOf("\r\nX-Access:");
1571 * Only trust X-Access (i.e. the imap select info) if we don't know
1572 * mUserRights. The latter is more accurate (checked on every sync)
1573 * whereas X-Access is only updated when selecting the folder again,
1574 * which might not happen if using RMB / Check Mail in this folder.
1575 * We don't need two (potentially conflicting) sources for the
1576 * readonly setting, in any case.
1578 if ( a
!= -1 && mUserRights
== -1 ) {
1579 int b
= (*it
).cdata
.indexOf( "\r\n", a
+ 12 );
1580 const QString access
= (*it
).cdata
.mid( a
+ 12, b
- a
- 12 );
1581 setReadOnly( access
== "Read only" );
1583 (*it
).cdata
.remove( 0, pos
);
1585 pos
= (*it
).cdata
.indexOf( "\r\n--IMAPDIGEST", 1 );
1587 // Start with something largish when rebuilding the cache
1588 if ( uidsOnServer
.size() == 0 ) {
1589 uidsOnServer
.resize( KMail::nextPrime( 2000 ) );
1594 while ( pos
>= 0 ) {
1596 msg
.fromString( (*it
).cdata
.mid( 16, pos
- 16 ) );
1597 flags
= msg
.headerField( "X-Flags" ).toInt();
1598 bool deleted
= ( flags
& 8 );
1599 ulong uid
= msg
.UID();
1602 if ( uidsOnServer
.count() == uidsOnServer
.size() ) {
1603 uidsOnServer
.resize( KMail::nextPrime( uidsOnServer
.size() * 2 ) );
1604 kDebug( 5006 ) << "Resizing to: " << uidsOnServer
.size() << endl
;
1606 uidsOnServer
.insert( uid
, &v
);
1608 bool redownload
= false;
1609 if ( uid
<= lastUid() ) {
1611 * If this message UID is not present locally, then it must
1612 * have been deleted by the user, so we delete it on the
1613 * server also. If we don't have delete permissions on the server,
1614 * re-download the message, it must have vanished by some error, or
1615 * while we still thought we were allowed to delete (ACL change).
1617 * This relies heavily on lastUid() being correct at all times.
1619 KMMsgBase
*existingMessage
= findByUID( uid
);
1620 if ( !existingMessage
) {
1621 if ( mUserRights
<= 0 || ( mUserRights
& KMail::ACLJobs::Delete
) ) {
1622 uidsForDeletionOnServer
<< uid
;
1628 * If this is a read only folder, ignore status updates from the
1629 * server since we can't write our status back our local version
1630 * is what has to be considered correct.
1633 /* The message is OK, update flags */
1634 KMFolderImap::flagsToStatus( existingMessage
, flags
);
1638 if ( uid
> lastUid() || redownload
) {
1640 * The message is new since the last sync, but we might have
1641 * just uploaded it, in which case the uid map already contains it.
1643 if ( !uidMap
.contains( uid
) ) {
1644 ulong size
= msg
.headerField("X-Length").toULong();
1645 mMsgsForDownload
<< KMail::CachedImapJob::MsgForDownload( uid
, flags
, size
);
1646 if ( imapPath() == "/INBOX/" ) {
1647 mUidsForDownload
<< uid
;
1650 // Remember the highest uid and once the download is completed, update mLastUid
1651 if ( uid
> mTentativeHighestUid
) {
1652 mTentativeHighestUid
= uid
;
1656 (*it
).cdata
.remove( 0, pos
);
1658 pos
= (*it
).cdata
.indexOf( "\r\n--IMAPDIGEST", 1 );
1662 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob
*job
, bool lastSet
)
1665 if ( job
->error() ) { // error listing messages but the user chose to continue
1666 mContentState
= imapNoInformation
;
1667 mSyncState
= SYNC_STATE_HANDLE_INBOX
; // be sure not to continue in this folder
1669 if ( lastSet
) { // always true here (this comes from online-imap...)
1670 mContentState
= imapFinished
;
1671 mStatusChangedLocally
= false; // we are up to date again
1674 serverSyncInternal();
1677 void KMFolderCachedImap::slotProgress( unsigned long done
, unsigned long total
)
1679 int progressSpan
= 100 - 5 - mProgress
;
1681 // Progress info while retrieving new emails
1682 // (going from mProgress to mProgress+progressSpan)
1683 newState( mProgress
+ ( progressSpan
* done
) / total
, QString() );
1686 void KMFolderCachedImap::setAccount( KMAcctCachedImap
*aAccount
)
1688 assert( ::qobject_cast
<KMAcctCachedImap
*>( aAccount
) );
1689 mAccount
= aAccount
;
1690 if ( imapPath() == "/" ) {
1691 aAccount
->setFolder( folder() );
1694 // Folder was renamed in a previous session, and the user didn't sync yet
1695 QString newName
= mAccount
->renamedFolder( imapPath() );
1696 if ( !newName
.isEmpty() ) {
1697 folder()->setLabel( newName
);
1700 if ( !folder() || !folder()->child() || !folder()->child()->count() ) {
1704 QList
<KMFolderNode
*>::const_iterator it
;
1705 for ( it
= folder()->child()->begin(); it
!= folder()->child()->end(); ++it
) {
1706 KMFolderNode
*node
= *it
;
1707 if ( !node
->isDir() ) {
1708 static_cast<KMFolderCachedImap
*>(
1709 static_cast<KMFolder
*>( node
)->storage() )->setAccount( aAccount
);
1714 void KMFolderCachedImap::listNamespaces()
1716 ImapAccountBase::ListType type
= ImapAccountBase::List
;
1717 if ( mAccount
->onlySubscribedFolders() ) {
1718 type
= ImapAccountBase::ListSubscribed
;
1721 kDebug(5006) << "listNamespaces " << mNamespacesToList
<< endl
;
1722 if ( mNamespacesToList
.isEmpty() ) {
1723 mSyncState
= SYNC_STATE_DELETE_SUBFOLDERS
;
1724 mPersonalNamespacesCheckDone
= true;
1726 QStringList ns
= mAccount
->namespaces()[ImapAccountBase::OtherUsersNS
];
1727 ns
+= mAccount
->namespaces()[ImapAccountBase::SharedNS
];
1728 mNamespacesToCheck
= ns
.count();
1729 for ( QStringList::Iterator it
= ns
.begin(); it
!= ns
.end(); ++it
) {
1730 if ( (*it
).isEmpty() ) {
1731 // ignore empty listings as they have been listed before
1732 --mNamespacesToCheck
;
1735 KMail::ListJob
*job
=
1736 new KMail::ListJob( mAccount
, type
, this, mAccount
->addPathToNamespace( *it
) );
1737 connect( job
, SIGNAL( receivedFolders( const QStringList
&, const QStringList
&,
1738 const QStringList
&, const QStringList
&,
1739 const ImapAccountBase::jobData
& ) ),
1740 this, SLOT( slotCheckNamespace( const QStringList
&, const QStringList
&,
1741 const QStringList
&, const QStringList
&,
1742 const ImapAccountBase::jobData
& ) ) );
1745 if ( mNamespacesToCheck
== 0 ) {
1746 serverSyncInternal();
1750 mPersonalNamespacesCheckDone
= false;
1752 QString ns
= mNamespacesToList
.front();
1753 mNamespacesToList
.pop_front();
1755 mSyncState
= SYNC_STATE_LIST_SUBFOLDERS2
;
1756 newState( mProgress
, i18n("Retrieving folders for namespace %1", ns
) );
1757 KMail::ListJob
*job
= new KMail::ListJob( mAccount
, type
, this,
1758 mAccount
->addPathToNamespace( ns
) );
1759 job
->setNamespace( ns
);
1760 connect( job
, SIGNAL( receivedFolders( const QStringList
&, const QStringList
&,
1761 const QStringList
&, const QStringList
&,
1762 const ImapAccountBase::jobData
& ) ),
1763 this, SLOT( slotListResult( const QStringList
&, const QStringList
&,
1764 const QStringList
&, const QStringList
&,
1765 const ImapAccountBase::jobData
& ) ) );
1769 void KMFolderCachedImap::slotCheckNamespace( const QStringList
&subfolderNames
,
1770 const QStringList
&subfolderPaths
,
1771 const QStringList
&subfolderMimeTypes
,
1772 const QStringList
&subfolderAttributes
,
1773 const ImapAccountBase::jobData
&jobData
)
1775 Q_UNUSED( subfolderPaths
);
1776 Q_UNUSED( subfolderMimeTypes
);
1777 Q_UNUSED( subfolderAttributes
);
1778 --mNamespacesToCheck
;
1779 kDebug(5006) << "slotCheckNamespace " << subfolderNames
<< ",remain=" <<
1780 mNamespacesToCheck
<< endl
;
1782 // get a correct foldername:
1783 // strip / and make sure it does not contain the delimiter
1784 QString name
= jobData
.path
.mid( 1, jobData
.path
.length() - 2 );
1785 name
.remove( mAccount
->delimiterForNamespace( name
) );
1786 if ( name
.isEmpty() ) {
1787 // should not happen
1788 kWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl
;
1792 folder()->createChildFolder();
1793 QList
<KMFolderNode
*>::const_iterator it
;
1794 KMFolderNode
*node
= 0;
1795 for ( it
= folder()->child()->begin(); it
!= folder()->child()->end(); ++it
) {
1796 if ( !(*it
)->isDir() && (*it
)->name() == name
) {
1801 if ( !subfolderNames
.isEmpty() ) {
1803 // folder exists so we have nothing to do - it will be listed later
1804 kDebug(5006) << "found namespace folder " << name
<< endl
;
1807 kDebug(5006) << "create namespace folder " << name
<< endl
;
1808 KMFolder
*newFolder
= folder()->child()->createFolder( name
, false,
1809 KMFolderTypeCachedImap
);
1811 KMFolderCachedImap
*f
= static_cast<KMFolderCachedImap
*>( newFolder
->storage() );
1812 f
->setImapPath( mAccount
->addPathToNamespace( name
) );
1813 f
->setNoContent( true );
1814 f
->setAccount( mAccount
);
1815 f
->close( "cachedimap" );
1816 kmkernel
->dimapFolderMgr()->contentsChanged();
1821 kDebug(5006) << "delete namespace folder " << name
<< endl
;
1822 KMFolder
*fld
= static_cast<KMFolder
*>( node
);
1823 kmkernel
->dimapFolderMgr()->remove( fld
);
1827 if ( mNamespacesToCheck
== 0 ) {
1828 // all namespaces are done so continue with the next step
1829 serverSyncInternal();
1833 bool KMFolderCachedImap::listDirectory()
1835 if ( !mAccount
->slave() ) { // sync aborted
1837 emit
folderComplete( this, false );
1840 mSubfolderState
= imapInProgress
;
1843 ImapAccountBase::ListType type
= ImapAccountBase::List
;
1844 if ( mAccount
->onlySubscribedFolders() ) {
1845 type
= ImapAccountBase::ListSubscribed
;
1847 KMail::ListJob
*job
= new KMail::ListJob( mAccount
, type
, this );
1848 job
->setHonorLocalSubscription( true );
1849 connect( job
, SIGNAL( receivedFolders( const QStringList
&, const QStringList
&,
1850 const QStringList
&, const QStringList
&,
1851 const ImapAccountBase::jobData
& ) ),
1852 this, SLOT( slotListResult( const QStringList
&, const QStringList
&,
1853 const QStringList
&, const QStringList
&,
1854 const ImapAccountBase::jobData
& ) ) );
1860 void KMFolderCachedImap::slotListResult( const QStringList
&folderNames
,
1861 const QStringList
&folderPaths
,
1862 const QStringList
&folderMimeTypes
,
1863 const QStringList
&folderAttributes
,
1864 const ImapAccountBase::jobData
&jobData
)
1866 Q_UNUSED( jobData
);
1868 mSubfolderNames
= folderNames
;
1869 mSubfolderPaths
= folderPaths
;
1870 mSubfolderMimeTypes
= folderMimeTypes
;
1871 mSubfolderAttributes
= folderAttributes
;
1873 mSubfolderState
= imapFinished
;
1875 folder()->createChildFolder();
1876 bool root
= ( this == mAccount
->rootFolder() );
1878 QList
<KMFolder
*> toRemove
;
1879 bool emptyList
= ( root
&& mSubfolderNames
.empty() );
1881 QList
<KMFolderNode
*>::const_iterator it
;
1882 for ( it
= folder()->child()->begin(); it
!= folder()->child()->end(); ++it
) {
1883 KMFolderNode
*node
= *it
;
1884 if (!node
->isDir() ) {
1885 KMFolderCachedImap
*f
=
1886 static_cast<KMFolderCachedImap
*>( static_cast<KMFolder
*>( node
)->storage() );
1888 if ( !mSubfolderNames
.contains(node
->name()) ) {
1889 QString name
= node
->name();
1890 // as more than one namespace can be listed in the root folder we need to make sure
1891 // that the folder is within the current namespace
1892 bool isInNamespace
= ( jobData
.curNamespace
.isEmpty() ||
1893 jobData
.curNamespace
== mAccount
->namespaceForFolder( f
) );
1894 // ignore some cases
1895 bool ignore
= root
&& ( f
->imapPath() == "/INBOX/" ||
1896 mAccount
->isNamespaceFolder( name
) || !isInNamespace
);
1898 // This subfolder isn't present on the server
1899 if ( !f
->imapPath().isEmpty() && !ignore
) {
1900 // The folder has an imap path set, so it has been
1901 // on the server before. Delete it locally.
1902 toRemove
.append( f
->folder() );
1903 kDebug(5006) << node
->name()
1904 << " isn't on the server. It has an imapPath -> delete it locally" << endl
;
1906 } else { // folder both local and on server
1907 //kDebug(5006) << node->name() << " is on the server." << endl;
1910 //kDebug(5006) << "skipping dir node:" << node->name() << endl;
1915 QList
<KMFolder
*>::const_iterator jt
;
1916 for ( jt
= toRemove
.constBegin(); jt
!= toRemove
.constEnd(); ++jt
) {
1918 rescueUnsyncedMessagesAndDeleteFolder( *jt
);
1923 if ( mToBeDeletedAfterRescue
.isEmpty() )
1924 serverSyncInternal();
1927 void KMFolderCachedImap::listDirectory2()
1929 QString path
= folder()->path();
1930 kmkernel
->dimapFolderMgr()->quiet( true );
1932 bool root
= ( this == mAccount
->rootFolder() );
1933 if ( root
&& !mAccount
->hasInbox() ) {
1934 KMFolderCachedImap
*f
= 0;
1935 KMFolderNode
*node
= 0;
1937 QList
<KMFolderNode
*>::const_iterator it
= folder()->child()->begin();
1938 for ( ; it
!= folder()->child()->end(); ++it
) {
1939 if ( !(*it
)->isDir() && (*it
)->name() == "INBOX" ) {
1945 f
= static_cast<KMFolderCachedImap
*>( static_cast<KMFolder
*>( node
)->storage() );
1947 KMFolder
*newFolder
=
1948 folder()->child()->createFolder( "INBOX", true, KMFolderTypeCachedImap
);
1950 f
= static_cast<KMFolderCachedImap
*>( newFolder
->storage() );
1954 f
->setAccount( mAccount
);
1955 f
->setImapPath( "/INBOX/" );
1956 f
->folder()->setLabel( i18n("inbox") );
1960 f
->close( "cachedimap" );
1962 kmkernel
->dimapFolderMgr()->contentsChanged();
1964 // so we have an INBOX
1965 mAccount
->setHasInbox( true );
1968 if ( root
&& !mSubfolderNames
.isEmpty() ) {
1969 KMFolderCachedImap
*parent
=
1970 findParent( mSubfolderPaths
.first(), mSubfolderNames
.first() );
1972 kDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
1973 << parent
->label() << endl
;
1974 mSubfolderNames
.clear();
1978 // Find all subfolders present on server but not on disk
1979 QVector
<int> foldersNewOnServer
;
1980 for (int i
= 0; i
< mSubfolderNames
.count(); i
++) {
1982 // Find the subdir, if already present
1983 KMFolderCachedImap
*f
= 0;
1984 KMFolderNode
*node
= 0;
1985 for ( QList
<KMFolderNode
*>::ConstIterator it
= folder()->child()->begin();
1986 it
!= folder()->child()->end(); ++it
)
1987 if ( !(*it
)->isDir() && (*it
)->name() == mSubfolderNames
[i
] ) {
1993 // This folder is not present here
1994 // Either it's new on the server, or we just deleted it.
1995 QString subfolderPath
= mSubfolderPaths
[i
];
1996 // The code used to look at the uidcache to know if it was "just deleted".
1997 // But this breaks with noContent folders and with shared folders.
1998 // So instead we keep a list in the account.
1999 bool locallyDeleted
= mAccount
->isDeletedFolder( subfolderPath
);
2000 // That list is saved/restored across sessions, but to avoid any mistake,
2001 // ask for confirmation if the folder was deleted in a previous session
2002 // (could be that the folder was deleted & recreated meanwhile from another client...)
2003 if ( !locallyDeleted
&& mAccount
->isPreviouslyDeletedFolder( subfolderPath
) ) {
2004 locallyDeleted
= KMessageBox::warningYesNo(
2006 i18n("<qt><p>It seems that the folder <b>%1</b> was deleted. "
2007 "Do you want to delete it from the server?</p></qt>",
2008 mSubfolderNames
[i
] ), QString(),
2009 KStandardGuiItem::del(), KStandardGuiItem::cancel() ) == KMessageBox::Yes
;
2012 if ( locallyDeleted
) {
2013 kDebug(5006) << subfolderPath
2014 << " was deleted locally => delete on server." << endl
;
2015 // grab all subsubfolders too
2016 foldersForDeletionOnServer
+= mAccount
->deletedFolderPaths( subfolderPath
);
2018 kDebug(5006) << subfolderPath
2019 << " is a new folder on the server => create local cache" << endl
;
2020 foldersNewOnServer
.append( i
);
2022 } else { // Folder found locally
2023 if ( static_cast<KMFolder
*>(node
)->folderType() == KMFolderTypeCachedImap
) {
2024 f
= dynamic_cast<KMFolderCachedImap
*>(static_cast<KMFolder
*>(node
)->storage());
2027 // kDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
2028 // << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
2029 // Write folder settings
2030 f
->setAccount( mAccount
);
2031 f
->setNoContent( mSubfolderMimeTypes
[i
] == "inode/directory" );
2032 f
->setNoChildren( mSubfolderMimeTypes
[i
] == "message/digest" );
2033 f
->setImapPath( mSubfolderPaths
[i
] );
2038 /* In case we are ignoring non-groupware folders, and this is the groupware
2039 * main account, find out the contents types of folders that have newly
2040 * appeared on the server. Otherwise just create them and finish listing.
2041 * If a folder is already known to be locally unsubscribed, it won't be
2042 * listed at all, on this level, so these are only folders that we are
2043 * seeing for the first time. */
2045 /* Note: We ask the globalsettings, and not the current state of the
2046 * kmkernel->iCalIface().isEnabled(), since that is false during the
2047 * very first sync, where we already want to filter. */
2048 if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount() &&
2049 GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount
->id() &&
2050 mAccount
->hasAnnotationSupport() &&
2051 GlobalSettings::self()->theIMAPResourceEnabled() &&
2052 !foldersNewOnServer
.isEmpty() ) {
2055 for ( int i
= 0; i
< foldersNewOnServer
.count(); ++i
) {
2056 paths
<< mSubfolderPaths
[ foldersNewOnServer
[i
] ];
2059 AnnotationJobs::MultiUrlGetAnnotationJob
*job
=
2060 AnnotationJobs::multiUrlGetAnnotation(
2061 mAccount
->slave(), mAccount
->getUrl(), paths
, KOLAB_FOLDERTYPE
);
2062 ImapAccountBase::jobData
jd( QString(), folder() );
2063 jd
.cancellable
= true;
2064 mAccount
->insertJob( job
, jd
);
2065 connect( job
, SIGNAL( result( KJob
* ) ),
2066 SLOT( slotMultiUrlGetAnnotationResult( KJob
* ) ) );
2069 createFoldersNewOnServerAndFinishListing( foldersNewOnServer
);
2073 void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing(
2074 const QVector
<int> foldersNewOnServer
)
2076 for ( int i
= 0; i
< foldersNewOnServer
.count(); ++i
) {
2077 int idx
= foldersNewOnServer
[i
];
2078 KMFolder
*newFolder
=
2079 folder()->child()->createFolder( mSubfolderNames
[idx
], false, KMFolderTypeCachedImap
);
2081 KMFolderCachedImap
*f
= dynamic_cast<KMFolderCachedImap
*>(newFolder
->storage());
2082 kDebug(5006) << " ####### Locally creating folder " << mSubfolderNames
[idx
] <<endl
;
2083 f
->close( "cachedimap" );
2084 f
->setAccount( mAccount
);
2085 f
->mAnnotationFolderType
= "FROMSERVER";
2086 f
->setNoContent( mSubfolderMimeTypes
[idx
] == "inode/directory" );
2087 f
->setNoChildren( mSubfolderMimeTypes
[idx
] == "message/digest" );
2088 f
->setImapPath( mSubfolderPaths
[idx
] );
2089 kmkernel
->dimapFolderMgr()->contentsChanged();
2091 kDebug(5006) << "can't create folder " << mSubfolderNames
[idx
] <<endl
;
2095 kmkernel
->dimapFolderMgr()->quiet( false );
2096 emit
listComplete( this );
2098 if ( !mPersonalNamespacesCheckDone
) {
2099 // we're not done with the namespaces
2100 mSyncState
= SYNC_STATE_LIST_NAMESPACES
;
2102 serverSyncInternal();
2105 //-----------------------------------------------------------------------------
2106 KMFolderCachedImap
*KMFolderCachedImap::findParent( const QString
&path
,
2107 const QString
&name
)
2109 QString parent
= path
.left( path
.length() - name
.length() - 2 );
2111 if ( parent
.length() > 1 ) {
2112 // extract name of the parent
2113 parent
= parent
.right( parent
.length() - 1 );
2114 if ( parent
!= label() ) {
2115 // look for a better parent
2116 QList
<KMFolderNode
*>::const_iterator it
;
2117 for ( it
= folder()->child()->begin(); it
!= folder()->child()->end(); ++it
) {
2118 KMFolderNode
*node
= *it
;
2119 if ( node
->name() == parent
) {
2120 KMFolder
*fld
= static_cast<KMFolder
*>( node
);
2121 KMFolderCachedImap
*imapFld
=
2122 static_cast<KMFolderCachedImap
*>( fld
->storage() );
2131 void KMFolderCachedImap::slotSubFolderComplete( KMFolderCachedImap
*sub
, bool success
)
2136 serverSyncInternal();
2138 // success == false means the sync was aborted.
2139 if ( mCurrentSubfolder
) {
2140 Q_ASSERT( sub
== mCurrentSubfolder
);
2141 disconnect( mCurrentSubfolder
, SIGNAL( folderComplete( KMFolderCachedImap
*, bool ) ),
2142 this, SLOT( slotSubFolderComplete( KMFolderCachedImap
*, bool ) ) );
2143 mCurrentSubfolder
= 0;
2146 mSubfoldersForSync
.clear();
2147 mSyncState
= SYNC_STATE_INITIAL
;
2148 close( "cachedimap" );
2149 emit
folderComplete( this, false );
2153 void KMFolderCachedImap::slotSimpleData( KIO::Job
*job
, const QByteArray
&data
)
2155 KMAcctCachedImap::JobIterator it
= mAccount
->findJob( job
);
2156 if ( it
== mAccount
->jobsEnd() ) {
2160 QBuffer
buff( &(*it
).data
);
2161 buff
.open( QIODevice::WriteOnly
| QIODevice::Append
);
2162 buff
.write( data
.data(), data
.size() );
2166 FolderJob
*KMFolderCachedImap::doCreateJob( KMMessage
*msg
,
2167 FolderJob::JobType jt
,
2169 const QString
&partSpecifier
,
2170 const AttachmentStrategy
* ) const
2172 Q_UNUSED( partSpecifier
);
2174 QList
<KMMessage
*> msgList
;
2175 msgList
.append( msg
);
2177 CachedImapJob
*job
=
2179 msgList
, jt
, folder
? static_cast<KMFolderCachedImap
*>( folder
->storage() ) : 0 );
2180 job
->setParentFolder( this );
2184 FolderJob
*KMFolderCachedImap::doCreateJob( QList
<KMMessage
*> &msgList
,
2185 const QString
&sets
,
2186 FolderJob::JobType jt
,
2187 KMFolder
*folder
) const
2189 //FIXME: how to handle sets here?
2191 CachedImapJob
*job
=
2193 msgList
, jt
, folder
? static_cast<KMFolderCachedImap
*>( folder
->storage() ) : 0 );
2194 job
->setParentFolder( this );
2198 void KMFolderCachedImap::setUserRights( unsigned int userRights
)
2200 mUserRights
= userRights
;
2201 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2204 void KMFolderCachedImap::slotReceivedUserRights( KMFolder
*folder
)
2206 if ( folder
->storage() == this ) {
2207 disconnect( mAccount
, SIGNAL( receivedUserRights( KMFolder
* ) ),
2208 this, SLOT( slotReceivedUserRights( KMFolder
* ) ) );
2209 if ( mUserRights
== 0 ) { // didn't work
2210 mUserRights
= -1; // error code (used in folderdia)
2212 setReadOnly( ( mUserRights
& KMail::ACLJobs::Insert
) == 0 );
2215 serverSyncInternal();
2219 void KMFolderCachedImap::setReadOnly( bool readOnly
)
2221 if ( readOnly
!= mReadOnly
) {
2222 mReadOnly
= readOnly
;
2223 emit
readOnlyChanged( folder() );
2227 void KMFolderCachedImap::slotReceivedACL( KMFolder
*folder
,
2228 KIO::Job
*, const KMail::ACLList
&aclList
)
2230 if ( folder
->storage() == this ) {
2231 disconnect( mAccount
, SIGNAL( receivedACL( KMFolder
*, KIO::Job
*, const KMail::ACLList
& ) ),
2232 this, SLOT( slotReceivedACL( KMFolder
*, KIO::Job
*, const KMail::ACLList
& ) ) );
2234 serverSyncInternal();
2238 void KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo
&info
)
2241 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2244 void KMFolderCachedImap::setACLList( const ACLList
&arr
)
2249 void KMFolderCachedImap::slotMultiSetACLResult( KJob
*job
)
2251 KMAcctCachedImap::JobIterator it
=
2252 mAccount
->findJob( static_cast<KIO::Job
*>( job
) );
2254 if ( it
== mAccount
->jobsEnd() ) {
2255 return; // Shouldn't happen
2257 if ( (*it
).parent
!= folder() ) {
2258 return; // Shouldn't happen
2261 if ( job
->error() ) {
2262 // Display error but don't abort the sync just for this
2263 // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
2264 static_cast<KIO::Job
*>(job
)->ui()->setWindow( 0 );
2265 static_cast<KIO::Job
*>(job
)->ui()->showErrorMessage();
2267 kmkernel
->iCalIface().addFolderChange( folder(), ACL
);
2270 if ( mAccount
->slave() ) {
2271 mAccount
->removeJob( static_cast<KIO::Job
*>( job
) );
2273 serverSyncInternal();
2277 KMFolderCachedImap::slotACLChanged( const QString
&userId
, int permissions
)
2279 // The job indicates success in changing the permissions for this user
2280 // -> we note that it's been done.
2281 for ( ACLList::Iterator it
= mACLList
.begin(); it
!= mACLList
.end(); ++it
) {
2282 if ( (*it
).userId
== userId
&& (*it
).permissions
== permissions
) {
2283 if ( permissions
== -1 ) { // deleted
2284 mACLList
.erase( it
);
2285 } else { // added/modified
2286 (*it
).changed
= false;
2293 // called by KMAcctCachedImap::killAllJobs
2294 void KMFolderCachedImap::resetSyncState()
2296 if ( mSyncState
== SYNC_STATE_INITIAL
) {
2300 mSubfoldersForSync
.clear();
2301 mSyncState
= SYNC_STATE_INITIAL
;
2302 close( "cachedimap" );
2304 // Don't use newState here, it would revert to mProgress
2305 // (which is less than the current value when listing messages)
2306 KPIM::ProgressItem
*progressItem
= mAccount
->mailCheckProgressItem();
2307 QString str
= i18n("Aborted");
2308 if ( progressItem
) {
2309 progressItem
->setStatus( str
);
2311 emit
statusMsg( str
);
2314 void KMFolderCachedImap::slotIncreaseProgress()
2319 void KMFolderCachedImap::newState( int progress
, const QString
&syncStatus
)
2321 KPIM::ProgressItem
*progressItem
= mAccount
->mailCheckProgressItem();
2322 if ( progressItem
) {
2323 progressItem
->setCompletedItems( progress
);
2326 if ( !syncStatus
.isEmpty() ) {
2328 // For a subfolder, show the label. But for the main folder, it's already shown.
2329 if ( mAccount
->imapFolder() == this ) {
2332 str
= QString( "%1: %2" ).arg( label() ).arg( syncStatus
);
2334 if ( progressItem
) {
2335 progressItem
->setStatus( str
);
2337 emit
statusMsg( str
);
2339 if ( progressItem
) {
2340 progressItem
->updateProgress();
2344 void KMFolderCachedImap::setSubfolderState( imapState state
)
2346 mSubfolderState
= state
;
2347 if ( state
== imapNoInformation
&& folder()->child() ) {
2348 // pass through to children
2349 QList
<KMFolderNode
*>::const_iterator it
;
2350 for ( it
= folder()->child()->begin(); it
!= folder()->child()->end(); ++it
) {
2351 KMFolderNode
*node
= *it
;
2352 if ( node
->isDir() ) {
2355 KMFolder
*folder
= static_cast<KMFolder
*>( node
);
2356 static_cast<KMFolderCachedImap
*>( folder
->storage())->setSubfolderState( state
);
2361 void KMFolderCachedImap::setImapPath( const QString
&path
)
2366 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
2367 // It is updated from the folder contents type and whether it's a standard resource folder.
2368 // This happens during the syncing phase and during initFolder for a new folder.
2369 // Don't do it earlier, e.g. from setContentsType:
2370 // on startup, it's too early there to know if this is a standard resource folder.
2371 void KMFolderCachedImap::updateAnnotationFolderType()
2373 QString oldType
= mAnnotationFolderType
;
2375 int dot
= oldType
.indexOf( '.' );
2377 oldType
.truncate( dot
);
2378 oldSubType
= mAnnotationFolderType
.mid( dot
+ 1 );
2381 QString newType
, newSubType
;
2382 // We want to store an annotation on the folder only if using the kolab storage.
2383 if ( kmkernel
->iCalIface().storageFormat( folder() ) == StorageXML
) {
2384 newType
= KMailICalIfaceImpl::annotationForContentsType( mContentsType
);
2385 if ( kmkernel
->iCalIface().isStandardResourceFolder( folder() ) ) {
2386 newSubType
= "default";
2388 // preserve unknown subtypes, like drafts etc. And preserve ".default" too.
2389 newSubType
= oldSubType
;
2393 if ( newType
!= oldType
|| newSubType
!= oldSubType
) {
2394 mAnnotationFolderType
= newType
+ ( newSubType
.isEmpty() ? QString() : '.'+newSubType
);
2395 mAnnotationFolderTypeChanged
= true; // force a "set annotation" on next sync
2396 kDebug(5006) << mImapPath
<< ": updateAnnotationFolderType: '"
2397 << mAnnotationFolderType
<< "', was (" << oldType
2398 << " " << oldSubType
<< ") => mAnnotationFolderTypeChanged set to TRUE" << endl
;
2400 // Ensure that further readConfig()s don't lose mAnnotationFolderType
2401 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2404 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor
)
2406 if ( mIncidencesFor
!= incfor
) {
2407 mIncidencesFor
= incfor
;
2408 mIncidencesForChanged
= true;
2412 void KMFolderCachedImap::slotAnnotationResult( const QString
&entry
,
2413 const QString
&value
,
2416 if ( entry
== KOLAB_FOLDERTYPE
) {
2418 * There are four cases.
2419 * 1) no content-type on server -> set it
2420 * 2) different content-type on server, locally changed -> set it
2421 * (we don't even come here)
2422 * 3) different (known) content-type on server, no local change -> get it
2423 * 4) different unknown content-type on server, probably some older
2427 QString type
= value
;
2429 int dot
= value
.indexOf( '.' );
2431 type
.truncate( dot
);
2432 subtype
= value
.mid( dot
+ 1 );
2434 bool foundKnownType
= false;
2435 for ( uint i
= 0 ; i
<= ContentsTypeLast
; ++i
) {
2436 FolderContentsType contentsType
= static_cast<KMail::FolderContentsType
>( i
);
2437 if ( type
== KMailICalIfaceImpl::annotationForContentsType( contentsType
) ) {
2438 // Case 3: known content-type on server, get it
2439 if ( contentsType
!= ContentsTypeMail
) {
2440 kmkernel
->iCalIface().setStorageFormat( folder(), StorageXML
);
2442 mAnnotationFolderType
= value
;
2443 if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent() &&
2444 GlobalSettings::self()->theIMAPResourceEnabled() &&
2445 subtype
== "default" ) {
2446 // Truncate subtype if this folder can't be a default resource
2447 // folder for us, although it apparently is for someone else.
2448 mAnnotationFolderType
= type
;
2449 kDebug(5006) << mImapPath
2450 << ": slotGetAnnotationResult: parent folder is "
2451 << folder()->parent()->owner()->idString()
2452 << " => truncating annotation to " << value
<< endl
;
2454 setContentsType( contentsType
);
2455 mAnnotationFolderTypeChanged
= false; // we changed it, not the user
2456 foundKnownType
= true;
2459 * Users don't read events/contacts/etc. in kmail, so mark them all
2460 * as read. This is done in cachedimapjob when getting new messages,
2461 * but do it here too, for the initial set of messages when we
2462 * didn't know this was a resource folder yet, for old folders, etc.
2464 if ( contentsType
!= ContentsTypeMail
) {
2468 // Ensure that further readConfig()s don't lose mAnnotationFolderType
2469 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2473 if ( !foundKnownType
&& !mReadOnly
) {
2474 // Case 4: server has strange content-type, set it to what we need
2475 mAnnotationFolderTypeChanged
= true;
2477 // TODO handle subtype (inbox, drafts, sentitems, junkemail)
2478 } else if ( !mReadOnly
) {
2479 // Case 1: server doesn't have content-type, set it
2480 mAnnotationFolderTypeChanged
= true;
2482 } else if ( entry
== KOLAB_INCIDENCESFOR
) {
2484 mIncidencesFor
= incidencesForFromString( value
);
2485 Q_ASSERT( mIncidencesForChanged
== false );
2490 void KMFolderCachedImap::slotGetAnnotationResult( KJob
*job
)
2492 KMAcctCachedImap::JobIterator it
=
2493 mAccount
->findJob( static_cast<KIO::Job
*>( job
) );
2495 Q_ASSERT( it
!= mAccount
->jobsEnd() );
2496 if ( it
== mAccount
->jobsEnd() ) {
2497 return; // Shouldn't happen
2499 Q_ASSERT( (*it
).parent
== folder() );
2500 if ( (*it
).parent
!= folder() ) {
2501 return; // Shouldn't happen
2504 AnnotationJobs::GetAnnotationJob
*annjob
=
2505 static_cast<AnnotationJobs::GetAnnotationJob
*>( job
);
2506 if ( annjob
->error() ) {
2507 if ( job
->error() == KIO::ERR_UNSUPPORTED_ACTION
) {
2508 // that's when the imap server doesn't support annotations
2509 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
&&
2510 (uint
)GlobalSettings::self()->theIMAPResourceAccount() == mAccount
->id() ) {
2513 i18n( "The IMAP server %1 does not have support for IMAP annotations. "
2514 "The XML storage cannot be used on this server; "
2515 "please re-configure KMail differently.",
2516 mAccount
->host() ) );
2518 mAccount
->setHasNoAnnotationSupport();
2520 kWarning(5006) << "slotGetAnnotationResult: " << job
->errorString() << endl
;
2524 if ( mAccount
->slave() ) {
2525 mAccount
->removeJob( static_cast<KIO::Job
*>( job
) );
2528 serverSyncInternal();
2531 void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( KJob
*job
)
2533 KMAcctCachedImap::JobIterator it
= mAccount
->findJob( job
);
2534 Q_ASSERT( it
!= mAccount
->jobsEnd() );
2535 if ( it
== mAccount
->jobsEnd() ) {
2536 return; // Shouldn't happen
2538 Q_ASSERT( (*it
).parent
== folder() );
2539 if ( (*it
).parent
!= folder() ) {
2540 return; // Shouldn't happen
2543 QVector
<int> folders
;
2544 AnnotationJobs::MultiUrlGetAnnotationJob
*annjob
=
2545 static_cast<AnnotationJobs::MultiUrlGetAnnotationJob
*>( job
);
2546 if ( annjob
->error() ) {
2547 if ( job
->error() == KIO::ERR_UNSUPPORTED_ACTION
) {
2548 // that's when the imap server doesn't support annotations
2549 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
&&
2550 (uint
)GlobalSettings::self()->theIMAPResourceAccount() == mAccount
->id() ) {
2553 i18n( "The IMAP server %1 does not support annotations. "
2554 "The XML storage cannot be used on this server, "
2555 "please re-configure KMail differently",
2556 mAccount
->host() ) );
2558 mAccount
->setHasNoAnnotationSupport();
2560 kWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job
->errorString() << endl
;
2563 // we got the annotation allright, let's filter out the ones with the wrong type
2564 QMap
<QString
, QString
> annotations
= annjob
->annotations();
2565 QMap
<QString
, QString
>::Iterator it
= annotations
.begin();
2566 for ( ; it
!= annotations
.end(); ++it
) {
2567 const QString folderPath
= it
.key();
2568 const QString annotation
= it
.value();
2569 kDebug(5006) << k_funcinfo
<< "Folder: " << folderPath
<< " has type: " << annotation
<< endl
;
2570 // we're only interested in the main type
2571 QString
type( annotation
);
2572 int dot
= annotation
.indexOf( '.' );
2574 type
.truncate( dot
);
2576 type
= type
.simplified();
2578 const int idx
= mSubfolderPaths
.indexOf( folderPath
);
2579 const bool isNoContent
= mSubfolderMimeTypes
[idx
] == "inode/directory";
2580 if ( ( isNoContent
&& type
.isEmpty() ) ||
2581 ( !type
.isEmpty() &&
2582 type
!= KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail
) ) ) {
2583 folders
.append( idx
);
2584 kDebug(5006) << k_funcinfo
<< " subscribing to: " << folderPath
<< endl
;
2586 kDebug(5006) << k_funcinfo
<< " automatically unsubscribing from: " << folderPath
<< endl
;
2587 mAccount
->changeLocalSubscription( folderPath
, false );
2592 if ( mAccount
->slave() ) {
2593 mAccount
->removeJob( static_cast<KIO::Job
*>( job
) );
2595 createFoldersNewOnServerAndFinishListing( folders
);
2598 void KMFolderCachedImap::slotQuotaResult( KIO::Job
*job
)
2600 KMAcctCachedImap::JobIterator it
= mAccount
->findJob( job
);
2601 Q_ASSERT( it
!= mAccount
->jobsEnd() );
2602 if ( it
== mAccount
->jobsEnd() ) {
2603 return; // Shouldn't happen
2605 Q_ASSERT( (*it
).parent
== folder() );
2606 if ( (*it
).parent
!= folder() ) {
2607 return; // Shouldn't happen
2610 QuotaJobs::GetStorageQuotaJob
*quotajob
= static_cast<QuotaJobs::GetStorageQuotaJob
*>( job
);
2612 if ( quotajob
->error() ) {
2613 if ( job
->error() == KIO::ERR_UNSUPPORTED_ACTION
) {
2614 // that's when the imap server doesn't support quota
2615 mAccount
->setHasNoQuotaSupport();
2618 kWarning(5006) << "slotGetQuotaResult: " << job
->errorString() << endl
;
2622 if ( mAccount
->slave() ) {
2623 mAccount
->removeJob( job
);
2626 serverSyncInternal();
2629 void KMFolderCachedImap::slotAnnotationChanged( const QString
&entry
,
2630 const QString
&attribute
,
2631 const QString
&value
)
2633 kDebug(5006) << k_funcinfo
<< entry
<< " " << attribute
<< " " << value
<< endl
;
2634 if ( entry
== KOLAB_FOLDERTYPE
) {
2635 mAnnotationFolderTypeChanged
= false;
2636 } else if ( entry
== KOLAB_INCIDENCESFOR
) {
2637 mIncidencesForChanged
= false;
2639 * The incidences-for changed, we must trigger the freebusy creation.
2640 * HACK: in theory we would need a new enum value for this.
2642 kmkernel
->iCalIface().addFolderChange( folder(), ACL
);
2646 void KMFolderCachedImap::slotTestAnnotationResult( KJob
*job
)
2648 KMAcctCachedImap::JobIterator it
=
2649 mAccount
->findJob( static_cast<KIO::Job
*>( job
) );
2651 Q_ASSERT( it
!= mAccount
->jobsEnd() );
2652 if ( it
== mAccount
->jobsEnd() ) {
2653 return; // Shouldn't happen
2655 Q_ASSERT( (*it
).parent
== folder() );
2656 if ( (*it
).parent
!= folder() ) {
2657 return; // Shouldn't happen
2660 mAccount
->setAnnotationCheckPassed( true );
2661 if ( job
->error() ) {
2662 kDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl
;
2663 mAccount
->setHasNoAnnotationSupport( );
2665 kDebug(5006) << "Test Annotation was passed OK" << endl
;
2667 if ( mAccount
->slave() ) {
2668 mAccount
->removeJob( static_cast<KIO::Job
*>( job
) );
2670 serverSyncInternal();
2673 void KMFolderCachedImap::slotSetAnnotationResult( KJob
*job
)
2675 KMAcctCachedImap::JobIterator it
=
2676 mAccount
->findJob( static_cast<KIO::Job
*>( job
) );
2678 if ( it
== mAccount
->jobsEnd() ) {
2679 return; // Shouldn't happen
2681 if ( (*it
).parent
!= folder() ) {
2682 return; // Shouldn't happen
2686 if ( job
->error() ) {
2688 * Don't show error if the server doesn't support ANNOTATEMORE
2689 * and this folder only contains mail.
2691 if ( job
->error() == KIO::ERR_UNSUPPORTED_ACTION
&&
2692 contentsType() == ContentsTypeMail
) {
2693 if ( mAccount
->slave() ) {
2694 mAccount
->removeJob( static_cast<KIO::Job
*>( job
) );
2697 mAccount
->handleJobError( static_cast<KIO::Job
*>( job
),
2698 i18n( "Error while setting annotation: " ) + '\n' );
2702 if ( mAccount
->slave() ) {
2703 mAccount
->removeJob( static_cast<KIO::Job
*>( job
) );
2707 serverSyncInternal();
2711 void KMFolderCachedImap::slotUpdateLastUid()
2713 if ( mTentativeHighestUid
!= 0 ) {
2714 setLastUid( mTentativeHighestUid
);
2716 mTentativeHighestUid
= 0;
2719 bool KMFolderCachedImap::isMoveable() const
2721 return ( hasChildren() == HasNoChildren
&&
2722 !folder()->isSystemFolder() ) ? true : false;
2725 void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
2727 for ( QStringList::const_iterator it
= foldersForDeletionOnServer
.constBegin();
2728 it
!= foldersForDeletionOnServer
.constEnd(); ++it
) {
2729 KUrl
url( mAccount
->getUrl() );
2731 kmkernel
->iCalIface().folderDeletedOnServer( url
);
2733 serverSyncInternal();
2736 int KMFolderCachedImap::createIndexFromContentsRecursive()
2738 if ( !folder() || !folder()->child() ) {
2742 for ( QList
<KMFolderNode
*>::Iterator it
= folder()->child()->begin();
2743 it
!= folder()->child()->end(); ++it
) {
2744 if ( !(*it
)->isDir() ) {
2745 KMFolderCachedImap
*storage
=
2746 static_cast<KMFolderCachedImap
*>(static_cast<KMFolder
*>(*it
)->storage());
2747 kDebug(5006) << k_funcinfo
<< "Re-indexing: " << storage
->folder()->label() << endl
;
2748 int rv
= storage
->createIndexFromContentsRecursive();
2755 return createIndexFromContents();
2758 void KMFolderCachedImap::setAlarmsBlocked( bool blocked
)
2760 mAlarmsBlocked
= blocked
;
2763 bool KMFolderCachedImap::alarmsBlocked() const
2765 return mAlarmsBlocked
;
2768 KMCommand
* KMFolderCachedImap::rescueUnsyncedMessages()
2770 QList
<unsigned long> newMsgs
= findNewMessages();
2771 kDebug(5006) << k_funcinfo
<< newMsgs
<< " of " << count() << endl
;
2772 if ( newMsgs
.isEmpty() )
2775 bool manualMove
= true;
2776 while ( GlobalSettings::autoLostFoundMove() ) {
2777 // find the inbox of this account
2778 KMFolder
*inboxFolder
= kmkernel
->findFolderById( QString(".%1.directory/INBOX").arg( account()->id() ) );
2779 if ( !inboxFolder
) {
2780 kWarning(5006) << k_funcinfo
<< "inbox not found!" << endl
;
2783 KMFolderDir
*inboxDir
= inboxFolder
->child();
2784 if ( !inboxDir
&& !inboxFolder
->storage() )
2786 assert( inboxFolder
->storage()->folderType() == KMFolderTypeCachedImap
);
2788 // create lost+found folder if needed
2790 KMFolder
*lfFolder
= 0;
2791 if ( !(node
= inboxDir
->hasNamedFolder( i18n("lost+found") )) ) {
2792 kDebug(5006) << k_funcinfo
<< "creating lost+found folder" << endl
;
2793 KMFolder
* folder
= kmkernel
->dimapFolderMgr()->createFolder(
2794 i18n("lost+found"), false, KMFolderTypeCachedImap
, inboxDir
);
2795 if ( !folder
|| !folder
->storage() )
2797 static_cast<KMFolderCachedImap
*>( folder
->storage() )->initializeFrom(
2798 static_cast<KMFolderCachedImap
*>( inboxFolder
->storage() ) );
2799 folder
->storage()->setContentsType( KMail::ContentsTypeMail
);
2800 folder
->storage()->writeConfig();
2803 kDebug(5006) << k_funcinfo
<< "found lost+found folder" << endl
;
2804 lfFolder
= dynamic_cast<KMFolder
*>( node
);
2806 if ( !lfFolder
|| !lfFolder
->createChildFolder() || !lfFolder
->storage() )
2809 // create subfolder for this incident
2810 QDate today
= QDate::currentDate();
2811 QString baseName
= folder()->label() + "-" + QString::number( today
.year() )
2812 + (today
.month() < 10 ? "0" : "" ) + QString::number( today
.month() )
2813 + (today
.day() < 10 ? "0" : "" ) + QString::number( today
.day() );
2814 QString name
= baseName
;
2816 while ( (node
= lfFolder
->child()->hasNamedFolder( name
)) ) {
2818 name
= baseName
+ '-' + QString::number( suffix
);
2820 kDebug(5006) << k_funcinfo
<< "creating lost+found folder " << name
<< endl
;
2821 dest
= kmkernel
->dimapFolderMgr()->createFolder( name
, false, KMFolderTypeCachedImap
, lfFolder
->child() );
2822 if ( !dest
|| !dest
->storage() )
2824 static_cast<KMFolderCachedImap
*>( dest
->storage() )->initializeFrom(
2825 static_cast<KMFolderCachedImap
*>( lfFolder
->storage() ) );
2826 dest
->storage()->setContentsType( contentsType() );
2827 dest
->storage()->writeConfig();
2829 KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which "
2830 "have not been uploaded to the server yet, but the folder has been deleted "
2831 "on the server or you do not "
2832 "have sufficient access rights on the folder to upload them.</p>"
2833 "<p>All affected messages will therefore be moved to <b>%2</b> "
2834 "to avoid data loss.</p>").arg( folder()->prettyUrl() ).arg( dest
->prettyUrl() ),
2835 i18n("Insufficient access rights") );
2841 const QString
msg ( i18n( "<p>There are new messages in this folder (%1), which "
2842 "have not been uploaded to the server yet, but the folder has been deleted "
2843 "on the server or you do not "
2844 "have sufficient access rights on the folder now to upload them. "
2845 "Please contact your administrator to allow upload of new messages "
2846 "to you, or move them out of this folder.</p> "
2847 "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyUrl() ) );
2848 if ( KMessageBox::warningYesNo( 0, msg
, QString::null
, KGuiItem( i18n("Move") ), KGuiItem( i18n("Do Not Move") ) )
2849 == KMessageBox::Yes
) {
2850 KMail::FolderSelectionDialog
dlg( kmkernel
->getKMMainWidget(),
2851 i18n("Move Messages to Folder"), true );
2853 dest
= dlg
.folder();
2858 QList
<KMMsgBase
*> msgs
;
2859 for( int i
= 0; i
< count(); ++i
) {
2860 KMMsgBase
*msg
= getMsgBase( i
);
2861 if( !msg
) continue; /* what goes on if getMsg() returns 0? */
2862 if ( msg
->UID() == 0 )
2865 KMCommand
*command
= new KMMoveCommand( dest
, msgs
);
2872 void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder
*folder
, bool root
)
2874 kDebug(5006) << k_funcinfo
<< folder
<< " " << root
<< endl
;
2876 mToBeDeletedAfterRescue
.append( folder
);
2877 folder
->open( "KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder" );
2878 KMFolderCachedImap
* storage
= dynamic_cast<KMFolderCachedImap
*>( folder
->storage() );
2880 KMCommand
*command
= storage
->rescueUnsyncedMessages();
2882 connect( command
, SIGNAL(completed(KMCommand
*)),
2883 SLOT(slotRescueDone(KMCommand
*)) );
2884 ++mRescueCommandCount
;
2887 if ( folder
->child() ) {
2888 for ( KMFolderNodeList::ConstIterator it
= folder
->child()->constBegin(); it
!= folder
->child()->constEnd(); ++it
) {
2889 KMFolderNode
*node
= *it
;
2890 if ( node
&& !node
->isDir() ) {
2891 KMFolder
*subFolder
= static_cast<KMFolder
*>( node
);
2892 rescueUnsyncedMessagesAndDeleteFolder( subFolder
, false );
2896 folder
->close( "KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder" );
2898 slotRescueDone( 0 ); // just in case there is nothing to rescue
2901 void KMFolderCachedImap::slotRescueDone(KMCommand
* command
)
2903 // FIXME: check command result
2904 --mRescueCommandCount
;
2905 if ( mRescueCommandCount
> 0 )
2907 for ( QList
<KMFolder
*>::ConstIterator it
= mToBeDeletedAfterRescue
.constBegin();
2908 it
!= mToBeDeletedAfterRescue
.constEnd(); ++it
) {
2909 kmkernel
->dimapFolderMgr()->remove( *it
);
2911 mToBeDeletedAfterRescue
.clear();
2912 serverSyncInternal();
2915 #include "kmfoldercachedimap.moc"