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 == NULL || message->subject() == subject ) {
01411         kdWarning(5006) << "Still got the wrong message: " << message->subject() << endl;
01412         KMFolderCachedImap * cimapFolder = dynamic_cast<KMFolderCachedImap *> ( aFolder->storage() );
01413         if ( cimapFolder != NULL ) {
01414           cimapFolder->markForReindexing();
01415         } else {
01416           // This can't happen,..
01417           kdWarning(5006) << "Folder is not a cached IMAP folder" << endl;
01418         }
01419         const QString errMsg = i18n("<qt>Kontact has detected a corruption in the folder: <br/> %1 <br/>"
01420                                     "Your current changes could not be saved.<br/><br/>"
01421                                     "It is <b>strongly</b> recommended that you restart Kontact now.<br/>"
01422                                     "Quit now?</qt>").arg(folder->prettyURL());
01423         if ( KMessageBox::warningYesNo( 0, errMsg, i18n("Invalid state detected") ) == KMessageBox::Yes ) {
01424           qApp->quit();
01425         } else {
01426           return 0;
01427         }
01428       } else {
01429         kdDebug(5006) << "Got the right message now, phew" << endl;
01430       }
01431     }
01432   }
01433   return message;
01434 }
01435 
01436 void KMailICalIfaceImpl::deleteMsg( KMMessage *msg )
01437 {
01438   if( !msg ) return;
01439   // Commands are now delayed; can't use that anymore, we need immediate deletion
01440   //( new KMDeleteMsgCommand( msg->parent(), msg ) )->start();
01441   KMFolder *srcFolder = msg->parent();
01442   int idx = srcFolder->find(msg);
01443   assert(idx != -1);
01444   // kill existing jobs since we are about to delete the message
01445   srcFolder->ignoreJobsForMessage( msg );
01446   if ( !msg->transferInProgress() ) {
01447     srcFolder->removeMsg(idx);
01448     delete msg;
01449   } else {
01450     kdDebug(5006) << k_funcinfo << "DEBUG Message cannot be deleted now because it is currently in use " << msg << endl;
01451     msg->deleteWhenUnused();
01452   }
01453   addFolderChange( srcFolder, Contents );
01454 }
01455 
01456 void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder,
01457                                                     KMail::FolderContentsType contentsType )
01458 {
01459   if ( !mUseResourceIMAP )
01460     return;
01461 //  kdDebug(5006) << "folderContentsTypeChanged( " << folder->name()
01462 //                << ", " << contentsType << ")\n";
01463 
01464   // The builtins can't change type
01465   if ( isStandardResourceFolder( folder ) )
01466     return;
01467 
01468   // Check if already know that 'extra folder'
01469   const QString location = folder->location();
01470   ExtraFolder* ef = mExtraFolders.find( location );
01471   if ( ef && ef->folder ) {
01472     // Notify that the old folder resource is no longer available
01473     subresourceDeleted(folderContentsType( folder->storage()->contentsType() ), location );
01474 
01475     if ( contentsType == KMail::ContentsTypeMail ) {
01476       // Delete the old entry, stop listening and stop here
01477       mExtraFolders.remove( location );
01478       folder->disconnect( this );
01479       return;
01480     }
01481     // So the type changed to another groupware type, ok.
01482   } else {
01483     if ( ef && !ef->folder ) // deleted folder, clean up
01484       mExtraFolders.remove( location );
01485     if ( contentsType == KMail::ContentsTypeMail )
01486         return;
01487 
01488     //kdDebug(5006) << "registering " << location << " as extra folder" << endl;
01489     // Make a new entry for the list
01490     ef = new ExtraFolder( folder );
01491     mExtraFolders.insert( location, ef );
01492 
01493     FolderInfo info = readFolderInfo( folder );
01494     mFolderInfoMap.insert( folder, info );
01495 
01496     // Adjust the folder names of all foo.default folders.
01497     // German users will get Kalender as the name of all default Calendar folders,
01498     // including their own, so that the default calendar folder of their Japanese
01499     // coworker appears as /user/hirohito/Kalender, although Hirohito sees his folder
01500     // in Japanese. On the server the folders are always in English.
01501     if ( folder->folderType() == KMFolderTypeCachedImap ) {
01502       QString annotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
01503       kdDebug(5006) << "folderContentsTypeChanged: " << folder->name() << " has annotation " << annotation << endl;
01504       if ( annotation == QString( s_folderContentsType[contentsType].annotation ) + ".default" )
01505         folder->setLabel( localizedDefaultFolderName( contentsType ) );
01506     }
01507 
01508     connectFolder( folder );
01509   }
01510   // Tell about the new resource
01511   subresourceAdded( folderContentsType( contentsType ), location, subresourceLabelForPresentation(folder),
01512                     folder->isWritable(), folderIsAlarmRelevant( folder ) );
01513 }
01514 
01515 KMFolder* KMailICalIfaceImpl::extraFolder( const QString& type,
01516                                            const QString& folder )
01517 {
01518   // If an extra folder exists that matches the type and folder location,
01519   // use that
01520   int t = folderContentsType( type );
01521   if ( t < 1 || t > 5 )
01522     return 0;
01523 
01524   ExtraFolder* ef = mExtraFolders.find( folder );
01525   if ( ef && ef->folder && ef->folder->storage()->contentsType() == t )
01526     return ef->folder;
01527 
01528   return 0;
01529 }
01530 
01531 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( KMFolder* folder ) const
01532 {
01533   FolderInfoMap::ConstIterator it = mFolderInfoMap.find( folder );
01534   if ( it != mFolderInfoMap.end() )
01535     return (*it).mStorageFormat;
01536   return globalStorageFormat();
01537 }
01538 
01539 void KMailICalIfaceImpl::setStorageFormat( KMFolder* folder, StorageFormat format )
01540 {
01541   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01542   if ( it != mFolderInfoMap.end() ) {
01543     (*it).mStorageFormat = format;
01544   } else {
01545     FolderInfo info( format, NoChange );
01546     mFolderInfoMap.insert( folder, info );
01547   }
01548   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01549   configGroup.writeEntry( folder->idString() + "-storageFormat",
01550                           format == StorageXML ? "xml" : "icalvcard" );
01551 }
01552 
01553 void KMailICalIfaceImpl::addFolderChange( KMFolder* folder, FolderChanges changes )
01554 {
01555   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01556   if ( it != mFolderInfoMap.end() ) {
01557     (*it).mChanges = static_cast<FolderChanges>( (*it).mChanges | changes );
01558   } else { // Otherwise, well, it's a folder we don't care about.
01559     kdDebug(5006) << "addFolderChange: nothing known about folder " << folder->location() << endl;
01560   }
01561   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01562   configGroup.writeEntry( folder->idString() + "-changes", (*it).mChanges );
01563 }
01564 
01565 KMailICalIfaceImpl::FolderInfo KMailICalIfaceImpl::readFolderInfo( const KMFolder * const folder ) const
01566 {
01567   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01568   QString str = configGroup.readEntry( folder->idString() + "-storageFormat", "unset" );
01569   FolderInfo info;
01570   if ( str == "unset" ) {
01571     info.mStorageFormat = globalStorageFormat();
01572     configGroup.writeEntry( folder->idString() + "-storageFormat",
01573                             info.mStorageFormat == StorageXML ? "xml" : "icalvcard" );
01574   } else {
01575     info.mStorageFormat = ( str == "xml" ) ? StorageXML : StorageIcalVcard;
01576   }
01577   info.mChanges = (FolderChanges) configGroup.readNumEntry( folder->idString() + "-changes" );
01578   return info;
01579 }
01580 
01581 
01582 void KMailICalIfaceImpl::folderSynced( KMFolder* folder, const KURL& folderURL )
01583 {
01584   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01585   if ( it != mFolderInfoMap.end() && (*it).mChanges ) {
01586     handleFolderSynced( folder, folderURL, (*it).mChanges );
01587     (*it).mChanges = NoChange;
01588   }
01589 }
01590 
01591 void KMailICalIfaceImpl::handleFolderSynced( KMFolder* folder,
01592                                              const KURL& folderURL,
01593                                              int _changes )
01594 {
01595   // This is done here instead of in the resource, because
01596   // there could be 0, 1, or N kolab resources at this point.
01597   // We can hack the N case, but not the 0 case.
01598   // So the idea of a DCOP signal for this wouldn't work.
01599   if ( ( _changes & KMailICalIface::Contents ) ||
01600        ( _changes & KMailICalIface::ACL ) ) {
01601     if ( storageFormat( folder ) == StorageXML && folder->storage()->contentsType() == KMail::ContentsTypeCalendar )
01602       triggerKolabFreeBusy( folderURL, true /*report errors*/ );
01603   }
01604 }
01605 
01606 void KMailICalIfaceImpl::folderDeletedOnServer( const KURL& folderURL )
01607 {
01608   triggerKolabFreeBusy( folderURL, false /*do not report errors*/ );
01609 }
01610 
01611 void KMailICalIfaceImpl::triggerKolabFreeBusy( const KURL& folderURL, bool report )
01612 {
01613   /* Steffen said: you must issue an authenticated HTTP GET request to
01614      https://kolabserver/freebusy/trigger/user@domain/Folder/NestedFolder.pfb
01615      (replace .pfb with .xpfb for extended fb lists). */
01616   KURL httpURL( folderURL );
01617   // Keep username ("user@domain"), pass, and host from the imap url
01618   httpURL.setProtocol( "https" );
01619   httpURL.setPort( 0 ); // remove imap port
01620 
01621   // IMAP path is either /INBOX/<path> or /user/someone/<path>
01622   QString path = folderURL.path( -1 );
01623   Q_ASSERT( path.startsWith( "/" ) );
01624   int secondSlash = path.find( '/', 1 );
01625   if ( secondSlash == -1 ) {
01626     kdWarning() << "KCal::ResourceKolab::fromKMailFolderSynced path is too short: " << path << endl;
01627     return;
01628   }
01629   if ( path.startsWith( "/INBOX/", false ) ) {
01630     // If INBOX, replace it with the username (which is user@domain)
01631     path = path.mid( secondSlash );
01632     path.prepend( folderURL.user() );
01633   } else {
01634     // If user, just remove it. So we keep the IMAP-returned username.
01635     // This assumes it's a known user on the same domain.
01636     path = path.mid( secondSlash );
01637   }
01638 
01639   if ( path.startsWith( "/" ) )
01640     httpURL.setPath( "/freebusy/trigger" + path + ".pfb" );
01641   else
01642     httpURL.setPath( "/freebusy/trigger/" + path + ".pfb" );
01643 
01644   httpURL.setQuery( QString::null );
01645   // Ensure that we encode everything with UTF8
01646   httpURL = KURL( httpURL.url(0,106), 106 );
01647   kdDebug() << "Triggering PFB update for " << folderURL << " : getting " << httpURL << endl;
01648 
01649   if ( report ) {
01650     KIO::Job* job = KIO::get( httpURL, false, false /*no progress info*/ );
01651     job->addMetaData( "errorPage", "false" ); // we want an error in case of 404
01652     connect( job, SIGNAL( result( KIO::Job* ) ), SLOT( slotFreeBusyTriggerResult( KIO::Job* ) ) );
01653   }
01654 }
01655 
01656 void KMailICalIfaceImpl::slotFreeBusyTriggerResult( KIO::Job *job )
01657 {
01658   if ( job->error() ) {
01659     KURL url( job->errorText() );
01660     KMessageBox::sorry(0, i18n("Could not trigger Free/Busy information update: %1.").arg( url.prettyURL() ) );
01661   }
01662 }
01663 
01664 void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder )
01665 {
01666   if ( isResourceFolder( folder ) ) {
01667     const QString location = folder->location();
01668     const QString contentsTypeStr = folderContentsType( folder->storage()->contentsType() );
01669     subresourceDeleted( contentsTypeStr, location );
01670 
01671     subresourceAdded( contentsTypeStr, location, subresourceLabelForPresentation( folder ),
01672                       folder->isWritable(), folderIsAlarmRelevant( folder ) );
01673   }
01674 }
01675 
01676 // Must only be connected to a signal from KMFolder!
01677 void KMailICalIfaceImpl::slotFolderRenamed()
01678 {
01679   const KMFolder* folder = static_cast<const KMFolder *>( sender() );
01680   slotFolderPropertiesChanged( const_cast<KMFolder*>( folder ) );
01681 }
01682 
01683 void KMailICalIfaceImpl::slotFolderLocationChanged( const QString &oldLocation,
01684                                                     const QString &newLocation )
01685 {
01686   KMFolder *folder = findResourceFolder( oldLocation );
01687   ExtraFolder* ef = mExtraFolders.find( oldLocation );
01688   if ( ef ) {
01689     // reuse the ExtraFolder entry, but adjust the key
01690     mExtraFolders.setAutoDelete( false );
01691     mExtraFolders.remove( oldLocation );
01692     mExtraFolders.setAutoDelete( true );
01693     mExtraFolders.insert( newLocation, ef );
01694   }
01695   if (  folder )
01696     subresourceDeleted( folderContentsType(  folder->storage()->contentsType() ), oldLocation );
01697 
01698 }
01699 
01700 KMFolder* KMailICalIfaceImpl::findResourceFolder( const QString& resource )
01701 {
01702   // Try the standard folders
01703   if( mCalendar && mCalendar->location() == resource )
01704     return mCalendar;
01705   if ( mContacts && mContacts->location() == resource )
01706     return mContacts;
01707   if ( mNotes && mNotes->location() == resource )
01708     return mNotes;
01709   if ( mTasks && mTasks->location() == resource )
01710     return mTasks;
01711   if ( mJournals && mJournals->location() == resource )
01712     return mJournals;
01713 
01714   // No luck. Try the extrafolders
01715   ExtraFolder* ef = mExtraFolders.find( resource );
01716   if ( ef )
01717     return ef->folder;
01718 
01719   // No luck at all
01720   return 0;
01721 }
01722 
01723 QString KMailICalIfaceImpl::dimapFolderAccountName( const QString &folderPath )
01724 {
01725   QString name;
01726   KMFolder *f = findResourceFolder( folderPath );
01727   if ( f ) {
01728     if ( f->storage() && static_cast<const KMFolderCachedImap*>( f->storage() )->account() ) {
01729       name = static_cast<const KMFolderCachedImap*>( f->storage() )->account()->name();
01730     }
01731   }
01732   return name;
01733 }
01734 
01735 void KMailICalIfaceImpl::changeResourceUIName( const QString &folderPath, const QString &newName )
01736 {
01737   kdDebug() << "Folder path " << folderPath << endl;
01738   KMFolder *f = findResourceFolder( folderPath );
01739   if ( f ) {
01740     KMailICalIfaceImpl::getResourceMap()->insert( folderPath, newName );
01741     kmkernel->folderMgr()->renameFolder( f, newName );
01742     KConfigGroup configGroup( kmkernel->config(), "Resource UINames" );
01743     configGroup.writeEntry( folderPath, newName );
01744   }
01745 }
01746 
01747 // Builds a folder list from the dimap and the local folder list.
01748 static void createFolderList( QStringList &folderNames, QValueList<QGuardedPtr<KMFolder> > &folderList )
01749 {
01750   QStringList dimapFolderNames;
01751   QStringList localFolderNames;
01752   QValueList<QGuardedPtr<KMFolder> > dimapFolderList;
01753   QValueList<QGuardedPtr<KMFolder> > localFolderList;
01754   kmkernel->dimapFolderMgr()->createFolderList( &dimapFolderNames, &dimapFolderList );
01755   kmkernel->folderMgr()->createFolderList( &localFolderNames, &localFolderList );
01756   folderNames += dimapFolderNames;
01757   folderNames += localFolderNames;
01758   folderList += dimapFolderList;
01759   folderList += localFolderList;
01760 }
01761 
01762 /****************************
01763  * The config stuff
01764  */
01765 
01766 void KMailICalIfaceImpl::readConfig()
01767 {
01768   bool enabled = GlobalSettings::self()->theIMAPResourceEnabled() &&
01769                  ( GlobalSettings::self()->theIMAPResourceAccount() != 0 );
01770 
01771   if( !enabled ) {
01772     if( mUseResourceIMAP == true ) {
01773       // Shutting down
01774       mUseResourceIMAP = false;
01775       cleanup();
01776       reloadFolderTree();
01777     }
01778     return;
01779   }
01780   mUseResourceIMAP = enabled;
01781 
01782   // Read remaining options
01783   const bool hideFolders = GlobalSettings::self()->hideGroupwareFolders();
01784   QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();
01785 
01786   // Find the folder parent
01787   KMFolderDir* folderParentDir;
01788   KMFolderType folderType;
01789   KMFolder* folderParent = kmkernel->findFolderById( parentName );
01790   if( folderParent == 0 ) {
01791     // Parent folder not found. It was probably deleted. The user will have to
01792     // configure things again.
01793     kdDebug(5006) << "Groupware folder " << parentName << " not found. Groupware functionality disabled" << endl;
01794     // Or maybe the inbox simply wasn't created on the first startup
01795     KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
01796     Q_ASSERT( account );
01797     if ( account ) {
01798       // just in case we were connected already
01799       disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01800                this, SLOT( slotCheckDone() ) );
01801       connect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01802                this, SLOT( slotCheckDone() ) );
01803     }
01804     mUseResourceIMAP = false;
01805     // We can't really call cleanup(), if those folders were completely deleted.
01806     mCalendar = 0;
01807     mTasks    = 0;
01808     mJournals = 0;
01809     mContacts = 0;
01810     mNotes    = 0;
01811     return;
01812   } else {
01813     folderParentDir = folderParent->createChildFolder();
01814     folderType = folderParent->folderType();
01815   }
01816 
01817   KMAcctCachedImap::GroupwareType groupwareType = dynamic_cast<KMFolderCachedImap *>( folderParent->storage() )->account()->groupwareType();
01818 
01819   if ( groupwareType == KMAcctCachedImap::GroupwareKolab ) {
01820     // Make sure the folder parent has the subdirs
01821     // Globally there are 3 cases: nothing found, some stuff found by type/name heuristics, or everything found OK
01822     bool noneFound = true;
01823     bool mustFix = false; // true when at least one was found by heuristics
01824     QValueVector<StandardFolderSearchResult> results( KMail::ContentsTypeLast + 1 );
01825     for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
01826       if ( i != KMail::ContentsTypeMail ) {
01827         results[i] = findStandardResourceFolder( folderParentDir, static_cast<KMail::FolderContentsType>(i) );
01828         if ( results[i].found == StandardFolderSearchResult::FoundAndStandard )
01829           noneFound = false;
01830         else if ( results[i].found == StandardFolderSearchResult::FoundByType ||
01831                   results[i].found == StandardFolderSearchResult::FoundByName ) {
01832           mustFix = true;
01833           noneFound = false;
01834         } else // NotFound
01835           mustFix = true;
01836       }
01837     }
01838 
01839     // Check if something changed
01840     if( mUseResourceIMAP && !noneFound && !mustFix && mFolderParentDir == folderParentDir
01841         && mFolderType == folderType ) {
01842       // Nothing changed
01843       if ( hideFolders != mHideFolders ) {
01844         // Well, the folder hiding has changed
01845         mHideFolders = hideFolders;
01846         reloadFolderTree();
01847       }
01848       return;
01849     }
01850 
01851     if( noneFound || mustFix ) {
01852       QString msg;
01853       QString parentFolderName = folderParent != 0 ? folderParent->name() : folderParentDir->name();
01854       if ( noneFound ) {
01855         // No subfolder was found, so ask if we can make them
01856         msg = i18n("KMail will now create the required groupware folders"
01857                    " as subfolders of %1; if you do not want this, cancel"
01858                    " and the IMAP resource will be disabled").arg(parentFolderName);
01859       } else {
01860         // Some subfolders were found, be more precise
01861         QString operations = "<ul>";
01862         for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
01863           if ( i != KMail::ContentsTypeMail ) {
01864             QString typeName = localizedDefaultFolderName( static_cast<KMail::FolderContentsType>( i ) );
01865             if ( results[i].found == StandardFolderSearchResult::NotFound )
01866               operations += "<li>" + i18n( "%1: no folder found. It will be created." ).arg( typeName ) + "</li>";
01867             else if ( results[i].found == StandardFolderSearchResult::FoundByType || results[i].found == StandardFolderSearchResult::FoundByName )
01868               operations += "<li>" + i18n( "%1: found folder %2. It will be set as the main groupware folder." ).
01869                             arg( typeName ).arg( results[i].folder->label() ) + "</li>";
01870           }
01871         }
01872         operations += "</ul>";
01873 
01874         msg = i18n("<qt>KMail found the following groupware folders in %1 and needs to perform the following operations: %2"
01875                    "<br>If you do not want this, cancel"
01876                    " and the IMAP resource will be disabled").arg(parentFolderName, operations);
01877 
01878       }
01879 
01880       if( KMessageBox::questionYesNo( 0, msg,
01881                                       i18n("Standard Groupware Folders"), KStdGuiItem::cont(), KStdGuiItem::cancel() ) == KMessageBox::No ) {
01882 
01883         GlobalSettings::self()->setTheIMAPResourceEnabled( false );
01884         mUseResourceIMAP = false;
01885         mFolderParentDir = 0;
01886         mFolderParent = 0;
01887         reloadFolderTree();
01888         return;
01889       }
01890     }
01891 
01892     // Make the new settings work
01893     mUseResourceIMAP = true;
01894     mFolderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
01895     if( mFolderLanguage > 3 ) mFolderLanguage = 0;
01896     mFolderParentDir = folderParentDir;
01897     mFolderParent = folderParent;
01898     mFolderType = folderType;
01899     mHideFolders = hideFolders;
01900 
01901     // Close the previous folders
01902     cleanup();
01903 
01904     // Set the new folders
01905     mCalendar = initFolder( KMail::ContentsTypeCalendar );
01906     mTasks    = initFolder( KMail::ContentsTypeTask );
01907     mJournals = initFolder( KMail::ContentsTypeJournal );
01908     mContacts = initFolder( KMail::ContentsTypeContact );
01909     mNotes    = initFolder( KMail::ContentsTypeNote );
01910 
01911     // Store final annotation (with .default) so that we won't ask again on next startup
01912     if ( mCalendar->folderType() == KMFolderTypeCachedImap )
01913       static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType();
01914     if ( mTasks->folderType() == KMFolderTypeCachedImap )
01915       static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType();
01916     if ( mJournals->folderType() == KMFolderTypeCachedImap )
01917       static_cast<KMFolderCachedImap *>( mJournals->storage() )->updateAnnotationFolderType();
01918     if ( mContacts->folderType() == KMFolderTypeCachedImap )
01919       static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType();
01920     if ( mNotes->folderType() == KMFolderTypeCachedImap )
01921       static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType();
01922 
01923     //kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
01924     //kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl;
01925     //kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;
01926 
01927     // Find all extra folders
01928     QStringList folderNames;
01929     QValueList<QGuardedPtr<KMFolder> > folderList;
01930     createFolderList( folderNames, folderList );
01931     for( QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
01932          it != folderList.end(); ++it )
01933     {
01934       FolderStorage *storage = (*it)->storage();
01935       KMFolderCachedImap* dimapStorage = dynamic_cast<KMFolderCachedImap*>( storage );
01936       if ( storage && storage->contentsType() != 0 ) {
01937         if ( dimapStorage )
01938           dimapStorage->updateAnnotationFolderType();
01939         folderContentsTypeChanged( *it, storage->contentsType() );
01940       }
01941     }
01942 
01943     // If we just created them, they might have been registered as extra folders temporarily.
01944     // -> undo that.
01945     mExtraFolders.remove( mCalendar->location() );
01946     mExtraFolders.remove( mTasks->location() );
01947     mExtraFolders.remove( mJournals->location() );
01948     mExtraFolders.remove( mContacts->location() );
01949     mExtraFolders.remove( mNotes->location() );
01950 
01951     subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true );
01952     subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true );
01953     subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(), mJournals->label(), true, false );
01954     subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false );
01955     subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false );
01956   } else if ( groupwareType == KMAcctCachedImap::GroupwareScalix ) {
01957     // Make the new settings work
01958     mUseResourceIMAP = true;
01959     mFolderParentDir = folderParentDir;
01960     mFolderParent = folderParent;
01961     mFolderType = folderType;
01962     mHideFolders = false;
01963 
01964     // Close the previous folders
01965     cleanup();
01966 
01967     // Set the new folders
01968     mCalendar = initScalixFolder( KMail::ContentsTypeCalendar );
01969     mTasks    = initScalixFolder( KMail::ContentsTypeTask );
01970     mJournals = 0;
01971     mContacts = initScalixFolder( KMail::ContentsTypeContact );
01972     mNotes    = initScalixFolder( KMail::ContentsTypeNote );
01973 
01974     // Store final annotation (with .default) so that we won't ask again on next startup
01975     if ( mCalendar->folderType() == KMFolderTypeCachedImap )
01976       static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType();
01977     if ( mTasks->folderType() == KMFolderTypeCachedImap )
01978       static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType();
01979     if ( mContacts->folderType() == KMFolderTypeCachedImap )
01980       static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType();
01981     if ( mNotes->folderType() == KMFolderTypeCachedImap )
01982       static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType();
01983 
01984     // BEGIN TILL TODO The below only uses the dimap folder manager, which
01985     // will fail for all other folder types. Adjust.
01986 
01987     kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
01988     kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl;
01989     kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;
01990 
01991     // Find all extra folders
01992     QStringList folderNames;
01993     QValueList<QGuardedPtr<KMFolder> > folderList;
01994     kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
01995     QValueList<QGuardedPtr<KMFolder> >::iterator it;
01996     for(it = folderList.begin(); it != folderList.end(); ++it)
01997     {
01998       FolderStorage *storage = (*it)->storage();
01999 
02000       if ( (*it)->folderType() == KMFolderTypeCachedImap ) {
02001         KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( storage );
02002 
02003         const QString attributes = imapFolder->folderAttributes();
02004         if ( attributes.contains( "X-FolderClass" ) ) {
02005           if ( !attributes.contains( "X-SpecialFolder" ) || (*it)->location().contains( "@" ) ) {
02006             const Scalix::FolderAttributeParser parser( attributes );
02007             if ( !parser.folderClass().isEmpty() ) {
02008               FolderContentsType type = Scalix::Utils::scalixIdToContentsType( parser.folderClass() );
02009               imapFolder->setContentsType( type );
02010               folderContentsTypeChanged( *it, type );
02011             }
02012           }
02013         }
02014       }
02015     }
02016 
02017     // If we just created them, they might have been registered as extra folders temporarily.
02018     // -> undo that.
02019     mExtraFolders.remove( mCalendar->location() );
02020     mExtraFolders.remove( mTasks->location() );
02021     mExtraFolders.remove( mContacts->location() );
02022     mExtraFolders.remove( mNotes->location() );
02023 
02024     // END TILL TODO
02025 
02026     subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true );
02027     subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true );
02028     subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false );
02029     subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false );
02030   }
02031 
02032   KConfig *config = kmkernel->config();
02033   config->setGroup("Resource UINames");
02034   *KMailICalIfaceImpl::mSubResourceUINamesMap =  config->entryMap( "Resource UINames" );
02035 
02036   reloadFolderTree();
02037 }
02038 
02039 void KMailICalIfaceImpl::slotCheckDone()
02040 {
02041   QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();
02042   KMFolder* folderParent = kmkernel->findFolderById( parentName );
02043   //kdDebug(5006) << k_funcinfo << " folderParent=" << folderParent << endl;
02044   if ( folderParent )  // cool it exists now
02045   {
02046     KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
02047     if ( account )
02048       disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
02049                   this, SLOT( slotCheckDone() ) );
02050     readConfig();
02051   }
02052 }
02053 
02054 KMFolder* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType )
02055 {
02056   // Figure out what type of folder this is supposed to be
02057   KMFolderType type = mFolderType;
02058   if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
02059 
02060   KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
02061   //kdDebug(5006) << "KMailICalIfaceImpl::initFolder " << folderName( itemType ) << endl;
02062 
02063   // Find the folder
02064   StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType );
02065 
02066   // deal with multiple default groupware folders
02067   if ( result.folders.count() > 1 && result.found == StandardFolderSearchResult::FoundAndStandard ) {
02068     QStringList labels;
02069     for ( QValueList<KMFolder*>::ConstIterator it = result.folders.begin(); it != result.folders.end(); ++it )
02070       labels << (*it)->prettyURL();
02071     const QString selected = KInputDialog::getItem( i18n("Default folder"),
02072         i18n("There are multiple %1 default folders, please choose one:")
02073         .arg( localizedDefaultFolderName( contentsType ) ), labels );
02074     if ( !selected.isEmpty() )
02075       result.folder = result.folders[ labels.findIndex( selected ) ];
02076   }
02077 
02078   KMFolder* folder = result.folder;
02079 
02080   if ( !folder ) {
02081     // The folder isn't there yet - create it
02082     folder =
02083       mFolderParentDir->createFolder( localizedDefaultFolderName( contentsType ), false, type );
02084     if( mFolderType == KMFolderTypeImap ) {
02085       KMFolderImap* parentFolder = static_cast<KMFolderImap*>( mFolderParent->storage() );
02086       parentFolder->createFolder( localizedDefaultFolderName( contentsType ) );
02087       static_cast<KMFolderImap*>( folder->storage() )->setAccount( parentFolder->account() );
02088     }
02089     // Groupware folder created, use the global setting for storage format
02090     setStorageFormat( folder, globalStorageFormat() );
02091   } else {
02092     FolderInfo info = readFolderInfo( folder );
02093     mFolderInfoMap.insert( folder, info );
02094     //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location()  << endl;
02095   }
02096 
02097   if( folder->canAccess() != 0 ) {
02098     KMessageBox::sorry(0, i18n("You do not have read/write permission to your %1 folder.")
02099                        .arg( folderName( itemType ) ) );
02100     return 0;
02101   }
02102   folder->storage()->setContentsType( contentsType );
02103   folder->setSystemFolder( true );
02104   folder->storage()->writeConfig();
02105   folder->open("ifacefolder");
02106   connectFolder( folder );
02107   return folder;
02108 }
02109 
02110 KMFolder* KMailICalIfaceImpl::initScalixFolder( KMail::FolderContentsType contentsType )
02111 {
02112   // Figure out what type of folder this is supposed to be
02113   KMFolderType type = mFolderType;
02114   if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
02115 
02116   KMFolder* folder = 0;
02117 
02118   // Find all extra folders
02119   QStringList folderNames;
02120   QValueList<QGuardedPtr<KMFolder> > folderList;
02121   Q_ASSERT( kmkernel );
02122   Q_ASSERT( kmkernel->dimapFolderMgr() );
02123   kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
02124   QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
02125   for(; it != folderList.end(); ++it)
02126   {
02127     FolderStorage *storage = (*it)->storage();
02128 
02129     if ( (*it)->folderType() == KMFolderTypeCachedImap ) {
02130       KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( storage );
02131 
02132       const QString attributes = imapFolder->folderAttributes();
02133       if ( attributes.contains( "X-SpecialFolder" ) ) {
02134         const Scalix::FolderAttributeParser parser( attributes );
02135         if ( contentsType == Scalix::Utils::scalixIdToContentsType( parser.folderClass() ) ) {
02136           folder = *it;
02137           break;
02138         }
02139       }
02140     }
02141   }
02142 
02143   if ( !folder ) {
02144     return 0;
02145   } else {
02146     FolderInfo info = readFolderInfo( folder );
02147     mFolderInfoMap.insert( folder, info );
02148     //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location()  << endl;
02149   }
02150 
02151   if( folder->canAccess() != 0 ) {
02152     KMessageBox::sorry(0, i18n("You do not have read/write permission to your folder.") );
02153     return 0;
02154   }
02155   folder->storage()->setContentsType( contentsType );
02156   folder->setSystemFolder( true );
02157   folder->storage()->writeConfig();
02158   folder->open( "scalixfolder" );
02159   connectFolder( folder );
02160   return folder;
02161 }
02162 
02163 void KMailICalIfaceImpl::connectFolder( KMFolder* folder )
02164 {
02165   // avoid multiple connections
02166   disconnect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
02167               this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
02168   disconnect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
02169               this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
02170   disconnect( folder, SIGNAL( expunged( KMFolder* ) ),
02171               this, SLOT( slotRefreshFolder( KMFolder* ) ) );
02172   disconnect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
02173               this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
02174   disconnect( folder, SIGNAL( nameChanged() ),
02175               this, SLOT( slotFolderRenamed() ) );
02176   disconnect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
02177               this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );
02178 
02179   // Setup the signals to listen for changes
02180   connect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
02181            this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
02182   connect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
02183            this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
02184   connect( folder, SIGNAL( expunged( KMFolder* ) ),
02185            this, SLOT( slotRefreshFolder( KMFolder* ) ) );
02186   connect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
02187            this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
02188   connect( folder, SIGNAL( nameChanged() ),
02189            this, SLOT( slotFolderRenamed() ) );
02190   connect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
02191            this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );
02192 
02193 }
02194 
02195 static void cleanupFolder( KMFolder* folder, KMailICalIfaceImpl* _this )
02196 {
02197   if( folder ) {
02198     folder->setSystemFolder( false );
02199     folder->disconnect( _this );
02200     folder->close("ifacefolder");
02201   }
02202 }
02203 
02204 void KMailICalIfaceImpl::cleanup()
02205 {
02206   cleanupFolder( mContacts, this );
02207   cleanupFolder( mCalendar, this );
02208   cleanupFolder( mNotes, this );
02209   cleanupFolder( mTasks, this );
02210   cleanupFolder( mJournals, this );
02211 
02212   mContacts = mCalendar = mNotes = mTasks = mJournals = 0;
02213 }
02214 
02215 QString KMailICalIfaceImpl::folderPixmap( KFolderTreeItem::Type type ) const
02216 {
02217   if( !mUseResourceIMAP )
02218     return QString::null;
02219 
02220   if( type == KFolderTreeItem::Contacts )
02221     return QString::fromLatin1( "kmgroupware_folder_contacts" );
02222   else if( type == KFolderTreeItem::Calendar )
02223     return QString::fromLatin1( "kmgroupware_folder_calendar" );
02224   else if( type == KFolderTreeItem::Notes )
02225     return QString::fromLatin1( "kmgroupware_folder_notes" );
02226   else if( type == KFolderTreeItem::Tasks )
02227     return QString::fromLatin1( "kmgroupware_folder_tasks" );
02228   else if( type == KFolderTreeItem::Journals )
02229     return QString::fromLatin1( "kmgroupware_folder_journals" );
02230 
02231   return QString::null;
02232 }
02233 
02234 static void reloadFolderTree()
02235 {
02236   // Make the folder tree show the icons or not
02237   kmkernel->folderMgr()->contentsChanged();
02238 }
02239 
02240 // This is a very light-weight and fast 'parser' to retrieve
02241 // a data entry from a vCal taking continuation lines
02242 // into account
02243 static void vPartMicroParser( const QString& str, QString& s )
02244 {
02245   QString line;
02246   uint len = str.length();
02247 
02248   for( uint i=0; i<len; ++i){
02249     if( str[i] == '\r' || str[i] == '\n' ){
02250       if( str[i] == '\r' )
02251         ++i;
02252       if( i+1 < len && str[i+1] == ' ' ){
02253         // found a continuation line, skip it's leading blanc
02254         ++i;
02255       }else{
02256         // found a logical line end, process the line
02257         if( line.startsWith( s ) ) {
02258           s = line.mid( s.length() + 1 );
02259           return;
02260         }
02261         line = "";
02262       }
02263     } else {
02264       line += str[i];
02265     }
02266   }
02267 
02268   // Not found. Clear it
02269   s.truncate(0);
02270 }
02271 
02272 // Returns the first child folder having the given annotation
02273 static QValueList<KMFolder*> findFolderByAnnotation( KMFolderDir* folderParentDir, const QString& annotation )
02274 {
02275   QValueList<KMFolder*> rv;
02276   QPtrListIterator<KMFolderNode> it( *folderParentDir );
02277   for ( ; it.current(); ++it ) {
02278     if ( !it.current()->isDir() ) {
02279       KMFolder* folder = static_cast<KMFolder *>( it.current() );
02280       if ( folder->folderType() == KMFolderTypeCachedImap ) {
02281         QString folderAnnotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
02282         //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl;
02283         if ( folderAnnotation == annotation )
02284           rv.append( folder );
02285       }
02286     }
02287   }
02288   return rv;
02289 }
02290 
02291 KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType )
02292 {
02293   if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
02294   {
02295     // Look for a folder with an annotation like "event.default"
02296     QValueList<KMFolder*> folders = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) + ".default" );
02297     if ( !folders.isEmpty() )
02298       return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundAndStandard );
02299 
02300     // Fallback: look for a folder with an annotation like "event"
02301     folders = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) );
02302     if ( !folders.isEmpty() )
02303       return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundByType );
02304 
02305     // Fallback: look for the folder by name (we'll need to change its type)
02306     KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) );
02307     if ( node && !node->isDir() )
02308       return StandardFolderSearchResult( static_cast<KMFolder *>( node ), StandardFolderSearchResult::FoundByName );
02309 
02310     kdDebug(5006) << "findStandardResourceFolder: found no resource folder for " << s_folderContentsType[contentsType].annotation << endl;
02311     return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
02312   }
02313   else // icalvcard: look up standard resource folders by name
02314   {
02315     KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
02316     unsigned int folderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
02317     if( folderLanguage > 3 ) folderLanguage = 0;
02318     KMFolderNode* node = folderParentDir->hasNamedFolder( folderName( itemType, folderLanguage ) );
02319     if ( !node || node->isDir() )
02320       return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
02321     return StandardFolderSearchResult( static_cast<KMFolder*>( node ), StandardFolderSearchResult::FoundAndStandard );
02322   }
02323 }
02324 
02325 /* We treat all folders as relevant wrt alarms for which we have Administer
02326  * rights or for which the "Incidences relevant for everyone" annotation has
02327  * been set. It can be reasonably assumed that those are "ours". All local folders
02328  * must be ours anyhow. */
02329 bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder *folder )
02330 {
02331   bool administerRights = true;
02332   bool relevantForOwner = true;
02333   bool relevantForEveryone = false;
02334   if ( folder->folderType() == KMFolderTypeImap ) {
02335     const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() );
02336     administerRights =
02337       imapFolder->userRightsState() != KMail::ACLJobs::Ok ||
02338       imapFolder->userRights() & KMail::ACLJobs::Administer;
02339   }
02340   if ( folder->folderType() == KMFolderTypeCachedImap ) {
02341     const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() );
02342     administerRights =
02343       dimapFolder->userRightsState() != KMail::ACLJobs::Ok ||
02344       dimapFolder->userRights() & KMail::ACLJobs::Administer;
02345     relevantForOwner = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor () == KMFolderCachedImap::IncForAdmins );
02346     relevantForEveryone = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor() == KMFolderCachedImap::IncForReaders );
02347   }
02348 #if 0
02349   kdDebug(5006) << k_funcinfo << endl;
02350   kdDebug(5006) << "Folder: " << folder->label() << " has administer rights: " << administerRights << endl;
02351   kdDebug(5006) << "and is relevant for owner: " << relevantForOwner <<  endl;
02352   kdDebug(5006) << "and relevant for everyone: "  << relevantForEveryone << endl;
02353 #endif
02354   return ( administerRights && relevantForOwner ) || relevantForEveryone;
02355 }
02356 
02357 void KMailICalIfaceImpl::setResourceQuiet(bool q)
02358 {
02359   //kdDebug(5006) << "DEBUG setResourceQuiet(). currently=" << mResourceQuiet << "; new=" << q << endl;
02360   mResourceQuiet = q;
02361 }
02362 
02363 bool KMailICalIfaceImpl::isResourceQuiet() const
02364 {
02365   return mResourceQuiet;
02366 }
02367 
02368 
02369 bool KMailICalIfaceImpl::addSubresource( const QString& resource,
02370                                          const QString& parent,
02371                                          const QString& contentsType )
02372 {
02373   kdDebug(5006) << "Adding subresource to parent: " << parent << " with name: " << resource << endl;
02374   kdDebug(5006) << "contents type: " << contentsType << endl;
02375   KMFolder *folder = findResourceFolder( parent );
02376   KMFolderDir *parentFolderDir = !parent.isEmpty() && folder ? folder->createChildFolder(): mFolderParentDir;
02377   if ( !parentFolderDir || parentFolderDir->hasNamedFolder( resource ) ) return false;
02378 
02379   QString msg;
02380   if ( parentFolderDir->owner() && !parentFolderDir->owner()->isValidName( resource, msg ) ) {
02381     KMessageBox::error( 0, msg );
02382     return false;
02383   }
02384 
02385   KMFolderType type = mFolderType;
02386   if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
02387 
02388   KMFolder* newFolder = parentFolderDir->createFolder( resource, false, type );
02389   if ( !newFolder ) return false;
02390   if( mFolderType == KMFolderTypeImap )
02391     static_cast<KMFolderImap*>( folder->storage() )->createFolder( resource );
02392 
02393   StorageFormat defaultFormat = GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard;
02394   setStorageFormat( newFolder, folder ? storageFormat( folder ) : defaultFormat );
02395   newFolder->storage()->setContentsType( folderContentsType( contentsType ) );
02396   newFolder->storage()->writeConfig();
02397   newFolder->open( "ical_subresource" );
02398   connectFolder( newFolder );
02399   reloadFolderTree();
02400 
02401   return true;
02402 }
02403 
02404 bool KMailICalIfaceImpl::removeSubresource( const QString& location )
02405 {
02406   kdDebug(5006) << k_funcinfo << endl;
02407 
02408   KMFolder *folder = findResourceFolder( location );
02409 
02410   // We don't allow the default folders to be deleted, so check for
02411   // those first. It would be nicer to produce a more meaningful error,
02412   // or prevent deletion of the builtin folders from the gui already.
02413   if ( !folder || isStandardResourceFolder( folder ) )
02414       return false;
02415 
02416   // the folder will be removed, which implies closed, so make sure
02417   // nothing is using it anymore first
02418   subresourceDeleted( folderContentsType( folder->storage()->contentsType() ), location );
02419   mExtraFolders.remove( location );
02420   folder->disconnect( this );
02421 
02422   if ( folder->folderType() == KMFolderTypeImap )
02423     kmkernel->imapFolderMgr()->remove( folder );
02424   else if ( folder->folderType() == KMFolderTypeCachedImap ) {
02425     // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2)
02426     KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( folder->storage() );
02427     KMAcctCachedImap* acct = storage->account();
02428     if ( acct )
02429       acct->addDeletedFolder( folder );
02430     kmkernel->dimapFolderMgr()->remove( folder );
02431   }
02432   return true;
02433 }
02434 
02435 void KMailICalIfaceImpl::syncFolder(KMFolder * folder) const
02436 {
02437   if ( kmkernel->isOffline() || !GlobalSettings::immediatlySyncDIMAPOnGroupwareChanges() )
02438     return;
02439   KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
02440   if ( !dimapFolder )
02441     return;
02442   // check if the folder exists already, otherwise sync its parent as well to create it
02443   if ( dimapFolder->imapPath().isEmpty() ) {
02444     if ( folder->parent() && folder->parent()->owner() )
02445       syncFolder( folder->parent()->owner() );
02446     else
02447       return;
02448   }
02449   dimapFolder->account()->processNewMailInFolder( folder );
02450 }
02451 
02452 KMailICalIface::Answer KMailICalIfaceImpl::messageReadyForUpdate( const QString &resource,
02453                                                                   Q_UINT32 sernum )
02454 {
02455   if ( sernum == 0 ) {
02456     // Yes, will store a new one
02457     return Yes;
02458   }
02459 
02460   KMFolder *folder = findResourceFolder( resource );
02461   if( !folder ) {
02462     kdError(5006) << "update(" << resource << ") : Not an IMAP resource folder" << endl;
02463     return Error;
02464   }
02465 
02466   folder->open( "messageReadyForUpdate" );
02467 
02468   const KMMessage *msg = findMessageBySerNum( sernum, folder );
02469 
02470   if ( !msg ) {
02471     return Error;
02472   }
02473 
02474   KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
02475   Answer answer = Yes;
02476   if ( dimapFolder ) {
02477     if ( msg->transferInProgress() || !dimapFolder->isInInitialState() ) {
02478       answer = No;
02479     }
02480   }
02481 
02482   folder->close( "messageReadyForUpdate" );
02483   return answer;
02484 }
02485 
02486 #include "kmailicalifaceimpl.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys