kmail

cachedimapjob.cpp
1 /*
2  *
3  * This file is part of KMail, the KDE mail client.
4  * Copyright (c) 2002-2004 Bo Thorsen <bo@sonofthor.dk>
5  * 2002-2003 Steffen Hansen <hansen@kde.org>
6  * 2002-2003 Zack Rusin <zack@kde.org>
7  *
8  * KMail is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License, version 2, as
10  * published by the Free Software Foundation.
11  *
12  * KMail is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  * In addition, as a special exception, the copyright holders give
22  * permission to link the code of this program with any edition of
23  * the TQt library by Trolltech AS, Norway (or with modified versions
24  * of TQt that use the same license as TQt), and distribute linked
25  * combinations including the two. You must obey the GNU General
26  * Public License in all respects for all of the code used other than
27  * TQt. If you modify this file, you may extend this exception to
28  * your version of the file, but you are not obligated to do so. If
29  * you do not wish to do so, delete this exception statement from
30  * your version.
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36 
37 #include "cachedimapjob.h"
38 #include "imapaccountbase.h"
39 
40 #include "kmfoldermgr.h"
41 #include "kmfolder.h"
42 #include "kmfoldercachedimap.h"
43 #include "kmailicalifaceimpl.h"
44 #include "kmacctcachedimap.h"
45 #include "kmmsgdict.h"
46 #include "maildirjob.h"
47 #include "scalix.h"
48 #include "util.h"
49 
50 #include <tdeio/scheduler.h>
51 #include <tdeio/job.h>
52 
53 #include <tdelocale.h>
54 #include <kdebug.h>
55 
56 
57 namespace KMail {
58 
59 // Get messages
60 CachedImapJob::CachedImapJob( const TQValueList<MsgForDownload>& msgs,
61  JobType type, KMFolderCachedImap* folder )
62  : FolderJob( type ), mFolder( folder ), mMsgsForDownload( msgs ),
63  mTotalBytes(0), mMsg(0), mParentFolder( 0 )
64 {
65  TQValueList<MsgForDownload>::ConstIterator it = msgs.begin();
66  for ( ; it != msgs.end() ; ++it )
67  mTotalBytes += (*it).size;
68 }
69 
70 // Put messages
71 CachedImapJob::CachedImapJob( const TQPtrList<KMMessage>& msgs, JobType type,
72  KMFolderCachedImap* folder )
73  : FolderJob( msgs, TQString(), type, folder?folder->folder():0 ), mFolder( folder ),
74  mTotalBytes( msgs.count() ), // we abuse it as "total number of messages"
75  mMsg( 0 ), mParentFolder( 0 )
76 {
77 }
78 
79 CachedImapJob::CachedImapJob( const TQValueList<unsigned long>& msgs,
80  JobType type, KMFolderCachedImap* folder )
81  : FolderJob( TQPtrList<KMMessage>(), TQString(), type, folder?folder->folder():0 ),
82  mFolder( folder ), mSerNumMsgList( msgs ), mTotalBytes( msgs.count() ), mMsg( 0 ),
83  mParentFolder ( 0 )
84 {
85 }
86 
87 // Add sub folders
88 CachedImapJob::CachedImapJob( const TQValueList<KMFolderCachedImap*>& fList,
89  JobType type, KMFolderCachedImap* folder )
90  : FolderJob( type ), mFolder( folder ), mFolderList( fList ), mMsg( 0 ),
91  mParentFolder ( 0 )
92 {
93 }
94 
95 // Rename folder
96 CachedImapJob::CachedImapJob( const TQString& string1, JobType type,
97  KMFolderCachedImap* folder )
98  : FolderJob( type ), mFolder(folder), mMsg( 0 ), mString( string1 ),
99  mParentFolder ( 0 )
100 {
101  assert( folder );
102  assert( type != tDeleteMessage ); // moved to another ctor
103 }
104 
105 // Delete folders or messages
106 CachedImapJob::CachedImapJob( const TQStringList& foldersOrMsgs, JobType type,
107  KMFolderCachedImap* folder )
108  : FolderJob( type ), mFolder( folder ), mFoldersOrMessages( foldersOrMsgs ),
109  mMsg( 0 ), mParentFolder( 0 )
110 {
111  assert( folder );
112 }
113 
114 // Other jobs (list messages,expunge folder, check uid validity)
115 CachedImapJob::CachedImapJob( JobType type, KMFolderCachedImap* folder )
116  : FolderJob( type ), mFolder( folder ), mMsg( 0 ), mParentFolder ( 0 )
117 {
118  assert( folder );
119 }
120 
121 CachedImapJob::~CachedImapJob()
122 {
123  mAccount->mJobList.remove(this);
124 }
125 
126 void CachedImapJob::execute()
127 {
128  mSentBytes = 0;
129 
130  if( !mFolder ) {
131  if( !mMsgList.isEmpty() ) {
132  mFolder = static_cast<KMFolderCachedImap*>(mMsgList.first()->storage());
133  }
134  }
135  assert( mFolder );
136  mAccount = mFolder->account();
137  assert( mAccount != 0 );
138  if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
139  // No connection to the IMAP server
140  kdDebug(5006) << "mAccount->makeConnection() failed" << endl;
141  mPassiveDestructor = true;
142  delete this;
143  return;
144  } else
145  mPassiveDestructor = false;
146 
147  // All necessary conditions have been met. Register this job
148  mAccount->mJobList.append(this);
149 
156  if ( mAccount->groupwareType() == KMAcctCachedImap::GroupwareScalix ) {
157  if ( !mAccount->sentCustomLoginCommand() ) {
158  TQByteArray packedArgs;
159  TQDataStream stream( packedArgs, IO_WriteOnly );
160 
161  const TQString command = TQString( "X-SCALIX-ID " );
162  const TQString argument = TQString( "(\"name\" \"Evolution\" \"version\" \"2.10.0\")" );
163 
164  stream << (int) 'X' << 'N' << command << argument;
165 
166  const KURL url = mAccount->getUrl();
167 
168  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
169  jd.items << mFolder->label(); // for the err msg
170  TDEIO::SimpleJob *simpleJob = TDEIO::special( url.url(), packedArgs, false );
171  TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
172  mAccount->insertJob(simpleJob, jd);
173 
174  mAccount->setSentCustomLoginCommand( true );
175  }
176  }
177 
178  switch( mType ) {
179  case tGetMessage: slotGetNextMessage(); break;
180  case tPutMessage: slotPutNextMessage(); break;
181  case tDeleteMessage: slotDeleteNextMessages(); break;
182  case tExpungeFolder: expungeFolder(); break;
183  case tAddSubfolders: slotAddNextSubfolder(); break;
184  case tDeleteFolders: slotDeleteNextFolder(); break;
185  case tCheckUidValidity: checkUidValidity(); break;
186  case tRenameFolder: renameFolder(mString); break;
187  case tListMessages: listMessages(); break;
188  default:
189  assert( 0 );
190  }
191 }
192 
193 void CachedImapJob::listMessages()
194 {
195  KURL url = mAccount->getUrl();
196  url.setPath( mFolder->imapPath() + ";UID=1:*;SECTION=FLAGS RFC822.SIZE");
197 
198  TDEIO::SimpleJob *job = TDEIO::get(url, false, false);
199  TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
200  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
201  jd.cancellable = true;
202  mAccount->insertJob( job, jd );
203  connect( job, TQ_SIGNAL( result(TDEIO::Job *) ),
204  this, TQ_SLOT( slotListMessagesResult( TDEIO::Job* ) ) );
205  // send the data directly for KMFolderCachedImap
206  connect( job, TQ_SIGNAL( data( TDEIO::Job*, const TQByteArray& ) ),
207  mFolder, TQ_SLOT( slotGetMessagesData( TDEIO::Job* , const TQByteArray& ) ) );
208 }
209 
210 void CachedImapJob::slotDeleteNextMessages( TDEIO::Job* job )
211 {
212  if (job) {
213  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
214  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
215  delete this;
216  return;
217  }
218 
219  if( job->error() ) {
220  mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
221  delete this;
222  return;
223  }
224  mAccount->removeJob(it);
225  }
226 
227  if( mFoldersOrMessages.isEmpty() ) {
228  // No more messages to delete
229  delete this;
230  return;
231  }
232 
233  TQString uids = mFoldersOrMessages.front(); mFoldersOrMessages.pop_front();
234 
235  KURL url = mAccount->getUrl();
236  url.setPath( mFolder->imapPath() +
237  TQString::fromLatin1(";UID=%1").arg(uids) );
238 
239  TDEIO::SimpleJob *simpleJob = TDEIO::file_delete( url, false );
240  TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
241  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
242  mAccount->insertJob( simpleJob, jd );
243  connect( simpleJob, TQ_SIGNAL( result(TDEIO::Job *) ),
244  this, TQ_SLOT( slotDeleteNextMessages(TDEIO::Job *) ) );
245 }
246 
247 void CachedImapJob::expungeFolder()
248 {
249  KURL url = mAccount->getUrl();
250  // Special URL that means EXPUNGE
251  url.setPath( mFolder->imapPath() + TQString::fromLatin1(";UID=*") );
252 
253  TDEIO::SimpleJob *job = TDEIO::file_delete( url, false );
254  TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
255  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
256  mAccount->insertJob( job, jd );
257  connect( job, TQ_SIGNAL( result(TDEIO::Job *) ),
258  this, TQ_SLOT( slotExpungeResult(TDEIO::Job *) ) );
259 }
260 
261 void CachedImapJob::slotExpungeResult( TDEIO::Job * job )
262 {
263  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
264  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
265  delete this;
266  return;
267  }
268 
269  if (job->error()) {
270  mErrorCode = job->error();
271  mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
272  }
273  else
274  mAccount->removeJob(it);
275 
276  delete this;
277 }
278 
279 void CachedImapJob::slotGetNextMessage(TDEIO::Job * job)
280 {
281  if (job) {
282  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
283  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
284  delete this;
285  return;
286  }
287 
288  if (job->error()) {
289  mErrorCode = job->error();
290  mAccount->handleJobError( job, i18n( "Error while retrieving message on the server: " ) + '\n' );
291  delete this;
292  return;
293  }
294 
295  ulong size = 0;
296  if ((*it).data.size() > 0) {
297  ulong uid = mMsg->UID();
298  size = mMsg->msgSizeServer();
299 
300  // Convert CR/LF to LF.
301  size_t dataSize = (*it).data.size();
302  dataSize = Util::crlf2lf( (*it).data.data(), dataSize ); // always <=
303  (*it).data.resize( dataSize );
304 
305  mMsg->setComplete( true );
306  mMsg->fromByteArray( (*it).data );
307  mMsg->setUID(uid);
308  mMsg->setMsgSizeServer(size);
309  mMsg->setTransferInProgress( false );
310  int index = 0;
311  mFolder->addMsgInternal( mMsg, true, &index );
312 
313  if ( kmkernel->iCalIface().isResourceFolder( mFolder->folder() ) ) {
314  mFolder->setStatus( index, KMMsgStatusRead, false );
315  }
316 
317  emit messageRetrieved( mMsg );
318  if ( index > 0 ) mFolder->unGetMsg( index );
319  } else {
320  emit messageRetrieved( 0 );
321  }
322  mMsg = 0;
323 
324  mSentBytes += size;
325  emit progress( mSentBytes, mTotalBytes );
326  mAccount->removeJob(it);
327  } else
328  mFolder->quiet( true );
329 
330  if( mMsgsForDownload.isEmpty() ) {
331  mFolder->quiet( false );
332  delete this;
333  return;
334  }
335 
336  MsgForDownload mfd = mMsgsForDownload.front(); mMsgsForDownload.pop_front();
337 
338  mMsg = new KMMessage;
339  mMsg->setUID(mfd.uid);
340  mMsg->setMsgSizeServer(mfd.size);
341  if( mfd.flags > 0 )
342  KMFolderImap::flagsToStatus(mMsg, mfd.flags, true, GlobalSettings::allowLocalFlags() ? mFolder->permanentFlags() : INT_MAX);
343  KURL url = mAccount->getUrl();
344  url.setPath(mFolder->imapPath() + TQString(";UID=%1;SECTION=BODY.PEEK[]").arg(mfd.uid));
345 
346  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
347  jd.cancellable = true;
348  mMsg->setTransferInProgress(true);
349  TDEIO::SimpleJob *simpleJob = TDEIO::get(url, false, false);
350  TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
351  mAccount->insertJob(simpleJob, jd);
352  connect(simpleJob, TQ_SIGNAL(processedSize(TDEIO::Job *, TDEIO::filesize_t)),
353  this, TQ_SLOT(slotProcessedSize(TDEIO::Job *, TDEIO::filesize_t)));
354  connect(simpleJob, TQ_SIGNAL(result(TDEIO::Job *)),
355  this, TQ_SLOT(slotGetNextMessage(TDEIO::Job *)));
356  connect(simpleJob, TQ_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
357  mFolder, TQ_SLOT(slotSimpleData(TDEIO::Job *, const TQByteArray &)));
358 }
359 
360 void CachedImapJob::slotProcessedSize(TDEIO::Job *, TDEIO::filesize_t processed)
361 {
362  emit progress( mSentBytes + processed, mTotalBytes );
363 }
364 
365 void CachedImapJob::slotPutNextMessage()
366 {
367  mMsg = 0;
368 
369  // First try the message list
370  if( !mMsgList.isEmpty() ) {
371  mMsg = mMsgList.first();
372  mMsgList.removeFirst();
373  }
374 
375  // Now try the serial number list
376  while( mMsg == 0 && !mSerNumMsgList.isEmpty() ) {
377  unsigned long serNum = mSerNumMsgList.first();
378  mSerNumMsgList.pop_front();
379 
380  // Find the message with this serial number
381  int i = 0;
382  KMFolder* aFolder = 0;
383  KMMsgDict::instance()->getLocation( serNum, &aFolder, &i );
384  if( mFolder->folder() != aFolder )
385  // This message was moved or something
386  continue;
387  mMsg = mFolder->getMsg( i );
388  }
389 
390  if( !mMsg ) {
391  // No message found for upload
392  delete this;
393  return;
394  }
395 
396  KURL url = mAccount->getUrl();
397  TQString flags = KMFolderImap::statusToFlags( mMsg->status(), mFolder->permanentFlags() );
398  url.setPath( mFolder->imapPath() + ";SECTION=" + flags );
399 
400  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
401 
402  mMsg->setUID( 0 ); // for the index
403  TQCString cstr(mMsg->asString());
404  int a = cstr.find("\nX-UID: ");
405  int b = cstr.find('\n', a);
406  if (a != -1 && b != -1 && cstr.find("\n\n") > a) cstr.remove(a, b-a);
407  TQCString mData(cstr.length() + cstr.contains('\n'));
408  unsigned int i = 0;
409  for( char *ch = cstr.data(); *ch; ch++ ) {
410  if ( *ch == '\n' ) {
411  mData.at(i) = '\r';
412  i++;
413  }
414  mData.at(i) = *ch; i++;
415  }
416  jd.data = mData;
417  jd.msgList.append( mMsg );
418 
419  mMsg->setTransferInProgress(true);
420  TDEIO::SimpleJob *simpleJob = TDEIO::put(url, 0, false, false, false);
421  TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
422  mAccount->insertJob(simpleJob, jd);
423  connect( simpleJob, TQ_SIGNAL( result(TDEIO::Job *) ),
424  TQ_SLOT( slotPutMessageResult(TDEIO::Job *) ) );
425  connect( simpleJob, TQ_SIGNAL( dataReq(TDEIO::Job *, TQByteArray &) ),
426  TQ_SLOT( slotPutMessageDataReq(TDEIO::Job *, TQByteArray &) ) );
427  connect( simpleJob, TQ_SIGNAL( data(TDEIO::Job *, const TQByteArray &) ),
428  mFolder, TQ_SLOT( slotSimpleData(TDEIO::Job *, const TQByteArray &) ) );
429  connect( simpleJob, TQ_SIGNAL(infoMessage(TDEIO::Job *, const TQString &)),
430  TQ_SLOT(slotPutMessageInfoData(TDEIO::Job *, const TQString &)) );
431 
432 }
433 
434 //-----------------------------------------------------------------------------
435 // TODO: port to TDEIO::StoredTransferJob once it's ok to require tdelibs-3.3
436 void CachedImapJob::slotPutMessageDataReq(TDEIO::Job *job, TQByteArray &data)
437 {
438  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
439  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
440  delete this;
441  return;
442  }
443  if ((*it).data.size() - (*it).offset > 0x8000) {
444  data.duplicate((*it).data.data() + (*it).offset, 0x8000);
445  (*it).offset += 0x8000;
446  } else if ((*it).data.size() - (*it).offset > 0) {
447  data.duplicate((*it).data.data() + (*it).offset,
448  (*it).data.size() - (*it).offset);
449  (*it).offset = (*it).data.size();
450  } else
451  data.resize(0);
452 }
453 
454 //----------------------------------------------------------------------------
455 void CachedImapJob::slotPutMessageInfoData( TDEIO::Job *job, const TQString &data )
456 {
457  KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( mDestFolder->storage() );
458  if ( imapFolder ) {
459  KMAcctCachedImap *account = imapFolder->account();
460  ImapAccountBase::JobIterator it = account->findJob( job );
461  if ( it == account->jobsEnd() ) {
462  return;
463  }
464 
465  if ( data.find( "UID" ) != -1 && mMsg ) {
466  int uid = ( data.right( data.length() - 4 ) ).toInt();
467  kdDebug( 5006 ) << k_funcinfo << "Server told us uid is: " << uid << endl;
468  mMsg->setUID( uid );
469  }
470  }
471 }
472 
473 
474 //-----------------------------------------------------------------------------
475 void CachedImapJob::slotPutMessageResult(TDEIO::Job *job)
476 {
477  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
478  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
479  delete this;
480  return;
481  }
482 
483  if ( job->error() ) {
484  bool cont = mAccount->handlePutError( job, *it, mFolder->folder() );
485  if ( !cont ) {
486  delete this;
487  } else {
488  mMsg = 0;
489  slotPutNextMessage();
490  }
491  return;
492  }
493 
494  emit messageStored( mMsg );
495 
496  // we abuse those fields, the unit is the number of messages, here
497  ++mSentBytes;
498  emit progress( mSentBytes, mTotalBytes );
499 
500  int i;
501  if( ( i = mFolder->find(mMsg) ) != -1 ) {
502  /*
503  * If we have acquired a uid during upload the server supports the uidnext
504  * extension and there is no need to redownload this mail, we already have
505  * it. Otherwise remove it, it will be redownloaded.
506  */
507  if ( mMsg->UID() == 0 ) {
508  mFolder->removeMsg(i);
509  } else {
510  // When removing+readding, no point in telling the imap resources about it
511  bool b = kmkernel->iCalIface().isResourceQuiet();
512  kmkernel->iCalIface().setResourceQuiet( true );
513 
514  mFolder->takeTemporarily( i );
515  mFolder->addMsgKeepUID( mMsg );
516  mMsg->setTransferInProgress( false );
517 
518  kmkernel->iCalIface().setResourceQuiet( b );
519  }
520  }
521  mMsg = NULL;
522  mAccount->removeJob( it );
523  slotPutNextMessage();
524 }
525 
526 
527 void CachedImapJob::slotAddNextSubfolder( TDEIO::Job * job )
528 {
529  if (job) {
530  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
531  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
532  delete this;
533  return;
534  }
535 
536  // make copy of setting, to reset it before potentially destroying 'it'
537  bool silentUpload = static_cast<KMFolderCachedImap*>((*it).parent->storage())->silentUpload();
538  static_cast<KMFolderCachedImap*>((*it).parent->storage())->setSilentUpload( false );
539 
540  if ( job->error() && !silentUpload ) {
541  TQString myError = "<p><b>" + i18n("Error while uploading folder")
542  + "</b></p><p>" + i18n("Could not make the folder <b>%1</b> on the server.").arg((*it).items[0])
543  + "</p><p>" + i18n("This could be because you do not have permission to do this, or because the folder is already present on the server; the error message from the server communication is here:") + "</p>";
544  mAccount->handleJobError( job, myError );
545  }
546 
547  if( job->error() ) {
548  delete this;
549  return;
550  } else {
551  KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( (*it).current->storage() );
552  KMFolderCachedImap* parentStorage = static_cast<KMFolderCachedImap*>( (*it).parent->storage() );
553  Q_ASSERT( storage );
554  Q_ASSERT( parentStorage );
555  if ( storage->imapPath().isEmpty() ) {
556  TQString path = mAccount->createImapPath( parentStorage->imapPath(), storage->folder()->name() );
557  if ( !storage->imapPathForCreation().isEmpty() )
558  path = storage->imapPathForCreation();
559  storage->setImapPath( path );
560  storage->writeConfig();
561  }
562  }
563  mAccount->removeJob( it );
564  }
565 
566  if (mFolderList.isEmpty()) {
567  // No more folders to add
568  delete this;
569  return;
570  }
571 
572  KMFolderCachedImap *folder = mFolderList.front();
573  mFolderList.pop_front();
574  KURL url = mAccount->getUrl();
575  TQString path = mAccount->createImapPath( mFolder->imapPath(),
576  folder->folder()->name() );
577  if ( !folder->imapPathForCreation().isEmpty() ) {
578  // the folder knows it's namespace
579  path = folder->imapPathForCreation();
580  }
581  url.setPath( path );
582 
583  if ( mAccount->groupwareType() != KMAcctCachedImap::GroupwareScalix ) {
584  // Associate the jobData with the parent folder, not with the child
585  // This is necessary in case of an error while creating the subfolder,
586  // so that folderComplete is called on the parent (and the sync resetted).
587  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
588  jd.items << folder->label(); // for the err msg
589  jd.current = folder->folder();
590  TDEIO::SimpleJob *simpleJob = TDEIO::mkdir(url);
591  TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
592  mAccount->insertJob(simpleJob, jd);
593  connect( simpleJob, TQ_SIGNAL(result(TDEIO::Job *)),
594  this, TQ_SLOT(slotAddNextSubfolder(TDEIO::Job *)) );
595  } else {
596  TQByteArray packedArgs;
597  TQDataStream stream( packedArgs, IO_WriteOnly );
598 
599  const TQString command = TQString( "X-CREATE-SPECIAL" );
600  const TQString argument = TQString( "%1 %2" ).arg( Scalix::Utils::contentsTypeToScalixId( folder->contentsType() ) )
601  .arg( path );
602 
603  stream << (int) 'X' << 'N' << command << argument;
604 
605  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
606  jd.items << folder->label(); // for the err msg
607  jd.current = folder->folder();
608  TDEIO::SimpleJob *simpleJob = TDEIO::special( url.url(), packedArgs, false );
609  TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
610  mAccount->insertJob(simpleJob, jd);
611  connect( simpleJob, TQ_SIGNAL(result(TDEIO::Job *)),
612  this, TQ_SLOT(slotAddNextSubfolder(TDEIO::Job *)) );
613  }
614 }
615 
616 
617 void CachedImapJob::slotDeleteNextFolder( TDEIO::Job *job )
618 {
619  if (job) {
620  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
621  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
622  delete this;
623  return;
624  }
625 
626  mAccount->removeDeletedFolder( (*it).path );
627 
628  if( job->error() ) {
629  mAccount->handleJobError( job, i18n( "Error while deleting folder %1 on the server: " ).arg( (*it).path ) + '\n' );
630  delete this;
631  return;
632  }
633  mAccount->removeJob(it);
634  }
635 
636  if( mFoldersOrMessages.isEmpty() ) {
637  // No more folders to delete
638  delete this;
639  return;
640  }
641 
642  TQString folderPath = mFoldersOrMessages.front();
643  mFoldersOrMessages.pop_front();
644  KURL url = mAccount->getUrl();
645  url.setPath(folderPath);
646  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
647  jd.path = url.path();
648  TDEIO::SimpleJob *simpleJob = TDEIO::file_delete(url, false);
649  TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
650  mAccount->insertJob(simpleJob, jd);
651  connect( simpleJob, TQ_SIGNAL( result(TDEIO::Job *) ),
652  TQ_SLOT( slotDeleteNextFolder(TDEIO::Job *) ) );
653 }
654 
655 void CachedImapJob::checkUidValidity()
656 {
657  KURL url = mAccount->getUrl();
658  url.setPath( mFolder->imapPath() + ";UID=0:0" );
659 
660  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
661  jd.cancellable = true;
662 
663  TDEIO::SimpleJob *job = TDEIO::get( url, false, false );
664  TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
665  mAccount->insertJob( job, jd );
666  connect( job, TQ_SIGNAL(result(TDEIO::Job *)),
667  TQ_SLOT(slotCheckUidValidityResult(TDEIO::Job *)) );
668  connect( job, TQ_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
669  mFolder, TQ_SLOT(slotSimpleData(TDEIO::Job *, const TQByteArray &)));
670 }
671 
672 void CachedImapJob::slotCheckUidValidityResult(TDEIO::Job * job)
673 {
674  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
675  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
676  delete this;
677  return;
678  }
679 
680  if( job->error() ) {
681  mErrorCode = job->error();
682  mAccount->handleJobError( job, i18n( "Error while reading folder %1 on the server: " ).arg( (*it).parent->label() ) + '\n' );
683  delete this;
684  return;
685  }
686 
687  // Check the uidValidity
688  TQCString cstr((*it).data.data(), (*it).data.size() + 1);
689  int a = cstr.find("X-uidValidity: ");
690  if (a < 0) {
691  // Something is seriously rotten here!
692  // TODO: Tell the user that he has a problem
693  kdDebug(5006) << "No uidvalidity available for folder "
694  << mFolder->name() << endl;
695  }
696  else {
697  int b = cstr.find("\r\n", a);
698  if ( (b - a - 15) >= 0 ) {
699  TQString uidv = cstr.mid(a + 15, b - a - 15);
700  // kdDebug(5006) << "New uidv = " << uidv << ", old uidv = "
701  // << mFolder->uidValidity() << endl;
702  if( !mFolder->uidValidity().isEmpty() && mFolder->uidValidity() != uidv ) {
703  // kdDebug(5006) << "Expunging the mailbox " << mFolder->name()
704  // << "!" << endl;
705  mFolder->expunge();
706  mFolder->setLastUid( 0 );
707  mFolder->clearUidMap();
708  }
709  } else
710  kdDebug(5006) << "No uidvalidity available for folder "
711  << mFolder->name() << endl;
712  }
713 
714  a = cstr.find( "X-PermanentFlags: " );
715  if ( a < 0 ) {
716  kdDebug(5006) << "no PERMANENTFLAGS response? assumming custom flags are not available" << endl;
717  } else {
718  int b = cstr.find( "\r\n", a );
719  if ( (b - a - 18) >= 0 ) {
720  int flags = cstr.mid( a + 18, b - a - 18 ).toInt();
721  emit permanentFlags( flags );
722  } else {
723  kdDebug(5006) << "PERMANENTFLAGS response broken, assumming custom flags are not available" << endl;
724  }
725  }
726 
727  mAccount->removeJob(it);
728  delete this;
729 }
730 
731 
732 void CachedImapJob::renameFolder( const TQString &newName )
733 {
734  mNewName = newName;
735 
736  // Set the source URL
737  KURL urlSrc = mAccount->getUrl();
738  mOldImapPath = mFolder->imapPath();
739  urlSrc.setPath( mOldImapPath );
740 
741  // Set the destination URL - this is a bit trickier
742  KURL urlDst = mAccount->getUrl();
743  mNewImapPath = mFolder->imapPath();
744  // Destination url = old imappath - oldname + new name
745  mNewImapPath.truncate( mNewImapPath.length() - mFolder->folder()->name().length() - 1);
746  mNewImapPath += newName + '/';
747  urlDst.setPath( mNewImapPath );
748 
749  ImapAccountBase::jobData jd( newName, mFolder->folder() );
750  jd.path = mNewImapPath;
751 
752  TDEIO::SimpleJob *simpleJob = TDEIO::rename( urlSrc, urlDst, false );
753  TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
754  mAccount->insertJob( simpleJob, jd );
755  connect( simpleJob, TQ_SIGNAL(result(TDEIO::Job *)),
756  TQ_SLOT(slotRenameFolderResult(TDEIO::Job *)) );
757 }
758 
759 static void renameChildFolders( KMFolderDir* dir, const TQString& oldPath,
760  const TQString& newPath )
761 {
762  if( dir ) {
763  KMFolderNode *node = dir->first();
764  while( node ) {
765  if( !node->isDir() ) {
766  KMFolderCachedImap* imapFolder =
767  static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
768  if ( !imapFolder->imapPath().isEmpty() )
769  // Only rename folders that have been accepted by the server
770  if( imapFolder->imapPath().find( oldPath ) == 0 ) {
771  TQString p = imapFolder->imapPath();
772  p = p.mid( oldPath.length() );
773  p.prepend( newPath );
774  imapFolder->setImapPath( p );
775  renameChildFolders( imapFolder->folder()->child(), oldPath, newPath );
776  }
777  }
778  node = dir->next();
779  }
780  }
781 }
782 
783 void CachedImapJob::revertLabelChange()
784 {
785  TQMap<TQString, KMAcctCachedImap::RenamedFolder>::ConstIterator renit = mAccount->renamedFolders().find( mFolder->imapPath() );
786  Q_ASSERT( renit != mAccount->renamedFolders().end() );
787  if ( renit != mAccount->renamedFolders().end() ) {
788  mFolder->folder()->setLabel( (*renit).mOldLabel );
789  mAccount->removeRenamedFolder( mFolder->imapPath() );
790  kmkernel->dimapFolderMgr()->contentsChanged();
791  }
792 }
793 
794 void CachedImapJob::renameOnDisk()
795 {
796  TQString oldName = mFolder->name();
797  TQString oldPath = mFolder->imapPath();
798  mAccount->removeRenamedFolder( oldPath );
799  mFolder->setImapPath( mNewImapPath );
800  mFolder->FolderStorage::rename( mNewName );
801 
802  if( oldPath.endsWith( "/" ) ) oldPath.truncate( oldPath.length() -1 );
803  TQString newPath = mFolder->imapPath();
804  if( newPath.endsWith( "/" ) ) newPath.truncate( newPath.length() -1 );
805  renameChildFolders( mFolder->folder()->child(), oldPath, newPath );
806  kmkernel->dimapFolderMgr()->contentsChanged();
807 }
808 
809 void CachedImapJob::slotSubscribtionChange1Failed( const TQString &errorMessage )
810 {
811  KMessageBox::sorry( 0, i18n( "Error while trying to subscribe to the renamed folder %1.\n"
812  "Renaming itself was successful, but the renamed folder might disappear "
813  "from the folder list after the next sync since it is unsubscribed on the server.\n"
814  "You can try to manually subscribe to the folder yourself.\n\n"
815  "%2" )
816  .arg( mFolder->label() ).arg( errorMessage ) );
817  delete this;
818 }
819 
820 void CachedImapJob::slotSubscribtionChange2Failed( const TQString &errorMessage )
821 {
822  kdWarning(5006) << k_funcinfo << errorMessage << endl;
823  // Ignore this error, not something user-visible anyway
824  delete this;
825 }
826 
827 void CachedImapJob::slotSubscribtionChange1Done( const TQString&, bool )
828 {
829  disconnect( mAccount, TQ_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
830  this, TQ_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
831  connect( mAccount, TQ_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
832  this, TQ_SLOT( slotSubscribtionChange2Done( const TQString&, bool ) ) );
833  disconnect( mAccount, TQ_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
834  this, TQ_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
835  connect( mAccount, TQ_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
836  this, TQ_SLOT( slotSubscribtionChange2Failed( const TQString& ) ) );
837 
838  mAccount->changeSubscription( false, mOldImapPath, true /* quiet */ );
839 }
840 
841 void CachedImapJob::slotSubscribtionChange2Done( const TQString&, bool )
842 {
843  // Finally done with everything!
844  delete this;
845 }
846 
847 void CachedImapJob::slotRenameFolderResult( TDEIO::Job *job )
848 {
849  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
850  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
851  delete this;
852  return;
853  }
854 
855  if( job->error() ) {
856  revertLabelChange();
857  const TQString errorMessage = i18n( "Error while trying to rename folder %1" ).arg( mFolder->label() );
858  mAccount->handleJobError( job, errorMessage );
859  delete this;
860  } else {
861 
862  mAccount->removeJob( it );
863  renameOnDisk();
864 
865  // Okay, the folder seems to be renamed on the server and on disk.
866  // Now unsubscribe from the old folder name and subscribe to the new folder name,
867  // so that the folder doesn't suddenly disappear after renaming it
868  connect( mAccount, TQ_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
869  this, TQ_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
870  connect( mAccount, TQ_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
871  this, TQ_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
872  mAccount->changeSubscription( true, mNewImapPath, true /* quiet */ );
873  }
874 }
875 
876 void CachedImapJob::slotListMessagesResult( TDEIO::Job * job )
877 {
878  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
879  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
880  delete this;
881  return;
882  }
883 
884  if (job->error()) {
885  mErrorCode = job->error();
886  mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
887  }
888  else
889  mAccount->removeJob(it);
890 
891  delete this;
892 }
893 
894 //-----------------------------------------------------------------------------
895 void CachedImapJob::setParentFolder( const KMFolderCachedImap* parent )
896 {
897  mParentFolder = const_cast<KMFolderCachedImap*>( parent );
898 }
899 
900 }
901 
902 #include "cachedimapjob.moc"
KMail list that manages the contents of one directory that may contain folders and/or other directori...
Definition: kmfolderdir.h:16
Mail folder.
Definition: kmfolder.h:69
This is a Mime Message.
Definition: kmmessage.h:68
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
Definition: kmmsgdict.cpp:319
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167
folderdiaquotatab.h
Definition: aboutdata.cpp:40