kmail

kmailicalifaceimpl.cpp

00001 /*
00002     This file is part of KMail.
00003 
00004     Copyright (c) 2003 Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
00005     Copyright (c) 2003 - 2004 Bo Thorsen <bo@sonofthor.dk>
00006     Copyright (c) 2004 Till Adam <adam@kde.org>
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021     Boston, MA 02110-1301, USA.
00022 
00023     In addition, as a special exception, the copyright holders give
00024     permission to link the code of this program with any edition of
00025     the Qt library by Trolltech AS, Norway (or with modified versions
00026     of Qt that use the same license as Qt), and distribute linked
00027     combinations including the two.  You must obey the GNU General
00028     Public License in all respects for all of the code used other than
00029     Qt.  If you modify this file, you may extend this exception to
00030     your version of the file, but you are not obligated to do so.  If
00031     you do not wish to do so, delete this exception statement from
00032     your version.
00033 */
00034 
00035 #ifdef HAVE_CONFIG_H
00036 #include <config.h>
00037 #endif
00038 
00039 #include "kmailicalifaceimpl.h"
00040 #include "kmfolder.h"
00041 #include "kmfoldertree.h"
00042 #include "kmfolderdir.h"
00043 #include "kmgroupware.h"
00044 #include "kmfoldermgr.h"
00045 #include "kmcommands.h"
00046 #include "kmfolderindex.h"
00047 #include "kmmsgdict.h"
00048 #include "kmmsgpart.h"
00049 using KMail::AccountManager;
00050 #include "kmfolderimap.h"
00051 #include "globalsettings.h"
00052 #include "accountmanager.h"
00053 #include "kmfoldercachedimap.h"
00054 #include "kmacctcachedimap.h"
00055 #include "acljobs.h"
00056 
00057 #include "scalix.h"
00058 
00059 #include <mimelib/enum.h>
00060 #include <mimelib/utility.h>
00061 #include <mimelib/body.h>
00062 #include <mimelib/mimepp.h>
00063 
00064 #include <qfile.h>
00065 #include <qmap.h>
00066 #include <qtextcodec.h>
00067 
00068 #include <kdebug.h>
00069 #include <kiconloader.h>
00070 #include <kinputdialog.h>
00071 #include <dcopclient.h>
00072 #include <kmessagebox.h>
00073 #include <kconfig.h>
00074 #include <kurl.h>
00075 #include <ktempfile.h>
00076 
00077 using namespace KMail;
00078 
00079 QMap<QString, QString> *KMailICalIfaceImpl::mSubResourceUINamesMap = new QMap<QString, QString>;
00080 
00081 // Local helper methods
00082 static void vPartMicroParser( const QString& str, QString& s );
00083 static void reloadFolderTree();
00084 
00085 // The index in this array is the KMail::FolderContentsType enum
00086 static const struct {
00087   const char* contentsTypeStr; // the string used in the DCOP interface
00088   const char* mimetype;
00089   KFolderTreeItem::Type treeItemType;
00090   const char* annotation;
00091   const char* translatedName;
00092 } s_folderContentsType[] = {
00093   { "Mail", "application/x-vnd.kolab.mail", KFolderTreeItem::Other, "mail", I18N_NOOP( "Mail" ) },
00094   { "Calendar", "application/x-vnd.kolab.event", KFolderTreeItem::Calendar, "event", I18N_NOOP( "Calendar" ) },
00095   { "Contact", "application/x-vnd.kolab.contact", KFolderTreeItem::Contacts, "contact", I18N_NOOP( "Contacts" ) },
00096   { "Note", "application/x-vnd.kolab.note", KFolderTreeItem::Notes, "note", I18N_NOOP( "Notes" ) },
00097   { "Task", "application/x-vnd.kolab.task", KFolderTreeItem::Tasks, "task", I18N_NOOP( "Tasks" ) },
00098   { "Journal", "application/x-vnd.kolab.journal", KFolderTreeItem::Journals, "journal", I18N_NOOP( "Journal" ) }
00099 };
00100 
00101 static QString folderContentsType( KMail::FolderContentsType type )
00102 {
00103   return s_folderContentsType[type].contentsTypeStr;
00104 }
00105 
00106 static QString folderKolabMimeType( KMail::FolderContentsType type )
00107 {
00108   return s_folderContentsType[type].mimetype;
00109 }
00110 
00111 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::globalStorageFormat() const {
00112   return GlobalSettings::self()->theIMAPResourceStorageFormat()
00113     == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard;
00114 }
00115 
00116 static KMail::FolderContentsType folderContentsType( const QString& type )
00117 {
00118   for ( uint i = 0 ; i < sizeof s_folderContentsType / sizeof *s_folderContentsType; ++i )
00119     if ( type == s_folderContentsType[i].contentsTypeStr )
00120       return static_cast<KMail::FolderContentsType>( i );
00121   return KMail::ContentsTypeMail;
00122 }
00123 
00124 static QString localizedDefaultFolderName( KMail::FolderContentsType type )
00125 {
00126   return i18n( s_folderContentsType[type].translatedName );
00127 }
00128 
00129 const char* KMailICalIfaceImpl::annotationForContentsType( KMail::FolderContentsType type )
00130 {
00131   return s_folderContentsType[type].annotation;
00132 }
00133 
00134 ExtraFolder::ExtraFolder( KMFolder* f )
00135     : folder( f )
00136 {
00137     folder->open("kmailicaliface::extrafolder");
00138 }
00139 
00140 ExtraFolder::~ExtraFolder()
00141 {
00142     if ( folder )
00143         folder->close("kmailicaliface::extrafolder");
00144 }
00145 
00146 
00147 /*
00148   This interface has three parts to it - libkcal interface;
00149   kmail interface; and helper functions.
00150 
00151   The libkcal interface and the kmail interface have the same three
00152   methods: add, delete and refresh. The only difference is that the
00153   libkcal interface is used from the IMAP resource in libkcal and
00154   the kmail interface is used from the groupware object in kmail.
00155 */
00156 
00157 KMailICalIfaceImpl::KMailICalIfaceImpl()
00158   : DCOPObject( "KMailICalIface" ), QObject( 0, "KMailICalIfaceImpl" ),
00159     mContacts( 0 ), mCalendar( 0 ), mNotes( 0 ), mTasks( 0 ), mJournals( 0 ),
00160     mFolderLanguage( 0 ), mFolderParentDir( 0 ), mFolderType( KMFolderTypeUnknown ),
00161     mUseResourceIMAP( false ), mResourceQuiet( false ), mHideFolders( true )
00162 {
00163   // Listen to config changes
00164   connect( kmkernel, SIGNAL( configChanged() ), this, SLOT( readConfig() ) );
00165   connect( kmkernel, SIGNAL( folderRemoved( KMFolder* ) ),
00166            this, SLOT( slotFolderRemoved( KMFolder* ) ) );
00167 
00168   mExtraFolders.setAutoDelete( true );
00169   mAccumulators.setAutoDelete( true );
00170 }
00171 
00172 
00173 /* libkcal part of the interface, called from the resources using this
00174  * when incidences are added or deleted */
00175 
00176 // Helper function to find an attachment of a given mimetype
00177 // Can't use KMMessage::findDwBodyPart since it only works with known mimetypes.
00178 static DwBodyPart* findBodyPartByMimeType( const KMMessage& msg, const char* sType, const char* sSubtype, bool startsWith = false )
00179 {
00180   // quickly searching for our message part: since Kolab parts are
00181   // top-level parts we do *not* have to travel into embedded multiparts
00182   DwBodyPart* part = msg.getFirstDwBodyPart();
00183   while( part ){
00184   //    kdDebug() << part->Headers().ContentType().TypeStr().c_str() << " "
00185   //            << part->Headers().ContentType().SubtypeStr().c_str() << endl;
00186     if ( part->hasHeaders() ) {
00187       DwMediaType& contentType = part->Headers().ContentType();
00188       if ( startsWith ) {
00189         if ( contentType.TypeStr() == sType
00190              && QString( contentType.SubtypeStr().c_str() ).startsWith( sSubtype ) )
00191           return part;
00192       }
00193       else
00194         if ( contentType.TypeStr() == sType
00195              && contentType.SubtypeStr() == sSubtype )
00196           return part;
00197     }
00198     part = part->Next();
00199   }
00200   return 0;
00201 }
00202 
00203 // Helper function to find an attachment with a given filename
00204 static DwBodyPart* findBodyPart( const KMMessage& msg, const QString& attachmentName )
00205 {
00206   // quickly searching for our message part: since Kolab parts are
00207   // top-level parts we do *not* have to travel into embedded multiparts
00208   for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) {
00209     //kdDebug(5006) << "findBodyPart:  - " << part->Headers().ContentDisposition().Filename().c_str() << endl;
00210     if ( part->hasHeaders()
00211          && attachmentName == part->Headers().ContentDisposition().Filename().c_str() )
00212       return part;
00213     if ( part->hasHeaders() && attachmentName == part->Headers().ContentType().Name().c_str() )
00214       return part;
00215   }
00216   return 0;
00217 }
00218 
00219 #if 0
00220 static void debugBodyParts( const char* foo, const KMMessage& msg )
00221 {
00222   kdDebug(5006) << "--debugBodyParts " << foo << "--" << endl;
00223   for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) {
00224     if ( part->hasHeaders() ) {
00225       kdDebug(5006) << " bodypart: " << part << endl;
00226       kdDebug(5006) << "        " << part->Headers().AsString().c_str() << endl;
00227     }
00228     else
00229       kdDebug(5006) << " part " << part << " has no headers" << endl;
00230   }
00231 }
00232 #else
00233 inline static void debugBodyParts( const char*, const KMMessage& ) {}
00234 #endif
00235 
00236 
00237 // Add (or overwrite, resp.) an attachment in an existing mail,
00238 // attachments must be local files, they are identified by their names.
00239 // If lookupByName if false the attachment to replace is looked up by mimetype.
00240 // return value: wrong if attachment could not be added/updated
00241 bool KMailICalIfaceImpl::updateAttachment( KMMessage& msg,
00242                                            const QString& attachmentURL,
00243                                            const QString& attachmentName,
00244                                            const QString& attachmentMimetype,
00245                                            bool lookupByName )
00246 {
00247   kdDebug(5006) << "KMailICalIfaceImpl::updateAttachment( " << attachmentURL << " )" << endl;
00248 
00249   bool bOK = false;
00250 
00251   KURL url( attachmentURL );
00252   if ( url.isValid() && url.isLocalFile() ) {
00253     const QString fileName( url.path() );
00254     QFile file( fileName );
00255     if( file.open( IO_ReadOnly ) ) {
00256       QByteArray rawData = file.readAll();
00257       file.close();
00258 
00259       // create the new message part with data read from temp file
00260       KMMessagePart msgPart;
00261       msgPart.setName( attachmentName );
00262 
00263       const int iSlash = attachmentMimetype.find('/');
00264       const QCString sType    = attachmentMimetype.left( iSlash   ).latin1();
00265       const QCString sSubtype = attachmentMimetype.mid(  iSlash+1 ).latin1();
00266       msgPart.setTypeStr( sType );
00267       msgPart.setSubtypeStr( sSubtype );
00268       QCString ctd("attachment;\n  filename=\"");
00269       ctd.append( attachmentName.latin1() );
00270       ctd.append("\"");
00271       msgPart.setContentDisposition( ctd );
00272       QValueList<int> dummy;
00273       msgPart.setBodyAndGuessCte( rawData, dummy );
00274       msgPart.setPartSpecifier( fileName );
00275 
00276       DwBodyPart* newPart = msg.createDWBodyPart( &msgPart );
00277       // This whole method is a bit special. We mix code for writing and code for reading.
00278       // E.g. we need to parse the content-disposition again for ContentDisposition().Filename()
00279       // to work later on.
00280       newPart->Headers().ContentDisposition().Parse();
00281 
00282       DwBodyPart* part = lookupByName ? findBodyPart( msg, attachmentName )
00283                          : findBodyPartByMimeType( msg, sType, sSubtype );
00284       if ( part ) {
00285         // Make sure the replacing body part is pointing
00286         // to the same next part as the original body part.
00287         newPart->SetNext( part->Next() );
00288         // call DwBodyPart::operator =
00289         // which calls DwEntity::operator =
00290         *part = *newPart;
00291         delete newPart;
00292         msg.setNeedsAssembly();
00293         kdDebug(5006) << "Attachment " << attachmentName << " updated." << endl;
00294       } else {
00295         msg.addDwBodyPart( newPart );
00296         kdDebug(5006) << "Attachment " << attachmentName << " added." << endl;
00297       }
00298       bOK = true;
00299     }else{
00300       kdDebug(5006) << "Attachment " << attachmentURL << " can not be read." << endl;
00301     }
00302   }else{
00303     kdDebug(5006) << "Attachment " << attachmentURL << " not a local file." << endl;
00304   }
00305 
00306   return bOK;
00307 }
00308 
00309 // Look for the attachment with the right mimetype
00310 bool KMailICalIfaceImpl::kolabXMLFoundAndDecoded( const KMMessage& msg, const QString& mimetype, QString& s )
00311 {
00312   const int iSlash = mimetype.find('/');
00313   const QCString sType    = mimetype.left( iSlash   ).latin1();
00314   const QCString sSubtype = mimetype.mid(  iSlash+1 ).latin1();
00315   DwBodyPart* part = findBodyPartByMimeType( msg, sType, sSubtype, true /* starts with sSubtype, to accept application/x-vnd.kolab.contact.distlist */ );
00316   if ( part ) {
00317     KMMessagePart msgPart;
00318     KMMessage::bodyPart(part, &msgPart);
00319     s = msgPart.bodyToUnicode( QTextCodec::codecForName( "utf8" ) );
00320     return true;
00321   }
00322   return false;
00323 }
00324 
00325 // Delete an attachment in an existing mail.
00326 // return value: wrong if attachment could not be deleted
00327 //
00328 // This code could be optimized: for now we just replace
00329 // the attachment by an empty dummy attachment since Mimelib
00330 // does not provide an option for deleting attachments yet.
00331 bool KMailICalIfaceImpl::deleteAttachment( KMMessage& msg,
00332                                            const QString& attachmentName )
00333 {
00334   kdDebug(5006) << "KMailICalIfaceImpl::deleteAttachment( " << attachmentName << " )" << endl;
00335 
00336   bool bOK = false;
00337 
00338   // quickly searching for our message part: since Kolab parts are
00339   // top-level parts we do *not* have to travel into embedded multiparts
00340   DwBodyPart* part = findBodyPart( msg, attachmentName );
00341   if ( part ) {
00342     msg.getTopLevelPart()->Body().RemoveBodyPart( part );
00343     delete part;
00344     msg.setNeedsAssembly();
00345     kdDebug(5006) << "Attachment deleted." << endl;
00346     bOK = true;
00347   }
00348 
00349   if( !bOK ){
00350     kdDebug(5006) << "Attachment " << attachmentName << " not found." << endl;
00351   }
00352 
00353   return bOK;
00354 }
00355 
00356 static void setIcalVcardContentTypeHeader( KMMessage *msg, KMail::FolderContentsType t, KMFolder *folder )
00357 {
00358   KMAcctCachedImap::GroupwareType groupwareType = KMAcctCachedImap::GroupwareKolab;
00359 
00360   KMFolderCachedImap *imapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
00361   if ( imapFolder )
00362     groupwareType = imapFolder->account()->groupwareType();
00363 
00364   msg->setType( DwMime::kTypeText );
00365   if ( t == KMail::ContentsTypeCalendar || t == KMail::ContentsTypeTask
00366       || t == KMail::ContentsTypeJournal ) {
00367     msg->setSubtype( DwMime::kSubtypeVCal );
00368 
00369     if ( groupwareType == KMAcctCachedImap::GroupwareKolab )
00370       msg->setHeaderField("Content-Type",
00371           "text/calendar; method=REQUEST; charset=\"utf-8\"");
00372     else if ( groupwareType == KMAcctCachedImap::GroupwareScalix )
00373       msg->setHeaderField("Content-Type",
00374           "text/calendar; method=PUBLISH; charset=\"UTF-8\"");
00375 
00376   } else if ( t == KMail::ContentsTypeContact ) {
00377     msg->setSubtype( DwMime::kSubtypeXVCard );
00378     if ( groupwareType == KMAcctCachedImap::GroupwareKolab )
00379       msg->setHeaderField( "Content-Type", "Text/X-VCard; charset=\"utf-8\"" );
00380     else if ( groupwareType == KMAcctCachedImap::GroupwareScalix )
00381       msg->setHeaderField( "Content-Type", "application/scalix-properties; charset=\"UTF-8\"" );
00382   } else {
00383     kdWarning(5006) << k_funcinfo << "Attempt to write non-groupware contents to folder" << endl;
00384   }
00385 }
00386 
00387 static void setXMLContentTypeHeader( KMMessage *msg, const QString plainTextBody )
00388 {
00389    // add a first body part to be displayed by all mailer
00390     // than can NOT display Kolab data: no matter if these
00391     // mailers are MIME compliant or not
00392     KMMessagePart firstPart;
00393     firstPart.setType( DwMime::kTypeText );
00394     firstPart.setSubtype( DwMime::kSubtypePlain );
00395     msg->removeHeaderField( "Content-Type" );
00396     msg->setType( DwMime::kTypeMultipart );
00397     msg->setSubtype( DwMime::kSubtypeMixed );
00398     msg->headers().ContentType().CreateBoundary( 0 );
00399     msg->headers().ContentType().Assemble();
00400     firstPart.setBodyFromUnicode( plainTextBody );
00401     msg->addBodyPart( &firstPart );
00402 }
00403 
00404 // Store a new entry that was received from the resource
00405 Q_UINT32 KMailICalIfaceImpl::addIncidenceKolab( KMFolder& folder,
00406                                                 const QString& subject,
00407                                                 const QString& plainTextBody,
00408                                                 const QMap<QCString, QString>& customHeaders,
00409                                                 const QStringList& attachmentURLs,
00410                                                 const QStringList& attachmentNames,
00411                                                 const QStringList& attachmentMimetypes )
00412 {
00413   kdDebug(5006) << "KMailICalIfaceImpl::addIncidenceKolab( " << attachmentNames << " )" << endl;
00414 
00415   Q_UINT32 sernum = 0;
00416   bool bAttachOK = true;
00417 
00418   // Make a new message for the incidence
00419   KMMessage* msg = new KMMessage();
00420   msg->initHeader();
00421   msg->setSubject( subject );
00422   msg->setAutomaticFields( true );
00423 
00424   QMap<QCString, QString>::ConstIterator ith = customHeaders.begin();
00425   const QMap<QCString, QString>::ConstIterator ithEnd = customHeaders.end();
00426   for ( ; ith != ithEnd ; ++ith ) {
00427     msg->setHeaderField( ith.key(), ith.data() );
00428   }
00429   // In case of the ical format, simply add the plain text content with the
00430   // right content type
00431   if ( storageFormat( &folder ) == StorageXML ) {
00432     setXMLContentTypeHeader( msg, plainTextBody );
00433   } else if ( storageFormat( &folder ) == StorageIcalVcard ) {
00434     const KMail::FolderContentsType t = folder.storage()->contentsType();
00435     setIcalVcardContentTypeHeader( msg, t, &folder );
00436     msg->setBodyEncoded( plainTextBody.utf8() );
00437   } else {
00438     kdWarning(5006) << k_funcinfo << "Attempt to write to folder with unknown storage type" << endl;
00439   }
00440 
00441   Q_ASSERT( attachmentMimetypes.count() == attachmentURLs.count() );
00442   Q_ASSERT( attachmentNames.count() == attachmentURLs.count() );
00443   // Add all attachments by reading them from their temp. files
00444   QStringList::ConstIterator itmime = attachmentMimetypes.begin();
00445   QStringList::ConstIterator iturl = attachmentURLs.begin();
00446   for( QStringList::ConstIterator itname = attachmentNames.begin();
00447        itname != attachmentNames.end()
00448        && itmime != attachmentMimetypes.end()
00449        && iturl != attachmentURLs.end();
00450        ++itname, ++iturl, ++itmime ){
00451     bool byname = !(*itmime).startsWith( "application/x-vnd.kolab." );
00452     if( !updateAttachment( *msg, *iturl, *itname, *itmime, byname ) ){
00453       kdWarning(5006) << "Attachment error, can not add Incidence." << endl;
00454       bAttachOK = false;
00455       break;
00456     }
00457   }
00458 
00459   if( bAttachOK ){
00460     // Mark the message as read and store it in the folder
00461     msg->cleanupHeader();
00462     //debugBodyParts( "after cleanup", *msg );
00463     msg->touch();
00464     if ( folder.addMsg( msg ) == 0 )
00465       // Message stored
00466       sernum = msg->getMsgSerNum();
00467     kdDebug(5006) << "addIncidenceKolab(): Message done and saved. Sernum: "
00468                   << sernum << endl;
00469 
00470     //debugBodyParts( "after addMsg", *msg );
00471     addFolderChange( &folder, Contents );
00472     syncFolder( &folder );
00473   } else
00474     kdError(5006) << "addIncidenceKolab(): Message *NOT* saved!\n";
00475 
00476   return sernum;
00477 }
00478 
00479 bool KMailICalIfaceImpl::deleteIncidenceKolab( const QString& resource,
00480                                                Q_UINT32 sernum )
00481 {
00482   // Find the message from the serial number and delete it.
00483   if( !mUseResourceIMAP )
00484     return false;
00485 
00486   kdDebug(5006) << "KMailICalIfaceImpl::deleteIncidenceKolab( "
00487                 << resource << ", " << sernum << ")\n";
00488 
00489   // Find the folder
00490   KMFolder* f = findResourceFolder( resource );
00491   if( !f ) {
00492     kdError(5006) << "deleteIncidenceKolab(" << resource << ") : Not an IMAP resource folder" << endl;
00493     return false;
00494   }
00495 
00496   bool rc = false;
00497 
00498   KMMessage* msg = findMessageBySerNum( sernum, f );
00499   if( msg ) {
00500     // Message found - delete it and return happy
00501     deleteMsg( msg );
00502     syncFolder( f );
00503     rc = true;
00504   } else {
00505     kdDebug(5006) << "Message not found, cannot remove serNum " << sernum << endl;
00506   }
00507   return rc;
00508 }
00509 
00510 
00511 int KMailICalIfaceImpl::incidencesKolabCount( const QString& mimetype,
00512                                               const QString& resource )
00513 {
00514   Q_UNUSED( mimetype ); // honouring that would be too slow...
00515 
00516   if( !mUseResourceIMAP )
00517     return 0;
00518 
00519   KMFolder* f = findResourceFolder( resource );
00520   if( !f ) {
00521     kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl;
00522     return 0;
00523   }
00524 
00525   f->open("kolabcount");
00526   int n = f->count();
00527   f->close("kolabcount");
00528   kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolabCount( "
00529                 << resource << " ) returned " << n << endl;
00530   return n;
00531 }
00532 
00533 QMap<Q_UINT32, QString> KMailICalIfaceImpl::incidencesKolab( const QString& mimetype,
00534                                                              const QString& resource,
00535                                                              int startIndex,
00536                                                              int nbMessages )
00537 {
00541 
00542   QMap<Q_UINT32, QString> aMap;
00543   if( !mUseResourceIMAP )
00544     return aMap;
00545 
00546   KMFolder* f = findResourceFolder( resource );
00547   if( !f ) {
00548     kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl;
00549     return aMap;
00550   }
00551 
00552   f->open( "incidences" );
00553 
00554   if ( GlobalSettings::self()->mailLossDebug() ) {
00555     kdDebug(5006) << k_funcinfo << "Getting incidences (" << mimetype << ") for folder " << f->label()
00556                   << ", starting with index " << startIndex << ", " << nbMessages << " messages." << endl;
00557     kdDebug(5006) << "The folder has " << f->count() << " messages." << endl;
00558   }
00559 
00560   int stopIndex = nbMessages == -1 ? f->count() :
00561                   QMIN( f->count(), startIndex + nbMessages );
00562 
00563   for(int i = startIndex; i < stopIndex; ++i) {
00564 #if 0
00565     bool unget = !f->isMessage(i);
00566     KMMessage* msg = f->getMsg( i );
00567 #else // faster
00568     KMMessage* msg = f->storage()->readTemporaryMsg(i);
00569 #endif
00570     if ( msg ) {
00571       const int iSlash = mimetype.find('/');
00572       const QCString sType    = mimetype.left( iSlash   ).latin1();
00573       const QCString sSubtype = mimetype.mid(  iSlash+1 ).latin1();
00574       if ( sType.isEmpty() || sSubtype.isEmpty() ) {
00575         kdError(5006) << mimetype << " not an type/subtype combination" << endl;
00576       } else {
00577         DwBodyPart* dwPart = findBodyPartByMimeType( *msg, sType, sSubtype );
00578         if ( dwPart ) {
00579           KMMessagePart msgPart;
00580           KMMessage::bodyPart(dwPart, &msgPart);
00581           aMap.insert(msg->getMsgSerNum(), msgPart.bodyToUnicode( QTextCodec::codecForName( "utf8" ) ));
00582         } else {
00583           // Check if the whole message has the right types. This is what
00584           // happens in the case of ical storage, where the whole mail is
00585           // the data
00586           const QCString type( msg->typeStr() );
00587           const QCString subtype( msg->subtypeStr() );
00588           if (type.lower() == sType && subtype.lower() == sSubtype ) {
00589             aMap.insert( msg->getMsgSerNum(), msg->bodyToUnicode() );
00590           }
00591           // This is *not* an error: it may be that not all of the messages
00592           // have a message part that is matching the wanted MIME type
00593         }
00594       }
00595 #if 0
00596       if( unget ) f->unGetMsg(i);
00597 #else
00598       delete msg;
00599 #endif
00600     } else {
00601       kdDebug(5006) << k_funcinfo << " Unable to retrieve message " << i << " for incidence!" << endl;
00602     }
00603   }
00604   f->close( "incidences" );
00605   return aMap;
00606 }
00607 
00608 
00609 /* Called when a message that was downloaded from an online imap folder
00610  * arrives. Needed when listing incidences on online account folders. */
00611 // TODO: Till, port me
00612 void KMailICalIfaceImpl::slotMessageRetrieved( KMMessage* msg )
00613 {
00614   if( !msg ) return;
00615 
00616   KMFolder *parent = msg->parent();
00617   Q_ASSERT( parent );
00618   Q_UINT32 sernum = msg->getMsgSerNum();
00619 
00620   // do we have an accumulator for this folder?
00621   Accumulator *ac = mAccumulators.find( parent->location() );
00622   if( ac ) {
00623     QString s;
00624     if ( !vPartFoundAndDecoded( msg, s ) ) return;
00625     QString uid( "UID" );
00626     vPartMicroParser( s, uid );
00627     const Q_UINT32 sernum = msg->getMsgSerNum();
00628     mUIDToSerNum.insert( uid, sernum );
00629     ac->add( s );
00630     if( ac->isFull() ) {
00631       /* if this was the last one we were waiting for, tell the resource
00632        * about the new incidences and clean up. */
00633       //asyncLoadResult( ac->incidences, ac->type, ac->folder );
00634       mAccumulators.remove( ac->folder ); // autodelete
00635     }
00636   } else {
00637     /* We are not accumulating for this folder, so this one was added
00638      * by KMail. Do your thang. */
00639      slotIncidenceAdded( msg->parent(), msg->getMsgSerNum() );
00640   }
00641 
00642   if ( mTheUnGetMes.contains( sernum ) ) {
00643     mTheUnGetMes.remove( sernum );
00644     int i = 0;
00645     KMFolder* folder = 0;
00646     KMMsgDict::instance()->getLocation( sernum, &folder, &i );
00647     folder->unGetMsg( i );
00648   }
00649 }
00650 
00651 static int dimapAccountCount()
00652 {
00653   KMail::AccountManager *mgr = kmkernel->acctMgr();
00654   KMAccount *account = mgr->first();
00655   int count = 0;
00656   while ( account ) {
00657     if ( dynamic_cast<KMAcctCachedImap*>( account ) )
00658       ++count;
00659     account = mgr->next();
00660   }
00661   return count;
00662 }
00663 
00664 int KMailICalIfaceImpl::dimapAccounts()
00665 {
00666   return dimapAccountCount();
00667 }
00668 
00669 static QString subresourceLabelForPresentation( const KMFolder * folder )
00670 {
00671     if( KMailICalIfaceImpl::getResourceMap()->contains( folder->location() ) ) {
00672         return folder->label();
00673     }
00674 
00675     QString label = folder->prettyURL();
00676     QStringList parts = QStringList::split( QString::fromLatin1("/"), label );
00677 
00678     // In the common special case of some other user's folder shared with us
00679     // the url looks like "Server Name/user/$USERNAME/Folder/Name". Make
00680     // those a bit nicer.
00681     if ( parts[1] == QString::fromLatin1("user") ) {
00682         QStringList remainder(parts);
00683         remainder.pop_front();
00684         remainder.pop_front();
00685         remainder.pop_front();
00686         label = i18n("%1's %2")
00687             .arg( parts[2] )
00688             .arg( remainder.join( QString::fromLatin1("/") ) );
00689     }
00690     // Another special case is our own folders, under the imap INBOX, make
00691     // those prettier too
00692     const KMFolder *parent = folder;
00693     while ( parent->parent() && parent->parent()->owner() ) {
00694       parent = parent->parent()->owner();
00695       if ( parent->isSystemFolder() ) {
00696         QStringList remainder(parts);
00697         remainder.pop_front();
00698         remainder.pop_front();
00699         if ( dimapAccountCount() > 1 ) {
00700           // Fix kolab issue 2531 folder->storage() )->account() can be null
00701            if( folder->storage() && static_cast<const KMFolderCachedImap*>( folder->storage() )->account() ) {
00702           label = i18n( "My %1 (%2)")
00703               .arg( remainder.join( QString::fromLatin1("/") ),
00704                     static_cast<const KMFolderCachedImap*>( folder->storage() )->account()->name() );
00705           } else {
00706             label = i18n("My %1")
00707               .arg( remainder.join( QString::fromLatin1("/") ) );
00708           }
00709         } else {
00710           label = i18n("My %1")
00711               .arg( remainder.join( QString::fromLatin1("/") ) );
00712         }
00713         break;
00714       }
00715     }
00716     return label;
00717 }
00718 
00719 /* list all available subresources */
00720 QValueList<KMailICalIfaceImpl::SubResource> KMailICalIfaceImpl::subresourcesKolab( const QString& contentsType )
00721 {
00722   QValueList<SubResource> subResources;
00723 
00724   // Add the default one
00725   KMFolder* f = folderFromType( contentsType, QString::null );
00726   if ( f ) {
00727     subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ),
00728                                       f->isWritable(), folderIsAlarmRelevant( f ) ) );
00729     kdDebug(5006) << "Adding(1) folder " << f->location() << "    " <<
00730       ( !f->isWritable() ? "readonly" : "" ) << endl;
00731   }
00732 
00733   // get the extra ones
00734   const KMail::FolderContentsType t = folderContentsType( contentsType );
00735   QDictIterator<ExtraFolder> it( mExtraFolders );
00736   for ( ; it.current(); ++it ){
00737     f = it.current()->folder;
00738     if ( f && f->storage()->contentsType() == t ) {
00739       subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ),
00740                                         f->isWritable(), folderIsAlarmRelevant( f ) ) );
00741       kdDebug(5006) << "Adding(2) folder " << f->location() << "     " <<
00742               ( !f->isWritable() ? "readonly" : "" ) << endl;
00743     }
00744   }
00745 
00746   if ( subResources.isEmpty() )
00747     kdDebug(5006) << "subresourcesKolab: No folder found for " << contentsType << endl;
00748   return subResources;
00749 }
00750 
00751 bool KMailICalIfaceImpl::triggerSync( const QString& contentsType )
00752 {
00753   kdDebug(5006) << k_funcinfo << endl;
00754   QValueList<KMailICalIfaceImpl::SubResource> folderList = subresourcesKolab( contentsType );
00755   for ( QValueList<KMailICalIfaceImpl::SubResource>::const_iterator it( folderList.begin() ),
00756                                                                     end( folderList.end() );
00757         it != end ; ++it ) {
00758     KMFolder * const f = findResourceFolder( (*it).location );
00759     if ( !f ) continue;
00760     if ( f->folderType() == KMFolderTypeImap || f->folderType() == KMFolderTypeCachedImap ) {
00761       if ( !kmkernel->askToGoOnline() ) {
00762         return false;
00763       }
00764     }
00765 
00766     if ( f->folderType() == KMFolderTypeImap ) {
00767       KMFolderImap *imap = static_cast<KMFolderImap*>( f->storage() );
00768       imap->getAndCheckFolder();
00769     } else if ( f->folderType() == KMFolderTypeCachedImap ) {
00770       KMFolderCachedImap* cached = static_cast<KMFolderCachedImap*>( f->storage() );
00771       if ( cached->account() ) {
00772         cached->account()->processNewMailInFolder( f );
00773       }
00774     }
00775   }
00776   return true;
00777 }
00778 
00779 /* Used by the resource to query whether folders are writable. */
00780 bool KMailICalIfaceImpl::isWritableFolder( const QString& type,
00781                                            const QString& resource )
00782 {
00783   KMFolder* f = folderFromType( type, resource );
00784   if ( !f )
00785     // Definitely not writable
00786     return false;
00787 
00788   return f->isWritable();
00789 }
00790 
00791 /* Used by the resource to query the storage format of the folder. */
00792 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( const QString& resource )
00793 {
00794   StorageFormat format;
00795   KMFolder* f = findResourceFolder( resource );
00796   if ( f )
00797     format = storageFormat( f );
00798   else
00799     format = globalStorageFormat();
00800   return format;
00801 }
00802 
00817 Q_UINT32 KMailICalIfaceImpl::update( const QString& resource,
00818                                      Q_UINT32 sernum,
00819                                      const QString& subject,
00820                                      const QString& plainTextBody,
00821                                      const QMap<QCString, QString>& customHeaders,
00822                                      const QStringList& attachmentURLs,
00823                                      const QStringList& attachmentMimetypes,
00824                                      const QStringList& attachmentNames,
00825                                      const QStringList& deletedAttachments )
00826 {
00827   Q_UINT32 rc = 0;
00828 
00829    if( !mUseResourceIMAP )
00830     return rc;
00831 
00832   Q_ASSERT( !resource.isEmpty() );
00833 
00834   kdDebug(5006) << "KMailICalIfaceImpl::update( " << resource << ", " << sernum << " )\n";
00835   kdDebug(5006) << attachmentURLs << "\n";
00836   kdDebug(5006) << attachmentMimetypes << "\n";
00837   kdDebug(5006) << attachmentNames << "\n";
00838   kdDebug(5006) << subject << "\n";
00839   kdDebug(5006) << "deleted attachments:" << deletedAttachments << "\n";
00840 
00841   // Find the folder
00842   KMFolder* f = findResourceFolder( resource );
00843   if( !f ) {
00844     kdError(5006) << "update(" << resource << ") : Not an IMAP resource folder" << endl;
00845     return rc;
00846   }
00847 
00848   f->open( "ifaceupdate" );
00849 
00850   KMMessage* msg = 0;
00851   if ( sernum != 0 ) {
00852     if ( storageFormat( f ) == StorageXML ) {
00853       msg = findMessageBySerNum( sernum, f, subject );
00854     } else {
00855       msg = findMessageBySerNum( sernum, f );
00856     }
00857     if ( !msg ) return 0;
00858     // Message found - make a copy and update it:
00859     KMMessage* newMsg = new KMMessage( *msg );
00860     newMsg->setSubject( subject );
00861     QMap<QCString, QString>::ConstIterator ith = customHeaders.begin();
00862     const QMap<QCString, QString>::ConstIterator ithEnd = customHeaders.begin();
00863     for ( ; ith != ithEnd ; ++ith )
00864       newMsg->setHeaderField( ith.key(), ith.data() );
00865     newMsg->setParent( 0 ); // workaround strange line in KMMsgBase::assign. newMsg is not in any folder yet.
00866     // Note that plainTextBody isn't used in this branch. We assume it's still valid from when the mail was created.
00867 
00868     // Delete some attachments according to list
00869     for( QStringList::ConstIterator it = deletedAttachments.begin();
00870          it != deletedAttachments.end();
00871          ++it ){
00872       if( !deleteAttachment( *newMsg, *it ) ){
00873         // Note: It is _not_ an error if an attachment was already deleted.
00874       }
00875     }
00876 
00877     const KMail::FolderContentsType t = f->storage()->contentsType();
00878     const QCString type = msg->typeStr();
00879     const QCString subtype = msg->subtypeStr();
00880     const bool messageWasIcalVcardFormat = ( type.lower() == "text" &&
00881         ( subtype.lower() == "calendar" || subtype.lower() == "x-vcard" ) );
00882 
00883     if ( storageFormat( f ) == StorageIcalVcard ) {
00884       //kdDebug(5006) << k_funcinfo << " StorageFormatIcalVcard " << endl;
00885       if ( !messageWasIcalVcardFormat ) {
00886         setIcalVcardContentTypeHeader( newMsg, t, f );
00887       }
00888       newMsg->setBodyEncoded( plainTextBody.utf8() );
00889     } else if ( storageFormat( f ) == StorageXML ) {
00890       if ( messageWasIcalVcardFormat ) {
00891         // this was originally an ical event, but the folder changed to xml,
00892         // convert
00893        setXMLContentTypeHeader( newMsg, plainTextBody );
00894       }
00895       //kdDebug(5006) << k_funcinfo << " StorageFormatXML " << endl;
00896       // Add all attachments by reading them from their temp. files
00897       QStringList::ConstIterator iturl = attachmentURLs.begin();
00898       QStringList::ConstIterator itmime = attachmentMimetypes.begin();
00899       QStringList::ConstIterator itname = attachmentNames.begin();
00900       for( ;
00901           iturl != attachmentURLs.end()
00902           && itmime != attachmentMimetypes.end()
00903           && itname != attachmentNames.end();
00904           ++iturl, ++itname, ++itmime ){
00905         bool byname = !(*itmime).startsWith( "application/x-vnd.kolab." );
00906         if( !updateAttachment( *newMsg, *iturl, *itname, *itmime, byname ) ){
00907           kdDebug(5006) << "Attachment error, can not update attachment " << *iturl << endl;
00908           break;
00909         }
00910       }
00911     }
00912 
00913     //debugBodyParts( "in update, before cleanup", *newMsg );
00914 
00915     // This is necessary for the headers to be readable later on
00916     newMsg->cleanupHeader();
00917 
00918     //debugBodyParts( "in update, after cleanup", *newMsg );
00919     //kdDebug(5006) << "-DEBUG KMailICalIface:  will add now! isTransferInProgress() ? " << msg->transferInProgress() << endl;
00920     deleteMsg( msg );
00921     //kdDebug(5006) << "*DEBUG KMailICalIface: will add now!" << endl;
00922     if ( f->addMsg( newMsg ) == 0 ) {
00923       // Message stored
00924       rc = newMsg->getMsgSerNum();
00925       kdDebug(5006) << "forget about " << sernum << ", it's " << rc << " now" << endl;
00926     }
00927     addFolderChange( f, Contents );
00928     syncFolder( f );
00929   } else {
00930     // Message not found - store it newly
00931     rc = addIncidenceKolab( *f, subject, plainTextBody, customHeaders,
00932                             attachmentURLs,
00933                             attachmentNames,
00934                             attachmentMimetypes );
00935   }
00936 
00937   f->close("ifaceupdate");
00938   return rc;
00939 }
00940 
00941 KURL KMailICalIfaceImpl::getAttachment( const QString& resource,
00942                                         Q_UINT32 sernum,
00943                                         const QString& filename )
00944 {
00945   // This finds the attachment with the filename, saves it to a
00946   // temp file and returns a URL to it. It's up to the resource
00947   // to delete the tmp file later.
00948   if( !mUseResourceIMAP )
00949     return KURL();
00950 
00951   kdDebug(5006) << "KMailICalIfaceImpl::getAttachment( "
00952                 << resource << ", " << sernum << ", " << filename << " )\n";
00953 
00954   // Find the folder
00955   KMFolder* f = findResourceFolder( resource );
00956   if( !f ) {
00957     kdError(5006) << "getAttachment(" << resource << ") : Not an IMAP resource folder" << endl;
00958     return KURL();
00959   }
00960   if ( storageFormat( f ) != StorageXML ) {
00961     kdError(5006) << "getAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
00962     return KURL();
00963   }
00964 
00965   KURL url;
00966 
00967   bool bOK = false;
00968   bool quiet = mResourceQuiet;
00969   mResourceQuiet = true;
00970 
00971   KMMessage* msg = findMessageBySerNum( sernum, f );
00972   if( msg ) {
00973     // Message found - look for the attachment:
00974 
00975     DwBodyPart* part = findBodyPart( *msg, filename );
00976     if ( part ) {
00977       // Save the contents of the attachment.
00978       KMMessagePart aPart;
00979       msg->bodyPart( part, &aPart );
00980       QByteArray rawData( aPart.bodyDecodedBinary() );
00981 
00982       KTempFile file;
00983       file.file()->writeBlock( rawData.data(), rawData.size() );
00984 
00985       url.setPath( file.name() );
00986 
00987       bOK = true;
00988     }
00989 
00990     if( !bOK ){
00991       kdDebug(5006) << "Attachment " << filename << " not found." << endl;
00992     }
00993   }else{
00994     kdDebug(5006) << "Message not found." << endl;
00995   }
00996 
00997   mResourceQuiet = quiet;
00998   return url;
00999 }
01000 
01001 QString KMailICalIfaceImpl::attachmentMimetype( const QString & resource,
01002                                                 Q_UINT32 sernum,
01003                                                 const QString & filename )
01004 {
01005   if( !mUseResourceIMAP )
01006     return QString();
01007   KMFolder* f = findResourceFolder( resource );
01008   if( !f || storageFormat( f ) != StorageXML ) {
01009     kdError(5006) << "attachmentMimetype(" << resource << ") : Wrong folder" << endl;
01010     return QString();
01011   }
01012 
01013   KMMessage* msg = findMessageBySerNum( sernum, f );
01014   if( msg ) {
01015     // Message found - look for the attachment:
01016     DwBodyPart* part = findBodyPart( *msg, filename );
01017     if ( part ) {
01018       KMMessagePart kmPart;
01019       msg->bodyPart( part, &kmPart );
01020       return QString( kmPart.typeStr() ) + "/" + QString( kmPart.subtypeStr() );
01021     } else {
01022       kdDebug(5006) << "Attachment " << filename << " not found." << endl;
01023     }
01024   } else {
01025     kdDebug(5006) << "Message not found." << endl;
01026   }
01027 
01028   return QString();
01029 }
01030 
01031 QStringList KMailICalIfaceImpl::listAttachments(const QString & resource, Q_UINT32 sernum)
01032 {
01033   QStringList rv;
01034   if( !mUseResourceIMAP )
01035     return rv;
01036 
01037   // Find the folder
01038   KMFolder* f = findResourceFolder( resource );
01039   if( !f ) {
01040     kdError(5006) << "listAttachments(" << resource << ") : Not an IMAP resource folder" << endl;
01041     return rv;
01042   }
01043   if ( storageFormat( f ) != StorageXML ) {
01044     kdError(5006) << "listAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
01045     return rv;
01046   }
01047 
01048   KMMessage* msg = findMessageBySerNum( sernum, f );
01049   if( msg ) {
01050     for ( DwBodyPart* part = msg->getFirstDwBodyPart(); part; part = part->Next() ) {
01051       if ( part->hasHeaders() ) {
01052         QString name;
01053         DwMediaType& contentType = part->Headers().ContentType();
01054         if ( QString( contentType.SubtypeStr().c_str() ).startsWith( "x-vnd.kolab." )
01055            || QString( contentType.SubtypeStr().c_str() ).contains( "tnef" ) )
01056           continue;
01057         if ( !part->Headers().ContentDisposition().Filename().empty() )
01058           name = part->Headers().ContentDisposition().Filename().c_str();
01059         else if ( !contentType.Name().empty() )
01060           name = contentType.Name().c_str();
01061         if ( !name.isEmpty() )
01062           rv.append( name );
01063       }
01064     }
01065   } else {
01066     kdDebug(5006) << "Message not found." << endl;
01067   }
01068 
01069   return rv;
01070 }
01071 
01072 
01073 // ============================================================================
01074 
01075 /* KMail part of the interface. These slots are connected to the resource
01076  * folders and inform us of folders or incidences in them changing, being
01077  * added or going away. */
01078 
01079 void KMailICalIfaceImpl::slotFolderRemoved( KMFolder* folder )
01080 {
01081   // pretend the folder just changed back to the mail type, which
01082   // does the right thing, namely remove resource
01083   folderContentsTypeChanged( folder, KMail::ContentsTypeMail );
01084   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01085   configGroup.deleteEntry( folder->idString() + "-storageFormat" );
01086   configGroup.deleteEntry( folder->idString() + "-changes" );
01087 }
01088 
01089 // KMail added a file to one of the groupware folders
01090 void KMailICalIfaceImpl::slotIncidenceAdded( KMFolder* folder,
01091                                              Q_UINT32 sernum )
01092 {
01093   if( mResourceQuiet || !mUseResourceIMAP ) {
01094     //kdDebug(5006) << "*****DEBUG KMailICalIfaceImpl::slotIncidenceAdded(): SKIIIIPED ;sernum=" << sernum <<endl;
01095     return;
01096   }
01097   //kdDebug(5006) << "*****DEBUG KMailICalIfaceImpl::slotIncidenceAdded(): ;sernum=" << sernum <<endl;
01098 
01099 //  kdDebug(5006) << "KMailICalIfaceImpl::slotIncidenceAdded" << endl;
01100   QString type = folderContentsType( folder->storage()->contentsType() );
01101   if( type.isEmpty() ) {
01102     kdError(5006) << "Not an IMAP resource folder" << endl;
01103     return;
01104   }
01105   // Get the index of the mail
01106   int i = 0;
01107   KMFolder* aFolder = 0;
01108   KMMsgDict::instance()->getLocation( sernum, &aFolder, &i );
01109   assert( folder == aFolder );
01110 
01111   bool unget = !folder->isMessage( i );
01112   QString s;
01113   QString uid( "UID" );
01114   KMMessage *msg = folder->getMsg( i );
01115   if( !msg ) return;
01116   if( msg->isComplete() ) {
01117 
01118     bool ok = false;
01119     StorageFormat format = storageFormat( folder );
01120     switch( format ) {
01121       case StorageIcalVcard:
01122         // Read the iCal or vCard
01123         ok = vPartFoundAndDecoded( msg, s );
01124         if ( ok )
01125           vPartMicroParser( s, uid );
01126         break;
01127       case StorageXML:
01128         // Read the XML from the attachment with the given mimetype
01129         if ( kolabXMLFoundAndDecoded( *msg,
01130               folderKolabMimeType( folder->storage()->contentsType() ), s ) ) {
01131           uid = msg->subject();
01132           ok = true;
01133         }
01134         break;
01135     }
01136     if ( !ok ) {
01137       if ( unget )
01138         folder->unGetMsg( i );
01139       return;
01140     }
01141     const Q_UINT32 sernum = msg->getMsgSerNum();
01142     mUIDToSerNum.insert( uid, sernum );
01143 
01144     // tell the resource if we didn't trigger this ourselves
01145     if ( mInTransit.contains( uid ) ) {
01146       mInTransit.remove( uid );
01147     }
01148     incidenceAdded( type, folder->location(), sernum, format, s );
01149   } else {
01150     // go get the rest of it, then try again
01151     // TODO: Till, port me
01152     if ( unget ) mTheUnGetMes.insert( msg->getMsgSerNum(), true );
01153     FolderJob *job = msg->parent()->createJob( msg );
01154     connect( job, SIGNAL( messageRetrieved( KMMessage* ) ),
01155         this, SLOT( slotMessageRetrieved( KMMessage* ) ) );
01156     job->start();
01157     return;
01158   }
01159   if( unget ) folder->unGetMsg(i);
01160 }
01161 
01162 // KMail deleted a file
01163 void KMailICalIfaceImpl::slotIncidenceDeleted( KMFolder* folder,
01164                                                Q_UINT32 sernum )
01165 {
01166   if( mResourceQuiet || !mUseResourceIMAP ) {
01167     //kdDebug(5006) << "DEBUG KMailICalIfaceImpl::slotIncidenceDeleted(): SKIIIIPED ;sernum=" << sernum <<endl;
01168     return;
01169   }
01170   //kdDebug(5006) << "DEBUG KMailICalIfaceImpl::slotIncidenceDeleted(): sernum=" << sernum<< endl;
01171 
01172   QString type = folderContentsType( folder->storage()->contentsType() );
01173   //kdDebug(5006) << folder << " " << type << " " << sernum << endl;
01174   if( !type.isEmpty() ) {
01175     // Get the index of the mail
01176     int i = 0;
01177     KMFolder* aFolder = 0;
01178     KMMsgDict::instance()->getLocation( sernum, &aFolder, &i );
01179     assert( folder == aFolder );
01180 
01181     // Read the iCal or vCard
01182     bool unget = !folder->isMessage( i );
01183     QString s;
01184     bool ok = false;
01185     KMMessage* msg = folder->getMsg( i );
01186     QString uid( "UID" );
01187     switch( storageFormat( folder ) ) {
01188     case StorageIcalVcard:
01189         if( vPartFoundAndDecoded( msg, s ) ) {
01190             vPartMicroParser( s, uid );
01191             ok = true;
01192         }
01193         break;
01194     case StorageXML:
01195         if ( kolabXMLFoundAndDecoded( *msg, folderKolabMimeType( folder->storage()->contentsType() ), s ) ) {
01196           uid = msg->subject();
01197           ok = true;
01198         }
01199         break;
01200     }
01201     if ( ok ) {
01202         kdDebug(5006) << "Emitting DCOP signal incidenceDeleted( "
01203                       << type << ", " << folder->location() << ", " << uid
01204                       << " )" << endl;
01205         incidenceDeleted( type, folder->location(), uid, sernum );
01206     }
01207     if( unget ) folder->unGetMsg(i);
01208   } else
01209     kdError(5006) << "Not a groupware folder" << endl;
01210 }
01211 
01212 // KMail orders a refresh
01213 void KMailICalIfaceImpl::slotRefresh( const QString& type )
01214 {
01215   if( mUseResourceIMAP ) {
01216     signalRefresh( type, QString::null /* PENDING(bo) folder->location() */ );
01217     kdDebug(5006) << "Emitting DCOP signal signalRefresh( " << type << " )" << endl;
01218   }
01219 }
01220 
01221 // This is among other things called when an expunge of a folder happens
01222 void KMailICalIfaceImpl::slotRefreshFolder( KMFolder* folder)
01223 {
01224   // TODO: The resources would of course be better off, if only this
01225   // folder would need refreshing. Currently it just orders a reload of
01226   // the type of the folder
01227   if( mUseResourceIMAP && folder ) {
01228     if( folder == mCalendar || folder == mContacts
01229         || folder == mNotes || folder == mTasks
01230         || folder == mJournals || mExtraFolders.find( folder->location() ) ) {
01231       // Refresh the folder of this type
01232       KMail::FolderContentsType ct = folder->storage()->contentsType();
01233       slotRefresh( s_folderContentsType[ct].contentsTypeStr );
01234     }
01235   }
01236 }
01237 
01238 /****************************
01239  * The folder and message stuff code
01240  */
01241 
01242 KMFolder* KMailICalIfaceImpl::folderFromType( const QString& type,
01243                                               const QString& folder )
01244 {
01245   if( mUseResourceIMAP ) {
01246     KMFolder* f = 0;
01247     if ( !folder.isEmpty() ) {
01248       f = extraFolder( type, folder );
01249       if ( f )
01250         return f;
01251     }
01252 
01253     if( type == "Calendar" ) f = mCalendar;
01254     else if( type == "Contact" ) f = mContacts;
01255     else if( type == "Note" ) f = mNotes;
01256     else if( type == "Task" || type == "Todo" ) f = mTasks;
01257     else if( type == "Journal" ) f = mJournals;
01258 
01259     if ( f && ( folder.isEmpty() || folder == f->location() ) )
01260       return f;
01261 
01262     kdError(5006) << "No folder ( " << type << ", " << folder << " )\n";
01263   }
01264 
01265   return 0;
01266 }
01267 
01268 
01269 // Returns true if folder is a resource folder. If the resource isn't enabled
01270 // this always returns false
01271 bool KMailICalIfaceImpl::isResourceFolder( KMFolder* folder ) const
01272 {
01273   return mUseResourceIMAP && folder &&
01274     ( isStandardResourceFolder( folder ) || mExtraFolders.find( folder->location() )!=0 );
01275 }
01276 
01277 bool KMailICalIfaceImpl::isStandardResourceFolder( KMFolder* folder ) const
01278 {
01279   return ( folder == mCalendar || folder == mTasks || folder == mJournals ||
01280            folder == mNotes || folder == mContacts );
01281 }
01282 
01283 bool KMailICalIfaceImpl::hideResourceFolder( KMFolder* folder ) const
01284 {
01285   return mHideFolders && isResourceFolder( folder );
01286 }
01287 
01288 bool KMailICalIfaceImpl::hideResourceAccountRoot( KMFolder* folder ) const
01289 {
01290   KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
01291   bool hide = dimapFolder && mHideFolders
01292        && (int)dimapFolder->account()->id() == GlobalSettings::self()->theIMAPResourceAccount()
01293        && GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount();
01294   return hide;
01295 
01296 }
01297 
01298 KFolderTreeItem::Type KMailICalIfaceImpl::folderType( KMFolder* folder ) const
01299 {
01300   if( mUseResourceIMAP && folder ) {
01301     if( folder == mCalendar || folder == mContacts
01302         || folder == mNotes || folder == mTasks
01303         || folder == mJournals || mExtraFolders.find( folder->location() ) ) {
01304       KMail::FolderContentsType ct = folder->storage()->contentsType();
01305       return s_folderContentsType[ct].treeItemType;
01306     }
01307   }
01308 
01309   return KFolderTreeItem::Other;
01310 }
01311 
01312 // Global tables of foldernames is different languages
01313 // For now: 0->English, 1->German, 2->French, 3->Dutch
01314 static QMap<KFolderTreeItem::Type,QString> folderNames[4];
01315 QString KMailICalIfaceImpl::folderName( KFolderTreeItem::Type type, int language ) const
01316 {
01317   // With the XML storage, folders are always (internally) named in English
01318   if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
01319     language = 0;
01320 
01321   static bool folderNamesSet = false;
01322   if( !folderNamesSet ) {
01323     folderNamesSet = true;
01324     /* NOTE: If you add something here, you also need to update
01325        GroupwarePage in configuredialog.cpp */
01326 
01327     // English
01328     folderNames[0][KFolderTreeItem::Calendar] = QString::fromLatin1("Calendar");
01329     folderNames[0][KFolderTreeItem::Tasks] = QString::fromLatin1("Tasks");
01330     folderNames[0][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
01331     folderNames[0][KFolderTreeItem::Contacts] = QString::fromLatin1("Contacts");
01332     folderNames[0][KFolderTreeItem::Notes] = QString::fromLatin1("Notes");
01333 
01334     // German
01335     folderNames[1][KFolderTreeItem::Calendar] = QString::fromLatin1("Kalender");
01336     folderNames[1][KFolderTreeItem::Tasks] = QString::fromLatin1("Aufgaben");
01337     folderNames[1][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
01338     folderNames[1][KFolderTreeItem::Contacts] = QString::fromLatin1("Kontakte");
01339     folderNames[1][KFolderTreeItem::Notes] = QString::fromLatin1("Notizen");
01340 
01341     // French
01342     folderNames[2][KFolderTreeItem::Calendar] = QString::fromLatin1("Calendrier");
01343     // Tasks = Tâches (â == 0xE2 in latin1)
01344     folderNames[2][KFolderTreeItem::Tasks] = QString::fromLatin1("T\342ches");
01345     folderNames[2][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
01346     folderNames[2][KFolderTreeItem::Contacts] = QString::fromLatin1("Contacts");
01347     folderNames[2][KFolderTreeItem::Notes] = QString::fromLatin1("Notes");
01348 
01349     // Dutch
01350     folderNames[3][KFolderTreeItem::Calendar] = QString::fromLatin1("Agenda");
01351     folderNames[3][KFolderTreeItem::Tasks] = QString::fromLatin1("Taken");
01352     folderNames[3][KFolderTreeItem::Journals] = QString::fromLatin1("Logboek");
01353     folderNames[3][KFolderTreeItem::Contacts] = QString::fromLatin1("Contactpersonen");
01354     folderNames[3][KFolderTreeItem::Notes] = QString::fromLatin1("Notities");
01355   }
01356 
01357   if( language < 0 || language > 3 ) {
01358     return folderNames[mFolderLanguage][type];
01359   }
01360   else {
01361     return folderNames[language][type];
01362   }
01363 }
01364 
01365 
01366 // Find message matching a given UID
01367 KMMessage *KMailICalIfaceImpl::findMessageByUID( const QString& uid, KMFolder* folder )
01368 {
01369   if( !folder || !mUIDToSerNum.contains( uid ) ) return 0;
01370   int i;
01371   KMFolder *aFolder;
01372   KMMsgDict::instance()->getLocation( mUIDToSerNum[uid], &aFolder, &i );
01373   Q_ASSERT( aFolder == folder );
01374   return folder->getMsg( i );
01375 }
01376 
01377 // Find message matching a given serial number
01378 KMMessage *KMailICalIfaceImpl::findMessageBySerNum( Q_UINT32 serNum, KMFolder* folder, const QString& subject )
01379 {
01380   if( !folder ) return 0;
01381 
01382   KMMessage *message = 0;
01383   KMFolder* aFolder = 0;
01384   int index;
01385   KMMsgDict::instance()->getLocation( serNum, &aFolder, &index );
01386 
01387   if( aFolder && aFolder != folder ) {
01388     kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) found it in folder " << aFolder->location() << ", expected " << folder->location() << endl;
01389   } else {
01390     if( aFolder )
01391       message = aFolder->getMsg( index );
01392     if (!message)
01393       kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) invalid serial number\n" << endl;
01394   }
01395 
01396   // The Following code is aimed to avoid incidence loss where the
01397   // wrong mail is updated and thus deleted.
01398   //
01399   // see Kolab Issue 4849, 4843, and several other corrupted index problems
01400 
01401   // Safety check that found message has the same subject
01402   if ( !subject.isEmpty() ) {
01403     if ( message->subject() != subject ) {
01404       // This should not happen!
01405       kdWarning(5006) << k_funcinfo << " Subject check failed!" << endl
01406                       << "Got: " << message->subject() << endl
01407                       << "Expected: " << subject << endl;
01408       sleep( 1 ); // Maybe it is caused by a weird threading situation
01409       message = aFolder->getMsg( index );
01410       if ( !message ) {
01411         kdWarning(5006) << "Second getMsg returned 0" << endl;
01412         return 0;
01413       }
01414       if ( message->subject() != subject ) {
01415         kdWarning(5006) << "Still got the wrong message: " << message->subject() << endl;
01416         KMFolderCachedImap * cimapFolder = dynamic_cast<KMFolderCachedImap *> ( aFolder->storage() );
01417         if ( cimapFolder != NULL ) {
01418           cimapFolder->markForReindexing();
01419         } else {
01420           // This can't happen,..
01421           kdWarning(5006) << "Folder is not a cached IMAP folder" << endl;
01422         }
01423         const QString errMsg = i18n("<qt>Kontact has detected a corruption in the folder: <br/> %1 <br/>"
01424                                     "Your current changes could not be saved.<br/><br/>"
01425                                     "It is <b>strongly</b> recommended that you restart Kontact now.<br/>"
01426                                     "Quit now?</qt>").arg(folder->prettyURL());
01427         if ( KMessageBox::warningYesNo( 0, errMsg, i18n("Invalid state detected") ) == KMessageBox::Yes ) {
01428           qApp->quit();
01429         } else {
01430           return 0;
01431         }
01432       } else {
01433         kdDebug(5006) << "Got the right message now, phew" << endl;
01434       }
01435     }
01436   }
01437   return message;
01438 }
01439 
01440 void KMailICalIfaceImpl::deleteMsg( KMMessage *msg )
01441 {
01442   if( !msg ) return;
01443   // Commands are now delayed; can't use that anymore, we need immediate deletion
01444   //( new KMDeleteMsgCommand( msg->parent(), msg ) )->start();
01445   KMFolder *srcFolder = msg->parent();
01446   int idx = srcFolder->find(msg);
01447   assert(idx != -1);
01448   // kill existing jobs since we are about to delete the message
01449   srcFolder->ignoreJobsForMessage( msg );
01450   if ( !msg->transferInProgress() ) {
01451     srcFolder->removeMsg(idx);
01452     delete msg;
01453   } else {
01454     kdDebug(5006) << k_funcinfo << "DEBUG Message cannot be deleted now because it is currently in use " << msg << endl;
01455     msg->deleteWhenUnused();
01456   }
01457   addFolderChange( srcFolder, Contents );
01458 }
01459 
01460 void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder,
01461                                                     KMail::FolderContentsType contentsType )
01462 {
01463   if ( !mUseResourceIMAP )
01464     return;
01465 //  kdDebug(5006) << "folderContentsTypeChanged( " << folder->name()
01466 //                << ", " << contentsType << ")\n";
01467 
01468   // The builtins can't change type
01469   if ( isStandardResourceFolder( folder ) )
01470     return;
01471 
01472   // Check if already know that 'extra folder'
01473   const QString location = folder->location();
01474   ExtraFolder* ef = mExtraFolders.find( location );
01475   if ( ef && ef->folder ) {
01476     // Notify that the old folder resource is no longer available
01477     subresourceDeleted(folderContentsType( folder->storage()->contentsType() ), location );
01478 
01479     if ( contentsType == KMail::ContentsTypeMail ) {
01480       // Delete the old entry, stop listening and stop here
01481       mExtraFolders.remove( location );
01482       folder->disconnect( this );
01483       return;
01484     }
01485     // So the type changed to another groupware type, ok.
01486   } else {
01487     if ( ef && !ef->folder ) // deleted folder, clean up
01488       mExtraFolders.remove( location );
01489     if ( contentsType == KMail::ContentsTypeMail )
01490         return;
01491 
01492     //kdDebug(5006) << "registering " << location << " as extra folder" << endl;
01493     // Make a new entry for the list
01494     ef = new ExtraFolder( folder );
01495     mExtraFolders.insert( location, ef );
01496 
01497     FolderInfo info = readFolderInfo( folder );
01498     mFolderInfoMap.insert( folder, info );
01499 
01500     // Adjust the folder names of all foo.default folders.
01501     // German users will get Kalender as the name of all default Calendar folders,
01502     // including their own, so that the default calendar folder of their Japanese
01503     // coworker appears as /user/hirohito/Kalender, although Hirohito sees his folder
01504     // in Japanese. On the server the folders are always in English.
01505     if ( folder->folderType() == KMFolderTypeCachedImap ) {
01506       QString annotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
01507       kdDebug(5006) << "folderContentsTypeChanged: " << folder->name() << " has annotation " << annotation << endl;
01508       if ( annotation == QString( s_folderContentsType[contentsType].annotation ) + ".default" )
01509         folder->setLabel( localizedDefaultFolderName( contentsType ) );
01510     }
01511 
01512     connectFolder( folder );
01513   }
01514   // Tell about the new resource
01515   subresourceAdded( folderContentsType( contentsType ), location, subresourceLabelForPresentation(folder),
01516                     folder->isWritable(), folderIsAlarmRelevant( folder ) );
01517 }
01518 
01519 KMFolder* KMailICalIfaceImpl::extraFolder( const QString& type,
01520                                            const QString& folder )
01521 {
01522   // If an extra folder exists that matches the type and folder location,
01523   // use that
01524   int t = folderContentsType( type );
01525   if ( t < 1 || t > 5 )
01526     return 0;
01527 
01528   ExtraFolder* ef = mExtraFolders.find( folder );
01529   if ( ef && ef->folder && ef->folder->storage()->contentsType() == t )
01530     return ef->folder;
01531 
01532   return 0;
01533 }
01534 
01535 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( KMFolder* folder ) const
01536 {
01537   FolderInfoMap::ConstIterator it = mFolderInfoMap.find( folder );
01538   if ( it != mFolderInfoMap.end() )
01539     return (*it).mStorageFormat;
01540   return globalStorageFormat();
01541 }
01542 
01543 void KMailICalIfaceImpl::setStorageFormat( KMFolder* folder, StorageFormat format )
01544 {
01545   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01546   if ( it != mFolderInfoMap.end() ) {
01547     (*it).mStorageFormat = format;
01548   } else {
01549     FolderInfo info( format, NoChange );
01550     mFolderInfoMap.insert( folder, info );
01551   }
01552   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01553   configGroup.writeEntry( folder->idString() + "-storageFormat",
01554                           format == StorageXML ? "xml" : "icalvcard" );
01555 }
01556 
01557 void KMailICalIfaceImpl::addFolderChange( KMFolder* folder, FolderChanges changes )
01558 {
01559   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01560   if ( it != mFolderInfoMap.end() ) {
01561     (*it).mChanges = static_cast<FolderChanges>( (*it).mChanges | changes );
01562   } else { // Otherwise, well, it's a folder we don't care about.
01563     kdDebug(5006) << "addFolderChange: nothing known about folder " << folder->location() << endl;
01564   }
01565   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01566   configGroup.writeEntry( folder->idString() + "-changes", (*it).mChanges );
01567 }
01568 
01569 KMailICalIfaceImpl::FolderInfo KMailICalIfaceImpl::readFolderInfo( const KMFolder * const folder ) const
01570 {
01571   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01572   QString str = configGroup.readEntry( folder->idString() + "-storageFormat", "unset" );
01573   FolderInfo info;
01574   if ( str == "unset" ) {
01575     info.mStorageFormat = globalStorageFormat();
01576     configGroup.writeEntry( folder->idString() + "-storageFormat",
01577                             info.mStorageFormat == StorageXML ? "xml" : "icalvcard" );
01578   } else {
01579     info.mStorageFormat = ( str == "xml" ) ? StorageXML : StorageIcalVcard;
01580   }
01581   info.mChanges = (FolderChanges) configGroup.readNumEntry( folder->idString() + "-changes" );
01582   return info;
01583 }
01584 
01585 
01586 void KMailICalIfaceImpl::folderSynced( KMFolder* folder, const KURL& folderURL )
01587 {
01588   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01589   if ( it != mFolderInfoMap.end() && (*it).mChanges ) {
01590     handleFolderSynced( folder, folderURL, (*it).mChanges );
01591     (*it).mChanges = NoChange;
01592   }
01593 }
01594 
01595 void KMailICalIfaceImpl::handleFolderSynced( KMFolder* folder,
01596                                              const KURL& folderURL,
01597                                              int _changes )
01598 {
01599   // This is done here instead of in the resource, because
01600   // there could be 0, 1, or N kolab resources at this point.
01601   // We can hack the N case, but not the 0 case.
01602   // So the idea of a DCOP signal for this wouldn't work.
01603   if ( ( _changes & KMailICalIface::Contents ) ||
01604        ( _changes & KMailICalIface::ACL ) ) {
01605     if ( storageFormat( folder ) == StorageXML && folder->storage()->contentsType() == KMail::ContentsTypeCalendar )
01606       triggerKolabFreeBusy( folderURL, true /*report errors*/ );
01607   }
01608 }
01609 
01610 void KMailICalIfaceImpl::folderDeletedOnServer( const KURL& folderURL )
01611 {
01612   triggerKolabFreeBusy( folderURL, false /*do not report errors*/ );
01613 }
01614 
01615 void KMailICalIfaceImpl::triggerKolabFreeBusy( const KURL& folderURL, bool report )
01616 {
01617   /* Steffen said: you must issue an authenticated HTTP GET request to
01618      https://kolabserver/freebusy/trigger/user@domain/Folder/NestedFolder.pfb
01619      (replace .pfb with .xpfb for extended fb lists). */
01620   KURL httpURL( folderURL );
01621   // Keep username ("user@domain"), pass, and host from the imap url
01622   httpURL.setProtocol( "https" );
01623   httpURL.setPort( 0 ); // remove imap port
01624 
01625   // IMAP path is either /INBOX/<path> or /user/someone/<path>
01626   QString path = folderURL.path( -1 );
01627   Q_ASSERT( path.startsWith( "/" ) );
01628   int secondSlash = path.find( '/', 1 );
01629   if ( secondSlash == -1 ) {
01630     kdWarning() << "KCal::ResourceKolab::fromKMailFolderSynced path is too short: " << path << endl;
01631     return;
01632   }
01633   if ( path.startsWith( "/INBOX/", false ) ) {
01634     // If INBOX, replace it with the username (which is user@domain)
01635     path = path.mid( secondSlash );
01636     path.prepend( folderURL.user() );
01637   } else {
01638     // If user, just remove it. So we keep the IMAP-returned username.
01639     // This assumes it's a known user on the same domain.
01640     path = path.mid( secondSlash );
01641   }
01642 
01643   if ( path.startsWith( "/" ) )
01644     httpURL.setPath( "/freebusy/trigger" + path + ".pfb" );
01645   else
01646     httpURL.setPath( "/freebusy/trigger/" + path + ".pfb" );
01647 
01648   httpURL.setQuery( QString::null );
01649   // Ensure that we encode everything with UTF8
01650   httpURL = KURL( httpURL.url(0,106), 106 );
01651   kdDebug() << "Triggering PFB update for " << folderURL << " : getting " << httpURL << endl;
01652 
01653   if ( report ) {
01654     KIO::Job* job = KIO::get( httpURL, false, false /*no progress info*/ );
01655     job->addMetaData( "errorPage", "false" ); // we want an error in case of 404
01656     connect( job, SIGNAL( result( KIO::Job* ) ), SLOT( slotFreeBusyTriggerResult( KIO::Job* ) ) );
01657   }
01658 }
01659 
01660 void KMailICalIfaceImpl::slotFreeBusyTriggerResult( KIO::Job *job )
01661 {
01662   if ( job->error() ) {
01663     KURL url( job->errorText() );
01664     KMessageBox::sorry(0, i18n("Could not trigger Free/Busy information update: %1.").arg( url.prettyURL() ) );
01665   }
01666 }
01667 
01668 void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder )
01669 {
01670   if ( isResourceFolder( folder ) ) {
01671     const QString location = folder->location();
01672     const QString contentsTypeStr = folderContentsType( folder->storage()->contentsType() );
01673     subresourceDeleted( contentsTypeStr, location );
01674 
01675     subresourceAdded( contentsTypeStr, location, subresourceLabelForPresentation( folder ),
01676                       folder->isWritable(), folderIsAlarmRelevant( folder ) );
01677   }
01678 }
01679 
01680 // Must only be connected to a signal from KMFolder!
01681 void KMailICalIfaceImpl::slotFolderRenamed()
01682 {
01683   const KMFolder* folder = static_cast<const KMFolder *>( sender() );
01684   slotFolderPropertiesChanged( const_cast<KMFolder*>( folder ) );
01685 }
01686 
01687 void KMailICalIfaceImpl::slotFolderLocationChanged( const QString &oldLocation,
01688                                                     const QString &newLocation )
01689 {
01690   KMFolder *folder = findResourceFolder( oldLocation );
01691   ExtraFolder* ef = mExtraFolders.find( oldLocation );
01692   if ( ef ) {
01693     // reuse the ExtraFolder entry, but adjust the key
01694     mExtraFolders.setAutoDelete( false );
01695     mExtraFolders.remove( oldLocation );
01696     mExtraFolders.setAutoDelete( true );
01697     mExtraFolders.insert( newLocation, ef );
01698   }
01699   if (  folder )
01700     subresourceDeleted( folderContentsType(  folder->storage()->contentsType() ), oldLocation );
01701 
01702 }
01703 
01704 KMFolder* KMailICalIfaceImpl::findResourceFolder( const QString& resource )
01705 {
01706   // Try the standard folders
01707   if( mCalendar && mCalendar->location() == resource )
01708     return mCalendar;
01709   if ( mContacts && mContacts->location() == resource )
01710     return mContacts;
01711   if ( mNotes && mNotes->location() == resource )
01712     return mNotes;
01713   if ( mTasks && mTasks->location() == resource )
01714     return mTasks;
01715   if ( mJournals && mJournals->location() == resource )
01716     return mJournals;
01717 
01718   // No luck. Try the extrafolders
01719   ExtraFolder* ef = mExtraFolders.find( resource );
01720   if ( ef )
01721     return ef->folder;
01722 
01723   // No luck at all
01724   return 0;
01725 }
01726 
01727 QString KMailICalIfaceImpl::dimapFolderAccountName( const QString &folderPath )
01728 {
01729   QString name;
01730   KMFolder *f = findResourceFolder( folderPath );
01731   if ( f ) {
01732     if ( f->storage() && static_cast<const KMFolderCachedImap*>( f->storage() )->account() ) {
01733       name = static_cast<const KMFolderCachedImap*>( f->storage() )->account()->name();
01734     }
01735   }
01736   return name;
01737 }
01738 
01739 void KMailICalIfaceImpl::changeResourceUIName( const QString &folderPath, const QString &newName )
01740 {
01741   kdDebug() << "Folder path " << folderPath << endl;
01742   KMFolder *f = findResourceFolder( folderPath );
01743   if ( f ) {
01744     KMailICalIfaceImpl::getResourceMap()->insert( folderPath, newName );
01745     kmkernel->folderMgr()->renameFolder( f, newName );
01746     KConfigGroup configGroup( kmkernel->config(), "Resource UINames" );
01747     configGroup.writeEntry( folderPath, newName );
01748   }
01749 }
01750 
01751 // Builds a folder list from the dimap and the local folder list.
01752 static void createFolderList( QStringList &folderNames, QValueList<QGuardedPtr<KMFolder> > &folderList )
01753 {
01754   QStringList dimapFolderNames;
01755   QStringList localFolderNames;
01756   QValueList<QGuardedPtr<KMFolder> > dimapFolderList;
01757   QValueList<QGuardedPtr<KMFolder> > localFolderList;
01758   kmkernel->dimapFolderMgr()->createFolderList( &dimapFolderNames, &dimapFolderList );
01759   kmkernel->folderMgr()->createFolderList( &localFolderNames, &localFolderList );
01760   folderNames += dimapFolderNames;
01761   folderNames += localFolderNames;
01762   folderList += dimapFolderList;
01763   folderList += localFolderList;
01764 }
01765 
01766 /****************************
01767  * The config stuff
01768  */
01769 
01770 void KMailICalIfaceImpl::readConfig()
01771 {
01772   bool enabled = GlobalSettings::self()->theIMAPResourceEnabled() &&
01773                  ( GlobalSettings::self()->theIMAPResourceAccount() != 0 );
01774 
01775   if( !enabled ) {
01776     if( mUseResourceIMAP == true ) {
01777       // Shutting down
01778       mUseResourceIMAP = false;
01779       cleanup();
01780       reloadFolderTree();
01781     }
01782     return;
01783   }
01784   mUseResourceIMAP = enabled;
01785 
01786   // Read remaining options
01787   const bool hideFolders = GlobalSettings::self()->hideGroupwareFolders();
01788   QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();
01789 
01790   // Find the folder parent
01791   KMFolderDir* folderParentDir;
01792   KMFolderType folderType;
01793   KMFolder* folderParent = kmkernel->findFolderById( parentName );
01794   if( folderParent == 0 ) {
01795     // Parent folder not found. It was probably deleted. The user will have to
01796     // configure things again.
01797     kdDebug(5006) << "Groupware folder " << parentName << " not found. Groupware functionality disabled" << endl;
01798     // Or maybe the inbox simply wasn't created on the first startup
01799     KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
01800     Q_ASSERT( account );
01801     if ( account ) {
01802       // just in case we were connected already
01803       disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01804                this, SLOT( slotCheckDone() ) );
01805       connect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01806                this, SLOT( slotCheckDone() ) );
01807     }
01808     mUseResourceIMAP = false;
01809     // We can't really call cleanup(), if those folders were completely deleted.
01810     mCalendar = 0;
01811     mTasks    = 0;
01812     mJournals = 0;
01813     mContacts = 0;
01814     mNotes    = 0;
01815     return;
01816   } else {
01817     folderParentDir = folderParent->createChildFolder();
01818     folderType = folderParent->folderType();
01819   }
01820 
01821   KMAcctCachedImap::GroupwareType groupwareType = dynamic_cast<KMFolderCachedImap *>( folderParent->storage() )->account()->groupwareType();
01822 
01823   if ( groupwareType == KMAcctCachedImap::GroupwareKolab ) {
01824     // Make sure the folder parent has the subdirs
01825     // Globally there are 3 cases: nothing found, some stuff found by type/name heuristics, or everything found OK
01826     bool noneFound = true;
01827     bool mustFix = false; // true when at least one was found by heuristics
01828     QValueVector<StandardFolderSearchResult> results( KMail::ContentsTypeLast + 1 );
01829     for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
01830       if ( i != KMail::ContentsTypeMail ) {
01831         results[i] = findStandardResourceFolder( folderParentDir, static_cast<KMail::FolderContentsType>(i) );
01832         if ( results[i].found == StandardFolderSearchResult::FoundAndStandard )
01833           noneFound = false;
01834         else if ( results[i].found == StandardFolderSearchResult::FoundByType ||
01835                   results[i].found == StandardFolderSearchResult::FoundByName ) {
01836           mustFix = true;
01837           noneFound = false;
01838         } else // NotFound
01839           mustFix = true;
01840       }
01841     }
01842 
01843     // Check if something changed
01844     if( mUseResourceIMAP && !noneFound && !mustFix && mFolderParentDir == folderParentDir
01845         && mFolderType == folderType ) {
01846       // Nothing changed
01847       if ( hideFolders != mHideFolders ) {
01848         // Well, the folder hiding has changed
01849         mHideFolders = hideFolders;
01850         reloadFolderTree();
01851       }
01852       return;
01853     }
01854 
01855     if( noneFound || mustFix ) {
01856       QString msg;
01857       QString parentFolderName = folderParent != 0 ? folderParent->name() : folderParentDir->name();
01858       if ( noneFound ) {
01859         // No subfolder was found, so ask if we can make them
01860         msg = i18n("KMail will now create the required groupware folders"
01861                    " as subfolders of %1; if you do not want this, cancel"
01862                    " and the IMAP resource will be disabled").arg(parentFolderName);
01863       } else {
01864         // Some subfolders were found, be more precise
01865         QString operations = "<ul>";
01866         for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
01867           if ( i != KMail::ContentsTypeMail ) {
01868             QString typeName = localizedDefaultFolderName( static_cast<KMail::FolderContentsType>( i ) );
01869             if ( results[i].found == StandardFolderSearchResult::NotFound )
01870               operations += "<li>" + i18n( "%1: no folder found. It will be created." ).arg( typeName ) + "</li>";
01871             else if ( results[i].found == StandardFolderSearchResult::FoundByType || results[i].found == StandardFolderSearchResult::FoundByName )
01872               operations += "<li>" + i18n( "%1: found folder %2. It will be set as the main groupware folder." ).
01873                             arg( typeName ).arg( results[i].folder->label() ) + "</li>";
01874           }
01875         }
01876         operations += "</ul>";
01877 
01878         msg = i18n("<qt>KMail found the following groupware folders in %1 and needs to perform the following operations: %2"
01879                    "<br>If you do not want this, cancel"
01880                    " and the IMAP resource will be disabled").arg(parentFolderName, operations);
01881 
01882       }
01883 
01884       if( KMessageBox::questionYesNo( 0, msg,
01885                                       i18n("Standard Groupware Folders"), KStdGuiItem::cont(), KStdGuiItem::cancel() ) == KMessageBox::No ) {
01886 
01887         GlobalSettings::self()->setTheIMAPResourceEnabled( false );
01888         mUseResourceIMAP = false;
01889         mFolderParentDir = 0;
01890         mFolderParent = 0;
01891         reloadFolderTree();
01892         return;
01893       }
01894     }
01895 
01896     // Make the new settings work
01897     mUseResourceIMAP = true;
01898     mFolderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
01899     if( mFolderLanguage > 3 ) mFolderLanguage = 0;
01900     mFolderParentDir = folderParentDir;
01901     mFolderParent = folderParent;
01902     mFolderType = folderType;
01903     mHideFolders = hideFolders;
01904 
01905     // Close the previous folders
01906     cleanup();
01907 
01908     // Set the new folders
01909     mCalendar = initFolder( KMail::ContentsTypeCalendar );
01910     mTasks    = initFolder( KMail::ContentsTypeTask );
01911     mJournals = initFolder( KMail::ContentsTypeJournal );
01912     mContacts = initFolder( KMail::ContentsTypeContact );
01913     mNotes    = initFolder( KMail::ContentsTypeNote );
01914 
01915     // Store final annotation (with .default) so that we won't ask again on next startup
01916     if ( mCalendar->folderType() == KMFolderTypeCachedImap )
01917       static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType();
01918     if ( mTasks->folderType() == KMFolderTypeCachedImap )
01919       static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType();
01920     if ( mJournals->folderType() == KMFolderTypeCachedImap )
01921       static_cast<KMFolderCachedImap *>( mJournals->storage() )->updateAnnotationFolderType();
01922     if ( mContacts->folderType() == KMFolderTypeCachedImap )
01923       static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType();
01924     if ( mNotes->folderType() == KMFolderTypeCachedImap )
01925       static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType();
01926 
01927     //kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
01928     //kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl;
01929     //kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;
01930 
01931     // Find all extra folders
01932     QStringList folderNames;
01933     QValueList<QGuardedPtr<KMFolder> > folderList;
01934     createFolderList( folderNames, folderList );
01935     for( QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
01936          it != folderList.end(); ++it )
01937     {
01938       FolderStorage *storage = (*it)->storage();
01939       KMFolderCachedImap* dimapStorage = dynamic_cast<KMFolderCachedImap*>( storage );
01940       if ( storage && storage->contentsType() != 0 ) {
01941         if ( dimapStorage )
01942           dimapStorage->updateAnnotationFolderType();
01943         folderContentsTypeChanged( *it, storage->contentsType() );
01944       }
01945     }
01946 
01947     // If we just created them, they might have been registered as extra folders temporarily.
01948     // -> undo that.
01949     mExtraFolders.remove( mCalendar->location() );
01950     mExtraFolders.remove( mTasks->location() );
01951     mExtraFolders.remove( mJournals->location() );
01952     mExtraFolders.remove( mContacts->location() );
01953     mExtraFolders.remove( mNotes->location() );
01954 
01955     subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true );
01956     subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true );
01957     subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(), mJournals->label(), true, false );
01958     subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false );
01959     subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false );
01960   } else if ( groupwareType == KMAcctCachedImap::GroupwareScalix ) {
01961     // Make the new settings work
01962     mUseResourceIMAP = true;
01963     mFolderParentDir = folderParentDir;
01964     mFolderParent = folderParent;
01965     mFolderType = folderType;
01966     mHideFolders = false;
01967 
01968     // Close the previous folders
01969     cleanup();
01970 
01971     // Set the new folders
01972     mCalendar = initScalixFolder( KMail::ContentsTypeCalendar );
01973     mTasks    = initScalixFolder( KMail::ContentsTypeTask );
01974     mJournals = 0;
01975     mContacts = initScalixFolder( KMail::ContentsTypeContact );
01976     mNotes    = initScalixFolder( KMail::ContentsTypeNote );
01977 
01978     // Store final annotation (with .default) so that we won't ask again on next startup
01979     if ( mCalendar->folderType() == KMFolderTypeCachedImap )
01980       static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType();
01981     if ( mTasks->folderType() == KMFolderTypeCachedImap )
01982       static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType();
01983     if ( mContacts->folderType() == KMFolderTypeCachedImap )
01984       static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType();
01985     if ( mNotes->folderType() == KMFolderTypeCachedImap )
01986       static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType();
01987 
01988     // BEGIN TILL TODO The below only uses the dimap folder manager, which
01989     // will fail for all other folder types. Adjust.
01990 
01991     kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
01992     kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl;
01993     kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;
01994 
01995     // Find all extra folders
01996     QStringList folderNames;
01997     QValueList<QGuardedPtr<KMFolder> > folderList;
01998     kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
01999     QValueList<QGuardedPtr<KMFolder> >::iterator it;
02000     for(it = folderList.begin(); it != folderList.end(); ++it)
02001     {
02002       FolderStorage *storage = (*it)->storage();
02003 
02004       if ( (*it)->folderType() == KMFolderTypeCachedImap ) {
02005         KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( storage );
02006 
02007         const QString attributes = imapFolder->folderAttributes();
02008         if ( attributes.contains( "X-FolderClass" ) ) {
02009           if ( !attributes.contains( "X-SpecialFolder" ) || (*it)->location().contains( "@" ) ) {
02010             const Scalix::FolderAttributeParser parser( attributes );
02011             if ( !parser.folderClass().isEmpty() ) {
02012               FolderContentsType type = Scalix::Utils::scalixIdToContentsType( parser.folderClass() );
02013               imapFolder->setContentsType( type );
02014               folderContentsTypeChanged( *it, type );
02015             }
02016           }
02017         }
02018       }
02019     }
02020 
02021     // If we just created them, they might have been registered as extra folders temporarily.
02022     // -> undo that.
02023     mExtraFolders.remove( mCalendar->location() );
02024     mExtraFolders.remove( mTasks->location() );
02025     mExtraFolders.remove( mContacts->location() );
02026     mExtraFolders.remove( mNotes->location() );
02027 
02028     // END TILL TODO
02029 
02030     subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true );
02031     subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true );
02032     subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false );
02033     subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false );
02034   }
02035 
02036   KConfig *config = kmkernel->config();
02037   config->setGroup("Resource UINames");
02038   *KMailICalIfaceImpl::mSubResourceUINamesMap =  config->entryMap( "Resource UINames" );
02039 
02040   reloadFolderTree();
02041 }
02042 
02043 void KMailICalIfaceImpl::slotCheckDone()
02044 {
02045   QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();
02046   KMFolder* folderParent = kmkernel->findFolderById( parentName );
02047   //kdDebug(5006) << k_funcinfo << " folderParent=" << folderParent << endl;
02048   if ( folderParent )  // cool it exists now
02049   {
02050     KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
02051     if ( account )
02052       disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
02053                   this, SLOT( slotCheckDone() ) );
02054     readConfig();
02055   }
02056 }
02057 
02058 KMFolder* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType )
02059 {
02060   // Figure out what type of folder this is supposed to be
02061   KMFolderType type = mFolderType;
02062   if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
02063 
02064   KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
02065   //kdDebug(5006) << "KMailICalIfaceImpl::initFolder " << folderName( itemType ) << endl;
02066 
02067   // Find the folder
02068   StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType );
02069 
02070   // deal with multiple default groupware folders
02071   if ( result.folders.count() > 1 && result.found == StandardFolderSearchResult::FoundAndStandard ) {
02072     QStringList labels;
02073     for ( QValueList<KMFolder*>::ConstIterator it = result.folders.begin(); it != result.folders.end(); ++it )
02074       labels << (*it)->prettyURL();
02075     const QString selected = KInputDialog::getItem( i18n("Default folder"),
02076         i18n("There are multiple %1 default folders, please choose one:")
02077         .arg( localizedDefaultFolderName( contentsType ) ), labels );
02078     if ( !selected.isEmpty() )
02079       result.folder = result.folders[ labels.findIndex( selected ) ];
02080   }
02081 
02082   KMFolder* folder = result.folder;
02083 
02084   if ( !folder ) {
02085     // The folder isn't there yet - create it
02086     folder =
02087       mFolderParentDir->createFolder( localizedDefaultFolderName( contentsType ), false, type );
02088     if( mFolderType == KMFolderTypeImap ) {
02089       KMFolderImap* parentFolder = static_cast<KMFolderImap*>( mFolderParent->storage() );
02090       parentFolder->createFolder( localizedDefaultFolderName( contentsType ) );
02091       static_cast<KMFolderImap*>( folder->storage() )->setAccount( parentFolder->account() );
02092     }
02093     // Groupware folder created, use the global setting for storage format
02094     setStorageFormat( folder, globalStorageFormat() );
02095   } else {
02096     FolderInfo info = readFolderInfo( folder );
02097     mFolderInfoMap.insert( folder, info );
02098     //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location()  << endl;
02099   }
02100 
02101   if( folder->canAccess() != 0 ) {
02102     KMessageBox::sorry(0, i18n("You do not have read/write permission to your %1 folder.")
02103                        .arg( folderName( itemType ) ) );
02104     return 0;
02105   }
02106   folder->storage()->setContentsType( contentsType );
02107   folder->setSystemFolder( true );
02108   folder->storage()->writeConfig();
02109   folder->open("ifacefolder");
02110   connectFolder( folder );
02111   return folder;
02112 }
02113 
02114 KMFolder* KMailICalIfaceImpl::initScalixFolder( KMail::FolderContentsType contentsType )
02115 {
02116   // Figure out what type of folder this is supposed to be
02117   KMFolderType type = mFolderType;
02118   if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
02119 
02120   KMFolder* folder = 0;
02121 
02122   // Find all extra folders
02123   QStringList folderNames;
02124   QValueList<QGuardedPtr<KMFolder> > folderList;
02125   Q_ASSERT( kmkernel );
02126   Q_ASSERT( kmkernel->dimapFolderMgr() );
02127   kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
02128   QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
02129   for(; it != folderList.end(); ++it)
02130   {
02131     FolderStorage *storage = (*it)->storage();
02132 
02133     if ( (*it)->folderType() == KMFolderTypeCachedImap ) {
02134       KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( storage );
02135 
02136       const QString attributes = imapFolder->folderAttributes();
02137       if ( attributes.contains( "X-SpecialFolder" ) ) {
02138         const Scalix::FolderAttributeParser parser( attributes );
02139         if ( contentsType == Scalix::Utils::scalixIdToContentsType( parser.folderClass() ) ) {
02140           folder = *it;
02141           break;
02142         }
02143       }
02144     }
02145   }
02146 
02147   if ( !folder ) {
02148     return 0;
02149   } else {
02150     FolderInfo info = readFolderInfo( folder );
02151     mFolderInfoMap.insert( folder, info );
02152     //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location()  << endl;
02153   }
02154 
02155   if( folder->canAccess() != 0 ) {
02156     KMessageBox::sorry(0, i18n("You do not have read/write permission to your folder.") );
02157     return 0;
02158   }
02159   folder->storage()->setContentsType( contentsType );
02160   folder->setSystemFolder( true );
02161   folder->storage()->writeConfig();
02162   folder->open( "scalixfolder" );
02163   connectFolder( folder );
02164   return folder;
02165 }
02166 
02167 void KMailICalIfaceImpl::connectFolder( KMFolder* folder )
02168 {
02169   // avoid multiple connections
02170   disconnect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
02171               this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
02172   disconnect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
02173               this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
02174   disconnect( folder, SIGNAL( expunged( KMFolder* ) ),
02175               this, SLOT( slotRefreshFolder( KMFolder* ) ) );
02176   disconnect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
02177               this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
02178   disconnect( folder, SIGNAL( nameChanged() ),
02179               this, SLOT( slotFolderRenamed() ) );
02180   disconnect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
02181               this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );
02182 
02183   // Setup the signals to listen for changes
02184   connect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
02185            this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
02186   connect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
02187            this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
02188   connect( folder, SIGNAL( expunged( KMFolder* ) ),
02189            this, SLOT( slotRefreshFolder( KMFolder* ) ) );
02190   connect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
02191            this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
02192   connect( folder, SIGNAL( nameChanged() ),
02193            this, SLOT( slotFolderRenamed() ) );
02194   connect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
02195            this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );
02196 
02197 }
02198 
02199 static void cleanupFolder( KMFolder* folder, KMailICalIfaceImpl* _this )
02200 {
02201   if( folder ) {
02202     folder->setSystemFolder( false );
02203     folder->disconnect( _this );
02204     folder->close("ifacefolder");
02205   }
02206 }
02207 
02208 void KMailICalIfaceImpl::cleanup()
02209 {
02210   cleanupFolder( mContacts, this );
02211   cleanupFolder( mCalendar, this );
02212   cleanupFolder( mNotes, this );
02213   cleanupFolder( mTasks, this );
02214   cleanupFolder( mJournals, this );
02215 
02216   mContacts = mCalendar = mNotes = mTasks = mJournals = 0;
02217 }
02218 
02219 QString KMailICalIfaceImpl::folderPixmap( KFolderTreeItem::Type type ) const
02220 {
02221   if( !mUseResourceIMAP )
02222     return QString::null;
02223 
02224   if( type == KFolderTreeItem::Contacts )
02225     return QString::fromLatin1( "kmgroupware_folder_contacts" );
02226   else if( type == KFolderTreeItem::Calendar )
02227     return QString::fromLatin1( "kmgroupware_folder_calendar" );
02228   else if( type == KFolderTreeItem::Notes )
02229     return QString::fromLatin1( "kmgroupware_folder_notes" );
02230   else if( type == KFolderTreeItem::Tasks )
02231     return QString::fromLatin1( "kmgroupware_folder_tasks" );
02232   else if( type == KFolderTreeItem::Journals )
02233     return QString::fromLatin1( "kmgroupware_folder_journals" );
02234 
02235   return QString::null;
02236 }
02237 
02238 static void reloadFolderTree()
02239 {
02240   // Make the folder tree show the icons or not
02241   kmkernel->folderMgr()->contentsChanged();
02242 }
02243 
02244 // This is a very light-weight and fast 'parser' to retrieve
02245 // a data entry from a vCal taking continuation lines
02246 // into account
02247 static void vPartMicroParser( const QString& str, QString& s )
02248 {
02249   QString line;
02250   uint len = str.length();
02251 
02252   for( uint i=0; i<len; ++i){
02253     if( str[i] == '\r' || str[i] == '\n' ){
02254       if( str[i] == '\r' )
02255         ++i;
02256       if( i+1 < len && str[i+1] == ' ' ){
02257         // found a continuation line, skip it's leading blanc
02258         ++i;
02259       }else{
02260         // found a logical line end, process the line
02261         if( line.startsWith( s ) ) {
02262           s = line.mid( s.length() + 1 );
02263           return;
02264         }
02265         line = "";
02266       }
02267     } else {
02268       line += str[i];
02269     }
02270   }
02271 
02272   // Not found. Clear it
02273   s.truncate(0);
02274 }
02275 
02276 // Returns the first child folder having the given annotation
02277 static QValueList<KMFolder*> findFolderByAnnotation( KMFolderDir* folderParentDir, const QString& annotation )
02278 {
02279   QValueList<KMFolder*> rv;
02280   QPtrListIterator<KMFolderNode> it( *folderParentDir );
02281   for ( ; it.current(); ++it ) {
02282     if ( !it.current()->isDir() ) {
02283       KMFolder* folder = static_cast<KMFolder *>( it.current() );
02284       if ( folder->folderType() == KMFolderTypeCachedImap ) {
02285         QString folderAnnotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
02286         //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl;
02287         if ( folderAnnotation == annotation )
02288           rv.append( folder );
02289       }
02290     }
02291   }
02292   return rv;
02293 }
02294 
02295 KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType )
02296 {
02297   if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
02298   {
02299     // Look for a folder with an annotation like "event.default"
02300     QValueList<KMFolder*> folders = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) + ".default" );
02301     if ( !folders.isEmpty() )
02302       return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundAndStandard );
02303 
02304     // Fallback: look for a folder with an annotation like "event"
02305     folders = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) );
02306     if ( !folders.isEmpty() )
02307       return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundByType );
02308 
02309     // Fallback: look for the folder by name (we'll need to change its type)
02310     KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) );
02311     if ( node && !node->isDir() )
02312       return StandardFolderSearchResult( static_cast<KMFolder *>( node ), StandardFolderSearchResult::FoundByName );
02313 
02314     kdDebug(5006) << "findStandardResourceFolder: found no resource folder for " << s_folderContentsType[contentsType].annotation << endl;
02315     return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
02316   }
02317   else // icalvcard: look up standard resource folders by name
02318   {
02319     KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
02320     unsigned int folderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
02321     if( folderLanguage > 3 ) folderLanguage = 0;
02322     KMFolderNode* node = folderParentDir->hasNamedFolder( folderName( itemType, folderLanguage ) );
02323     if ( !node || node->isDir() )
02324       return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
02325     return StandardFolderSearchResult( static_cast<KMFolder*>( node ), StandardFolderSearchResult::FoundAndStandard );
02326   }
02327 }
02328 
02329 /* We treat all folders as relevant wrt alarms for which we have Administer
02330  * rights or for which the "Incidences relevant for everyone" annotation has
02331  * been set. It can be reasonably assumed that those are "ours". All local folders
02332  * must be ours anyhow. */
02333 bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder *folder )
02334 {
02335   bool administerRights = true;
02336   bool relevantForOwner = true;
02337   bool relevantForEveryone = false;
02338   if ( folder->folderType() == KMFolderTypeImap ) {
02339     const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() );
02340     administerRights =
02341       imapFolder->userRightsState() != KMail::ACLJobs::Ok ||
02342       imapFolder->userRights() & KMail::ACLJobs::Administer;
02343   }
02344   if ( folder->folderType() == KMFolderTypeCachedImap ) {
02345     const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() );
02346     administerRights =
02347       dimapFolder->userRightsState() != KMail::ACLJobs::Ok ||
02348       dimapFolder->userRights() & KMail::ACLJobs::Administer;
02349     relevantForOwner = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor () == KMFolderCachedImap::IncForAdmins );
02350     relevantForEveryone = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor() == KMFolderCachedImap::IncForReaders );
02351   }
02352 #if 0
02353   kdDebug(5006) << k_funcinfo << endl;
02354   kdDebug(5006) << "Folder: " << folder->label() << " has administer rights: " << administerRights << endl;
02355   kdDebug(5006) << "and is relevant for owner: " << relevantForOwner <<  endl;
02356   kdDebug(5006) << "and relevant for everyone: "  << relevantForEveryone << endl;
02357 #endif
02358   return ( administerRights && relevantForOwner ) || relevantForEveryone;
02359 }
02360 
02361 void KMailICalIfaceImpl::setResourceQuiet(bool q)
02362 {
02363   //kdDebug(5006) << "DEBUG setResourceQuiet(). currently=" << mResourceQuiet << "; new=" << q << endl;
02364   mResourceQuiet = q;
02365 }
02366 
02367 bool KMailICalIfaceImpl::isResourceQuiet() const
02368 {
02369   return mResourceQuiet;
02370 }
02371 
02372 
02373 bool KMailICalIfaceImpl::addSubresource( const QString& resource,
02374                                          const QString& parent,
02375                                          const QString& contentsType )
02376 {
02377   kdDebug(5006) << "Adding subresource to parent: " << parent << " with name: " << resource << endl;
02378   kdDebug(5006) << "contents type: " << contentsType << endl;
02379   KMFolder *folder = findResourceFolder( parent );
02380   KMFolderDir *parentFolderDir = !parent.isEmpty() && folder ? folder->createChildFolder(): mFolderParentDir;
02381   if ( !parentFolderDir || parentFolderDir->hasNamedFolder( resource ) ) return false;
02382 
02383   QString msg;
02384   if ( parentFolderDir->owner() && !parentFolderDir->owner()->isValidName( resource, msg ) ) {
02385     KMessageBox::error( 0, msg );
02386     return false;
02387   }
02388 
02389   KMFolderType type = mFolderType;
02390   if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
02391 
02392   KMFolder* newFolder = parentFolderDir->createFolder( resource, false, type );
02393   if ( !newFolder ) return false;
02394   if( mFolderType == KMFolderTypeImap )
02395     static_cast<KMFolderImap*>( folder->storage() )->createFolder( resource );
02396 
02397   StorageFormat defaultFormat = GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard;
02398   setStorageFormat( newFolder, folder ? storageFormat( folder ) : defaultFormat );
02399   newFolder->storage()->setContentsType( folderContentsType( contentsType ) );
02400   newFolder->storage()->writeConfig();
02401   newFolder->open( "ical_subresource" );
02402   connectFolder( newFolder );
02403   reloadFolderTree();
02404 
02405   return true;
02406 }
02407 
02408 bool KMailICalIfaceImpl::removeSubresource( const QString& location )
02409 {
02410   kdDebug(5006) << k_funcinfo << endl;
02411 
02412   KMFolder *folder = findResourceFolder( location );
02413 
02414   // We don't allow the default folders to be deleted, so check for
02415   // those first. It would be nicer to produce a more meaningful error,
02416   // or prevent deletion of the builtin folders from the gui already.
02417   if ( !folder || isStandardResourceFolder( folder ) )
02418       return false;
02419 
02420   // the folder will be removed, which implies closed, so make sure
02421   // nothing is using it anymore first
02422   subresourceDeleted( folderContentsType( folder->storage()->contentsType() ), location );
02423   mExtraFolders.remove( location );
02424   folder->disconnect( this );
02425 
02426   if ( folder->folderType() == KMFolderTypeImap )
02427     kmkernel->imapFolderMgr()->remove( folder );
02428   else if ( folder->folderType() == KMFolderTypeCachedImap ) {
02429     // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2)
02430     KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( folder->storage() );
02431     KMAcctCachedImap* acct = storage->account();
02432     if ( acct )
02433       acct->addDeletedFolder( folder );
02434     kmkernel->dimapFolderMgr()->remove( folder );
02435   }
02436   return true;
02437 }
02438 
02439 void KMailICalIfaceImpl::syncFolder(KMFolder * folder) const
02440 {
02441   if ( kmkernel->isOffline() || !GlobalSettings::immediatlySyncDIMAPOnGroupwareChanges() )
02442     return;
02443   KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
02444   if ( !dimapFolder )
02445     return;
02446   // check if the folder exists already, otherwise sync its parent as well to create it
02447   if ( dimapFolder->imapPath().isEmpty() ) {
02448     if ( folder->parent() && folder->parent()->owner() )
02449       syncFolder( folder->parent()->owner() );
02450     else
02451       return;
02452   }
02453   dimapFolder->account()->processNewMailInFolder( folder );
02454 }
02455 
02456 KMailICalIface::Answer KMailICalIfaceImpl::messageReadyForUpdate( const QString &resource,
02457                                                                   Q_UINT32 sernum )
02458 {
02459   if ( sernum == 0 ) {
02460     // Yes, will store a new one
02461     return Yes;
02462   }
02463 
02464   KMFolder *folder = findResourceFolder( resource );
02465   if( !folder ) {
02466     kdError(5006) << "update(" << resource << ") : Not an IMAP resource folder" << endl;
02467     return Error;
02468   }
02469 
02470   folder->open( "messageReadyForUpdate" );
02471 
02472   const KMMessage *msg = findMessageBySerNum( sernum, folder );
02473 
02474   if ( !msg ) {
02475     return Error;
02476   }
02477 
02478   KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
02479   Answer answer = Yes;
02480   if ( dimapFolder ) {
02481     if ( msg->transferInProgress() || !dimapFolder->isInInitialState() ) {
02482       answer = No;
02483     }
02484   }
02485 
02486   folder->close( "messageReadyForUpdate" );
02487   return answer;
02488 }
02489 
02490 #include "kmailicalifaceimpl.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys