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 
00056 #include <mimelib/enum.h>
00057 #include <mimelib/utility.h>
00058 #include <mimelib/body.h>
00059 #include <mimelib/mimepp.h>
00060 
00061 #include <qfile.h>
00062 #include <qmap.h>
00063 #include <qtextcodec.h>
00064 
00065 #include <kdebug.h>
00066 #include <kiconloader.h>
00067 #include <dcopclient.h>
00068 #include <kmessagebox.h>
00069 #include <kconfig.h>
00070 #include <kurl.h>
00071 #include <ktempfile.h>
00072 
00073 using namespace KMail;
00074 
00075 // Local helper methods
00076 static void vPartMicroParser( const QString& str, QString& s );
00077 static void reloadFolderTree();
00078 
00079 // The index in this array is the KMail::FolderContentsType enum
00080 static const struct {
00081   const char* contentsTypeStr; // the string used in the DCOP interface
00082   const char* mimetype;
00083   KFolderTreeItem::Type treeItemType;
00084   const char* annotation;
00085   const char* translatedName;
00086 } s_folderContentsType[] = {
00087   { "Mail", "application/x-vnd.kolab.mail", KFolderTreeItem::Other, "mail", I18N_NOOP( "Mail" ) },
00088   { "Calendar", "application/x-vnd.kolab.event", KFolderTreeItem::Calendar, "event", I18N_NOOP( "Calendar" ) },
00089   { "Contact", "application/x-vnd.kolab.contact", KFolderTreeItem::Contacts, "contact", I18N_NOOP( "Contacts" ) },
00090   { "Note", "application/x-vnd.kolab.note", KFolderTreeItem::Notes, "note", I18N_NOOP( "Notes" ) },
00091   { "Task", "application/x-vnd.kolab.task", KFolderTreeItem::Tasks, "task", I18N_NOOP( "Tasks" ) },
00092   { "Journal", "application/x-vnd.kolab.journal", KFolderTreeItem::Journals, "journal", I18N_NOOP( "Journal" ) }
00093 };
00094 
00095 static QString folderContentsType( KMail::FolderContentsType type )
00096 {
00097   return s_folderContentsType[type].contentsTypeStr;
00098 }
00099 
00100 static QString folderKolabMimeType( KMail::FolderContentsType type )
00101 {
00102   return s_folderContentsType[type].mimetype;
00103 }
00104 
00105 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::globalStorageFormat() const {
00106   return GlobalSettings::self()->theIMAPResourceStorageFormat()
00107     == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard;
00108 }
00109 
00110 static KMail::FolderContentsType folderContentsType( const QString& type )
00111 {
00112   for ( uint i = 0 ; i < sizeof s_folderContentsType / sizeof *s_folderContentsType; ++i )
00113     if ( type == s_folderContentsType[i].contentsTypeStr )
00114       return static_cast<KMail::FolderContentsType>( i );
00115   return KMail::ContentsTypeMail;
00116 }
00117 
00118 static QString localizedDefaultFolderName( KMail::FolderContentsType type )
00119 {
00120   return i18n( s_folderContentsType[type].translatedName );
00121 }
00122 
00123 const char* KMailICalIfaceImpl::annotationForContentsType( KMail::FolderContentsType type )
00124 {
00125   return s_folderContentsType[type].annotation;
00126 }
00127 
00128 /*
00129   This interface has three parts to it - libkcal interface;
00130   kmail interface; and helper functions.
00131 
00132   The libkcal interface and the kmail interface have the same three
00133   methods: add, delete and refresh. The only difference is that the
00134   libkcal interface is used from the IMAP resource in libkcal and
00135   the kmail interface is used from the groupware object in kmail.
00136 */
00137 
00138 KMailICalIfaceImpl::KMailICalIfaceImpl()
00139   : DCOPObject( "KMailICalIface" ), QObject( 0, "KMailICalIfaceImpl" ),
00140     mContacts( 0 ), mCalendar( 0 ), mNotes( 0 ), mTasks( 0 ), mJournals( 0 ),
00141     mFolderLanguage( 0 ), mFolderParentDir( 0 ), mFolderType( KMFolderTypeUnknown ),
00142     mUseResourceIMAP( false ), mResourceQuiet( false ), mHideFolders( true )
00143 {
00144   // Listen to config changes
00145   connect( kmkernel, SIGNAL( configChanged() ), this, SLOT( readConfig() ) );
00146   connect( kmkernel, SIGNAL( folderRemoved( KMFolder* ) ),
00147            this, SLOT( slotFolderRemoved( KMFolder* ) ) );
00148 
00149   mExtraFolders.setAutoDelete( true );
00150   mAccumulators.setAutoDelete( true );
00151 }
00152 
00153 
00154 /* libkcal part of the interface, called from the resources using this
00155  * when incidences are added or deleted */
00156 
00157 // Helper function to find an attachment of a given mimetype
00158 // Can't use KMMessage::findDwBodyPart since it only works with known mimetypes.
00159 static DwBodyPart* findBodyPartByMimeType( const KMMessage& msg, const char* sType, const char* sSubtype, bool startsWith = false )
00160 {
00161   // quickly searching for our message part: since Kolab parts are
00162   // top-level parts we do *not* have to travel into embedded multiparts
00163   DwBodyPart* part = msg.getFirstDwBodyPart();
00164   while( part ){
00165   //    kdDebug() << part->Headers().ContentType().TypeStr().c_str() << " "
00166   //            << part->Headers().ContentType().SubtypeStr().c_str() << endl;
00167     if ( part->hasHeaders() ) {
00168       DwMediaType& contentType = part->Headers().ContentType();
00169       if ( startsWith ) {
00170         if ( contentType.TypeStr() == sType
00171              && QString( contentType.SubtypeStr().c_str() ).startsWith( sSubtype ) )
00172           return part;
00173       }
00174       else
00175         if ( contentType.TypeStr() == sType
00176              && contentType.SubtypeStr() == sSubtype )
00177           return part;
00178     }
00179     part = part->Next();
00180   }
00181   return 0;
00182 }
00183 
00184 // Helper function to find an attachment with a given filename
00185 static DwBodyPart* findBodyPart( const KMMessage& msg, const QString& attachmentName )
00186 {
00187   // quickly searching for our message part: since Kolab parts are
00188   // top-level parts we do *not* have to travel into embedded multiparts
00189   for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) {
00190     //kdDebug(5006) << "findBodyPart:  - " << part->Headers().ContentDisposition().Filename().c_str() << endl;
00191     if ( part->hasHeaders()
00192          && attachmentName == part->Headers().ContentDisposition().Filename().c_str() )
00193       return part;
00194   }
00195   return 0;
00196 }
00197 
00198 #if 0
00199 static void debugBodyParts( const char* foo, const KMMessage& msg )
00200 {
00201   kdDebug(5006) << "--debugBodyParts " << foo << "--" << endl;
00202   for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) {
00203     if ( part->hasHeaders() ) {
00204       kdDebug(5006) << " bodypart: " << part << endl;
00205       kdDebug(5006) << "        " << part->Headers().AsString().c_str() << endl;
00206     }
00207     else
00208       kdDebug(5006) << " part " << part << " has no headers" << endl;
00209   }
00210 }
00211 #else
00212 inline static void debugBodyParts( const char*, const KMMessage& ) {}
00213 #endif
00214 
00215 
00216 // Add (or overwrite, resp.) an attachment in an existing mail,
00217 // attachments must be local files, they are identified by their names.
00218 // If lookupByName if false the attachment to replace is looked up by mimetype.
00219 // return value: wrong if attachment could not be added/updated
00220 bool KMailICalIfaceImpl::updateAttachment( KMMessage& msg,
00221                                            const QString& attachmentURL,
00222                                            const QString& attachmentName,
00223                                            const QString& attachmentMimetype,
00224                                            bool lookupByName )
00225 {
00226   kdDebug(5006) << "KMailICalIfaceImpl::updateAttachment( " << attachmentURL << " )" << endl;
00227 
00228   bool bOK = false;
00229 
00230   KURL url( attachmentURL );
00231   if ( url.isValid() && url.isLocalFile() ) {
00232     const QString fileName( url.path() );
00233     QFile file( fileName );
00234     if( file.open( IO_ReadOnly ) ) {
00235       QByteArray rawData = file.readAll();
00236       file.close();
00237 
00238       // create the new message part with data read from temp file
00239       KMMessagePart msgPart;
00240       msgPart.setName( attachmentName );
00241 
00242       const int iSlash = attachmentMimetype.find('/');
00243       const QCString sType    = attachmentMimetype.left( iSlash   ).latin1();
00244       const QCString sSubtype = attachmentMimetype.mid(  iSlash+1 ).latin1();
00245       msgPart.setTypeStr( sType );
00246       msgPart.setSubtypeStr( sSubtype );
00247       QCString ctd("attachment;\n  filename=\"");
00248       ctd.append( attachmentName.latin1() );
00249       ctd.append("\"");
00250       msgPart.setContentDisposition( ctd );
00251       QValueList<int> dummy;
00252       msgPart.setBodyAndGuessCte( rawData, dummy );
00253       msgPart.setPartSpecifier( fileName );
00254 
00255       DwBodyPart* newPart = msg.createDWBodyPart( &msgPart );
00256       // This whole method is a bit special. We mix code for writing and code for reading.
00257       // E.g. we need to parse the content-disposition again for ContentDisposition().Filename()
00258       // to work later on.
00259       newPart->Headers().ContentDisposition().Parse();
00260 
00261       DwBodyPart* part = lookupByName ? findBodyPart( msg, attachmentName )
00262                          : findBodyPartByMimeType( msg, sType, sSubtype );
00263       if ( part ) {
00264         // Make sure the replacing body part is pointing
00265         // to the same next part as the original body part.
00266         newPart->SetNext( part->Next() );
00267         // call DwBodyPart::operator =
00268         // which calls DwEntity::operator =
00269         *part = *newPart;
00270         delete newPart;
00271         msg.setNeedsAssembly();
00272         kdDebug(5006) << "Attachment " << attachmentName << " updated." << endl;
00273       } else {
00274         msg.addDwBodyPart( newPart );
00275         kdDebug(5006) << "Attachment " << attachmentName << " added." << endl;
00276       }
00277       bOK = true;
00278     }else{
00279       kdDebug(5006) << "Attachment " << attachmentURL << " can not be read." << endl;
00280     }
00281   }else{
00282     kdDebug(5006) << "Attachment " << attachmentURL << " not a local file." << endl;
00283   }
00284 
00285   return bOK;
00286 }
00287 
00288 // Look for the attachment with the right mimetype
00289 bool KMailICalIfaceImpl::kolabXMLFoundAndDecoded( const KMMessage& msg, const QString& mimetype, QString& s )
00290 {
00291   const int iSlash = mimetype.find('/');
00292   const QCString sType    = mimetype.left( iSlash   ).latin1();
00293   const QCString sSubtype = mimetype.mid(  iSlash+1 ).latin1();
00294   DwBodyPart* part = findBodyPartByMimeType( msg, sType, sSubtype, true /* starts with sSubtype, to accept application/x-vnd.kolab.contact.distlist */ );
00295   if ( part ) {
00296     KMMessagePart msgPart;
00297     KMMessage::bodyPart(part, &msgPart);
00298     s = msgPart.bodyToUnicode( QTextCodec::codecForName( "utf8" ) );
00299     return true;
00300   }
00301   return false;
00302 }
00303 
00304 // Delete an attachment in an existing mail.
00305 // return value: wrong if attachment could not be deleted
00306 //
00307 // This code could be optimized: for now we just replace
00308 // the attachment by an empty dummy attachment since Mimelib
00309 // does not provide an option for deleting attachments yet.
00310 bool KMailICalIfaceImpl::deleteAttachment( KMMessage& msg,
00311                                            const QString& attachmentName )
00312 {
00313   kdDebug(5006) << "KMailICalIfaceImpl::deleteAttachment( " << attachmentName << " )" << endl;
00314 
00315   bool bOK = false;
00316 
00317   // quickly searching for our message part: since Kolab parts are
00318   // top-level parts we do *not* have to travel into embedded multiparts
00319   DwBodyPart* part = findBodyPart( msg, attachmentName );
00320   if ( part ) {
00321     msg.getTopLevelPart()->Body().RemoveBodyPart( part );
00322     delete part;
00323     msg.setNeedsAssembly();
00324     kdDebug(5006) << "Attachment deleted." << endl;
00325     bOK = true;
00326   }
00327 
00328   if( !bOK ){
00329     kdDebug(5006) << "Attachment " << attachmentName << " not found." << endl;
00330   }
00331 
00332   return bOK;
00333 }
00334 
00335 static void setIcalVcardContentTypeHeader( KMMessage *msg, KMail::FolderContentsType t )
00336 {
00337   msg->setType( DwMime::kTypeText );
00338   if ( t == KMail::ContentsTypeCalendar || t == KMail::ContentsTypeTask
00339       || t == KMail::ContentsTypeJournal ) {
00340     msg->setSubtype( DwMime::kSubtypeVCal );
00341     msg->setHeaderField("Content-Type",
00342         "text/calendar; method=REQUEST; charset=\"utf-8\"");
00343   } else if ( t == KMail::ContentsTypeContact ) {
00344     msg->setSubtype( DwMime::kSubtypeXVCard );
00345     msg->setHeaderField( "Content-Type", "Text/X-VCard; charset=\"utf-8\"" );
00346   } else {
00347     kdWarning(5006) << k_funcinfo << "Attempt to write non-groupware contents to folder" << endl;
00348   }
00349 }
00350 
00351 static void setXMLContentTypeHeader( KMMessage *msg, const QString plainTextBody )
00352 {
00353    // add a first body part to be displayed by all mailer
00354     // than can NOT display Kolab data: no matter if these
00355     // mailers are MIME compliant or not
00356     KMMessagePart firstPart;
00357     firstPart.setType( DwMime::kTypeText );
00358     firstPart.setSubtype( DwMime::kSubtypePlain );
00359     msg->removeHeaderField( "Content-Type" );
00360     msg->setType( DwMime::kTypeMultipart );
00361     msg->setSubtype( DwMime::kSubtypeMixed );
00362     msg->headers().ContentType().CreateBoundary( 0 );
00363     msg->headers().ContentType().Assemble();
00364     firstPart.setBodyFromUnicode( plainTextBody );
00365     msg->addBodyPart( &firstPart );
00366 }
00367 
00368 // Store a new entry that was received from the resource
00369 Q_UINT32 KMailICalIfaceImpl::addIncidenceKolab( KMFolder& folder,
00370                                                 const QString& subject,
00371                                                 const QString& plainTextBody,
00372                                                 const QMap<QCString, QString>& customHeaders,
00373                                                 const QStringList& attachmentURLs,
00374                                                 const QStringList& attachmentNames,
00375                                                 const QStringList& attachmentMimetypes )
00376 {
00377   kdDebug(5006) << "KMailICalIfaceImpl::addIncidenceKolab( " << attachmentNames << " )" << endl;
00378 
00379   Q_UINT32 sernum = 0;
00380   bool bAttachOK = true;
00381 
00382   // Make a new message for the incidence
00383   KMMessage* msg = new KMMessage();
00384   msg->initHeader();
00385   msg->setSubject( subject );
00386   msg->setAutomaticFields( true );
00387 
00388   QMap<QCString, QString>::ConstIterator ith = customHeaders.begin();
00389   const QMap<QCString, QString>::ConstIterator ithEnd = customHeaders.end();
00390   for ( ; ith != ithEnd ; ++ith ) {
00391     msg->setHeaderField( ith.key(), ith.data() );
00392   }
00393   // In case of the ical format, simply add the plain text content with the
00394   // right content type
00395   if ( storageFormat( &folder ) == StorageXML ) {
00396     setXMLContentTypeHeader( msg, plainTextBody );
00397   } else if ( storageFormat( &folder ) == StorageIcalVcard ) {
00398     const KMail::FolderContentsType t = folder.storage()->contentsType();
00399     setIcalVcardContentTypeHeader( msg, t );
00400     msg->setBodyEncoded( plainTextBody.utf8() );
00401   } else {
00402     kdWarning(5006) << k_funcinfo << "Attempt to write to folder with unknown storage type" << endl;
00403   }
00404 
00405   Q_ASSERT( attachmentMimetypes.count() == attachmentURLs.count() );
00406   Q_ASSERT( attachmentNames.count() == attachmentURLs.count() );
00407   // Add all attachments by reading them from their temp. files
00408   QStringList::ConstIterator itmime = attachmentMimetypes.begin();
00409   QStringList::ConstIterator iturl = attachmentURLs.begin();
00410   for( QStringList::ConstIterator itname = attachmentNames.begin();
00411        itname != attachmentNames.end()
00412        && itmime != attachmentMimetypes.end()
00413        && iturl != attachmentURLs.end();
00414        ++itname, ++iturl, ++itmime ){
00415     bool bymimetype = (*itmime).startsWith( "application/x-vnd.kolab." );
00416     if( !updateAttachment( *msg, *iturl, *itname, *itmime, !bymimetype ) ){
00417       kdWarning(5006) << "Attachment error, can not add Incidence." << endl;
00418       bAttachOK = false;
00419       break;
00420     }
00421   }
00422 
00423   if( bAttachOK ){
00424     // Mark the message as read and store it in the folder
00425     msg->cleanupHeader();
00426     //debugBodyParts( "after cleanup", *msg );
00427     msg->touch();
00428     if ( folder.addMsg( msg ) == 0 )
00429       // Message stored
00430       sernum = msg->getMsgSerNum();
00431     kdDebug(5006) << "addIncidenceKolab(): Message done and saved. Sernum: "
00432                   << sernum << endl;
00433 
00434     //debugBodyParts( "after addMsg", *msg );
00435     addFolderChange( &folder, Contents );
00436   } else
00437     kdError(5006) << "addIncidenceKolab(): Message *NOT* saved!\n";
00438 
00439   return sernum;
00440 }
00441 
00442 bool KMailICalIfaceImpl::deleteIncidenceKolab( const QString& resource,
00443                                                Q_UINT32 sernum )
00444 {
00445   // Find the message from the serial number and delete it.
00446   if( !mUseResourceIMAP )
00447     return false;
00448 
00449   kdDebug(5006) << "KMailICalIfaceImpl::deleteIncidenceKolab( "
00450                 << resource << ", " << sernum << ")\n";
00451 
00452   // Find the folder
00453   KMFolder* f = findResourceFolder( resource );
00454   if( !f ) {
00455     kdError(5006) << "deleteIncidenceKolab(" << resource << ") : Not an IMAP resource folder" << endl;
00456     return false;
00457   }
00458 
00459   bool rc = false;
00460 
00461   KMMessage* msg = findMessageBySerNum( sernum, f );
00462   if( msg ) {
00463     // Message found - delete it and return happy
00464     deleteMsg( msg );
00465     rc = true;
00466   } else {
00467     kdDebug(5006) << "Message not found, cannot remove serNum " << sernum << endl;
00468   }
00469   return rc;
00470 }
00471 
00472 
00473 int KMailICalIfaceImpl::incidencesKolabCount( const QString& mimetype,
00474                                               const QString& resource )
00475 {
00476   if( !mUseResourceIMAP )
00477     return 0;
00478 
00479   KMFolder* f = findResourceFolder( resource );
00480   if( !f ) {
00481     kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl;
00482     return 0;
00483   }
00484 
00485   f->open("kolabcount");
00486   int n = f->count();
00487   f->close("kolabcount");
00488   kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolabCount( " << mimetype << ", "
00489                 << resource << " ) returned " << n << endl;
00490   return n;
00491 }
00492 
00493 QMap<Q_UINT32, QString> KMailICalIfaceImpl::incidencesKolab( const QString& mimetype,
00494                                                              const QString& resource,
00495                                                              int startIndex,
00496                                                              int nbMessages )
00497 {
00501 
00502   QMap<Q_UINT32, QString> aMap;
00503   if( !mUseResourceIMAP )
00504     return aMap;
00505 
00506   KMFolder* f = findResourceFolder( resource );
00507   if( !f ) {
00508     kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl;
00509     return aMap;
00510   }
00511 
00512   f->open("incidences");
00513 
00514   int stopIndex = nbMessages == -1 ? f->count() :
00515                   QMIN( f->count(), startIndex + nbMessages );
00516   kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolab( " << mimetype << ", "
00517                 << resource << " ) from " << startIndex << " to " << stopIndex << endl;
00518 
00519   for(int i = startIndex; i < stopIndex; ++i) {
00520 #if 0
00521     bool unget = !f->isMessage(i);
00522     KMMessage* msg = f->getMsg( i );
00523 #else // faster
00524     KMMessage* msg = f->storage()->readTemporaryMsg(i);
00525 #endif
00526     if ( msg ) {
00527       const int iSlash = mimetype.find('/');
00528       const QCString sType    = mimetype.left( iSlash   ).latin1();
00529       const QCString sSubtype = mimetype.mid(  iSlash+1 ).latin1();
00530       if ( sType.isEmpty() || sSubtype.isEmpty() ) {
00531         kdError(5006) << mimetype << " not an type/subtype combination" << endl;
00532       } else {
00533         DwBodyPart* dwPart = findBodyPartByMimeType( *msg, sType, sSubtype );
00534         if ( dwPart ) {
00535           KMMessagePart msgPart;
00536           KMMessage::bodyPart(dwPart, &msgPart);
00537           aMap.insert(msg->getMsgSerNum(), msgPart.bodyToUnicode( QTextCodec::codecForName( "utf8" ) ));
00538         } else {
00539           // Check if the whole message has the right types. This is what
00540           // happens in the case of ical storage, where the whole mail is
00541           // the data
00542           const QCString type( msg->typeStr() );
00543           const QCString subtype( msg->subtypeStr() );
00544           if (type.lower() == sType && subtype.lower() == sSubtype ) {
00545             aMap.insert( msg->getMsgSerNum(), msg->bodyToUnicode() );
00546           }
00547           // This is *not* an error: it may be that not all of the messages
00548           // have a message part that is matching the wanted MIME type
00549         }
00550       }
00551 #if 0
00552       if( unget ) f->unGetMsg(i);
00553 #else
00554       delete msg;
00555 #endif
00556     }
00557   }
00558   f->close( "incidences" );
00559   return aMap;
00560 }
00561 
00562 
00563 /* Called when a message that was downloaded from an online imap folder
00564  * arrives. Needed when listing incidences on online account folders. */
00565 // TODO: Till, port me
00566 void KMailICalIfaceImpl::slotMessageRetrieved( KMMessage* msg )
00567 {
00568   if( !msg ) return;
00569 
00570   KMFolder *parent = msg->parent();
00571   Q_ASSERT( parent );
00572   Q_UINT32 sernum = msg->getMsgSerNum();
00573 
00574   // do we have an accumulator for this folder?
00575   Accumulator *ac = mAccumulators.find( parent->location() );
00576   if( ac ) {
00577     QString s;
00578     if ( !vPartFoundAndDecoded( msg, s ) ) return;
00579     QString uid( "UID" );
00580     vPartMicroParser( s, uid );
00581     const Q_UINT32 sernum = msg->getMsgSerNum();
00582     mUIDToSerNum.insert( uid, sernum );
00583     ac->add( s );
00584     if( ac->isFull() ) {
00585       /* if this was the last one we were waiting for, tell the resource
00586        * about the new incidences and clean up. */
00587       //asyncLoadResult( ac->incidences, ac->type, ac->folder );
00588       mAccumulators.remove( ac->folder ); // autodelete
00589     }
00590   } else {
00591     /* We are not accumulating for this folder, so this one was added
00592      * by KMail. Do your thang. */
00593      slotIncidenceAdded( msg->parent(), msg->getMsgSerNum() );
00594   }
00595 
00596   if ( mTheUnGetMes.contains( sernum ) ) {
00597     mTheUnGetMes.remove( sernum );
00598     int i = 0;
00599     KMFolder* folder = 0;
00600     KMMsgDict::instance()->getLocation( sernum, &folder, &i );
00601     folder->unGetMsg( i );
00602   }
00603 }
00604 
00605 /* list all available subresources */
00606 QValueList<KMailICalIfaceImpl::SubResource> KMailICalIfaceImpl::subresourcesKolab( const QString& contentsType )
00607 {
00608   QValueList<SubResource> subResources;
00609 
00610   // Add the default one
00611   KMFolder* f = folderFromType( contentsType, QString::null );
00612   if ( f ) {
00613     subResources.append( SubResource( f->location(),  f->prettyURL(), !f->isReadOnly() ) );
00614     kdDebug(5006) << "Adding(1) folder " << f->location() << "    " <<
00615       ( f->isReadOnly() ? "readonly" : "" ) << endl;
00616   }
00617 
00618   // get the extra ones
00619   const KMail::FolderContentsType t = folderContentsType( contentsType );
00620   QDictIterator<ExtraFolder> it( mExtraFolders );
00621   for ( ; it.current(); ++it ){
00622     f = it.current()->folder;
00623     if ( f && f->storage()->contentsType() == t ) {
00624       subResources.append( SubResource( f->location(), f->prettyURL(), !f->isReadOnly() ) );
00625       kdDebug(5006) << "Adding(2) folder " << f->location() << "     " <<
00626               ( f->isReadOnly() ? "readonly" : "" ) << endl;
00627     }
00628   }
00629 
00630   if ( subResources.isEmpty() )
00631     kdDebug(5006) << "subresourcesKolab: No folder found for " << contentsType << endl;
00632   return subResources;
00633 }
00634 
00635 bool KMailICalIfaceImpl::triggerSync( const QString& contentsType )
00636 {
00637   kdDebug(5006) << k_funcinfo << endl;
00638   QValueList<KMailICalIfaceImpl::SubResource> folderList = subresourcesKolab( contentsType );
00639   for ( QValueList<KMailICalIfaceImpl::SubResource>::const_iterator it( folderList.begin() ),
00640                                                                     end( folderList.end() );
00641         it != end ; ++it ) {
00642     KMFolder * const f = findResourceFolder( (*it).location );
00643     if ( !f ) continue;
00644     if ( f->folderType() == KMFolderTypeImap || f->folderType() == KMFolderTypeCachedImap ) {
00645       if ( !kmkernel->askToGoOnline() ) {
00646         return false;
00647       }
00648     }
00649 
00650     if ( f->folderType() == KMFolderTypeImap ) {
00651       KMFolderImap *imap = static_cast<KMFolderImap*>( f->storage() );
00652       imap->getAndCheckFolder();
00653     } else if ( f->folderType() == KMFolderTypeCachedImap ) {
00654       KMFolderCachedImap* cached = static_cast<KMFolderCachedImap*>( f->storage() );
00655       cached->account()->processNewMailSingleFolder( f );
00656     }
00657   }
00658   return true;
00659 }
00660 
00661 /* Used by the resource to query whether folders are writable. */
00662 bool KMailICalIfaceImpl::isWritableFolder( const QString& type,
00663                                            const QString& resource )
00664 {
00665   KMFolder* f = folderFromType( type, resource );
00666   if ( !f )
00667     // Definitely not writable
00668     return false;
00669 
00670   return !f->isReadOnly();
00671 }
00672 
00673 /* Used by the resource to query the storage format of the folder. */
00674 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( const QString& resource )
00675 {
00676   StorageFormat format;
00677   KMFolder* f = findResourceFolder( resource );
00678   if ( f )
00679     format = storageFormat( f );
00680   else
00681     format = globalStorageFormat();
00682   return format;
00683 }
00684 
00699 Q_UINT32 KMailICalIfaceImpl::update( const QString& resource,
00700                                      Q_UINT32 sernum,
00701                                      const QString& subject,
00702                                      const QString& plainTextBody,
00703                                      const QMap<QCString, QString>& customHeaders,
00704                                      const QStringList& attachmentURLs,
00705                                      const QStringList& attachmentMimetypes,
00706                                      const QStringList& attachmentNames,
00707                                      const QStringList& deletedAttachments )
00708 {
00709   Q_UINT32 rc = 0;
00710 
00711    if( !mUseResourceIMAP )
00712     return rc;
00713 
00714   Q_ASSERT( !resource.isEmpty() );
00715 
00716   kdDebug(5006) << "KMailICalIfaceImpl::update( " << resource << ", " << sernum << " )\n";
00717   kdDebug(5006) << attachmentURLs << "\n";
00718   kdDebug(5006) << attachmentMimetypes << "\n";
00719   kdDebug(5006) << attachmentNames << "\n";
00720   kdDebug(5006) << "deleted attachments:" << deletedAttachments << "\n";
00721 
00722   // Find the folder
00723   KMFolder* f = findResourceFolder( resource );
00724   if( !f ) {
00725     kdError(5006) << "update(" << resource << ") : Not an IMAP resource folder" << endl;
00726     return rc;
00727   }
00728 
00729   f->open("ifaceupdate");
00730 
00731   KMMessage* msg = 0;
00732   if ( sernum != 0 ) {
00733     msg = findMessageBySerNum( sernum, f );
00734     if ( !msg ) return 0;
00735     // Message found - make a copy and update it:
00736     KMMessage* newMsg = new KMMessage( *msg );
00737     newMsg->setSubject( subject );
00738     QMap<QCString, QString>::ConstIterator ith = customHeaders.begin();
00739     const QMap<QCString, QString>::ConstIterator ithEnd = customHeaders.begin();
00740     for ( ; ith != ithEnd ; ++ith )
00741       newMsg->setHeaderField( ith.key(), ith.data() );
00742     newMsg->setParent( 0 ); // workaround strange line in KMMsgBase::assign. newMsg is not in any folder yet.
00743     // Note that plainTextBody isn't used in this branch. We assume it's still valid from when the mail was created.
00744 
00745     // Delete some attachments according to list
00746     for( QStringList::ConstIterator it = deletedAttachments.begin();
00747          it != deletedAttachments.end();
00748          ++it ){
00749       if( !deleteAttachment( *newMsg, *it ) ){
00750         // Note: It is _not_ an error if an attachment was already deleted.
00751       }
00752     }
00753 
00754     const KMail::FolderContentsType t = f->storage()->contentsType();
00755     const QCString type = msg->typeStr();
00756     const QCString subtype = msg->subtypeStr();
00757     const bool messageWasIcalVcardFormat = ( type.lower() == "text" &&
00758         ( subtype.lower() == "calendar" || subtype.lower() == "x-vcard" ) );
00759 
00760     if ( storageFormat( f ) == StorageIcalVcard ) {
00761       //kdDebug(5006) << k_funcinfo << " StorageFormatIcalVcard " << endl;
00762       if ( !messageWasIcalVcardFormat ) {
00763         setIcalVcardContentTypeHeader( newMsg, t );
00764       }
00765       newMsg->setBodyEncoded( plainTextBody.utf8() );
00766     } else if ( storageFormat( f ) == StorageXML ) {
00767       if ( messageWasIcalVcardFormat ) {
00768         // this was originally an ical event, but the folder changed to xml,
00769         // convert
00770        setXMLContentTypeHeader( newMsg, plainTextBody );
00771       }
00772       //kdDebug(5006) << k_funcinfo << " StorageFormatXML " << endl;
00773       // Add all attachments by reading them from their temp. files
00774       QStringList::ConstIterator iturl = attachmentURLs.begin();
00775       QStringList::ConstIterator itmime = attachmentMimetypes.begin();
00776       QStringList::ConstIterator itname = attachmentNames.begin();
00777       for( ;
00778           iturl != attachmentURLs.end()
00779           && itmime != attachmentMimetypes.end()
00780           && itname != attachmentNames.end();
00781           ++iturl, ++itname, ++itmime ){
00782         bool bymimetype = (*itname).startsWith( "application/x-vnd.kolab." );
00783         if( !updateAttachment( *newMsg, *iturl, *itname, *itmime, bymimetype ) ){
00784           kdDebug(5006) << "Attachment error, can not update attachment " << *iturl << endl;
00785           break;
00786         }
00787       }
00788     }
00789 
00790     //debugBodyParts( "in update, before cleanup", *newMsg );
00791 
00792     // This is necessary for the headers to be readable later on
00793     newMsg->cleanupHeader();
00794 
00795     //debugBodyParts( "in update, after cleanup", *newMsg );
00796 
00797     deleteMsg( msg );
00798     if ( f->addMsg( newMsg ) == 0 ) {
00799       // Message stored
00800       rc = newMsg->getMsgSerNum();
00801       kdDebug(5006) << "forget about " << sernum << ", it's " << rc << " now" << endl;
00802     }
00803     addFolderChange( f, Contents );
00804   } else {
00805     // Message not found - store it newly
00806     rc = addIncidenceKolab( *f, subject, plainTextBody, customHeaders,
00807                             attachmentURLs,
00808                             attachmentNames,
00809                             attachmentMimetypes );
00810   }
00811 
00812   f->close("ifaceupdate");
00813   return rc;
00814 }
00815 
00816 KURL KMailICalIfaceImpl::getAttachment( const QString& resource,
00817                                         Q_UINT32 sernum,
00818                                         const QString& filename )
00819 {
00820   // This finds the attachment with the filename, saves it to a
00821   // temp file and returns a URL to it. It's up to the resource
00822   // to delete the tmp file later.
00823   if( !mUseResourceIMAP )
00824     return KURL();
00825 
00826   kdDebug(5006) << "KMailICalIfaceImpl::getAttachment( "
00827                 << resource << ", " << sernum << ", " << filename << " )\n";
00828 
00829   // Find the folder
00830   KMFolder* f = findResourceFolder( resource );
00831   if( !f ) {
00832     kdError(5006) << "getAttachment(" << resource << ") : Not an IMAP resource folder" << endl;
00833     return KURL();
00834   }
00835   if ( storageFormat( f ) != StorageXML ) {
00836     kdError(5006) << "getAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
00837     return KURL();
00838   }
00839 
00840   KURL url;
00841 
00842   bool bOK = false;
00843   bool quiet = mResourceQuiet;
00844   mResourceQuiet = true;
00845 
00846   KMMessage* msg = findMessageBySerNum( sernum, f );
00847   if( msg ) {
00848     // Message found - look for the attachment:
00849 
00850     DwBodyPart* part = findBodyPart( *msg, filename );
00851     if ( part ) {
00852       // Save the contents of the attachment.
00853       KMMessagePart aPart;
00854       msg->bodyPart( part, &aPart );
00855       QByteArray rawData( aPart.bodyDecodedBinary() );
00856 
00857       KTempFile file;
00858       file.file()->writeBlock( rawData.data(), rawData.size() );
00859 
00860       url.setPath( file.name() );
00861 
00862       bOK = true;
00863     }
00864 
00865     if( !bOK ){
00866       kdDebug(5006) << "Attachment " << filename << " not found." << endl;
00867     }
00868   }else{
00869     kdDebug(5006) << "Message not found." << endl;
00870   }
00871 
00872   mResourceQuiet = quiet;
00873   return url;
00874 }
00875 
00876 // ============================================================================
00877 
00878 /* KMail part of the interface. These slots are connected to the resource
00879  * folders and inform us of folders or incidences in them changing, being
00880  * added or going away. */
00881 
00882 void KMailICalIfaceImpl::slotFolderRemoved( KMFolder* folder )
00883 {
00884   // pretend the folder just changed back to the mail type, which
00885   // does the right thing, namely remove resource
00886   folderContentsTypeChanged( folder, KMail::ContentsTypeMail );
00887   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
00888   configGroup.deleteEntry( folder->idString() + "-storageFormat" );
00889   configGroup.deleteEntry( folder->idString() + "-changes" );
00890 }
00891 
00892 // KMail added a file to one of the groupware folders
00893 void KMailICalIfaceImpl::slotIncidenceAdded( KMFolder* folder,
00894                                              Q_UINT32 sernum )
00895 {
00896   if( mResourceQuiet || !mUseResourceIMAP )
00897     return;
00898 
00899 //  kdDebug(5006) << "KMailICalIfaceImpl::slotIncidenceAdded" << endl;
00900   QString type = folderContentsType( folder->storage()->contentsType() );
00901   if( type.isEmpty() ) {
00902     kdError(5006) << "Not an IMAP resource folder" << endl;
00903     return;
00904   }
00905   // Get the index of the mail
00906   int i = 0;
00907   KMFolder* aFolder = 0;
00908   KMMsgDict::instance()->getLocation( sernum, &aFolder, &i );
00909   assert( folder == aFolder );
00910 
00911   bool unget = !folder->isMessage( i );
00912   QString s;
00913   QString uid( "UID" );
00914   KMMessage *msg = folder->getMsg( i );
00915   if( !msg ) return;
00916   if( msg->isComplete() ) {
00917 
00918     bool ok = false;
00919     StorageFormat format = storageFormat( folder );
00920     switch( format ) {
00921       case StorageIcalVcard:
00922         // Read the iCal or vCard
00923         ok = vPartFoundAndDecoded( msg, s );
00924         if ( ok )
00925           vPartMicroParser( s, uid );
00926         break;
00927       case StorageXML:
00928         // Read the XML from the attachment with the given mimetype
00929         if ( kolabXMLFoundAndDecoded( *msg,
00930               folderKolabMimeType( folder->storage()->contentsType() ), s ) ) {
00931           uid = msg->subject();
00932           ok = true;
00933         }
00934         break;
00935     }
00936     if ( !ok ) {
00937       if ( unget )
00938         folder->unGetMsg( i );
00939       return;
00940     }
00941     const Q_UINT32 sernum = msg->getMsgSerNum();
00942     mUIDToSerNum.insert( uid, sernum );
00943 
00944     // tell the resource if we didn't trigger this ourselves
00945     if ( mInTransit.contains( uid ) ) {
00946       mInTransit.remove( uid );
00947     }
00948     incidenceAdded( type, folder->location(), sernum, format, s );
00949   } else {
00950     // go get the rest of it, then try again
00951     // TODO: Till, port me
00952     if ( unget ) mTheUnGetMes.insert( msg->getMsgSerNum(), true );
00953     FolderJob *job = msg->parent()->createJob( msg );
00954     connect( job, SIGNAL( messageRetrieved( KMMessage* ) ),
00955         this, SLOT( slotMessageRetrieved( KMMessage* ) ) );
00956     job->start();
00957     return;
00958   }
00959   if( unget ) folder->unGetMsg(i);
00960 }
00961 
00962 // KMail deleted a file
00963 void KMailICalIfaceImpl::slotIncidenceDeleted( KMFolder* folder,
00964                                                Q_UINT32 sernum )
00965 {
00966   if( mResourceQuiet || !mUseResourceIMAP )
00967     return;
00968 
00969   QString type = folderContentsType( folder->storage()->contentsType() );
00970   //kdDebug(5006) << folder << " " << type << " " << sernum << endl;
00971   if( !type.isEmpty() ) {
00972     // Get the index of the mail
00973     int i = 0;
00974     KMFolder* aFolder = 0;
00975     KMMsgDict::instance()->getLocation( sernum, &aFolder, &i );
00976     assert( folder == aFolder );
00977 
00978     // Read the iCal or vCard
00979     bool unget = !folder->isMessage( i );
00980     QString s;
00981     bool ok = false;
00982     KMMessage* msg = folder->getMsg( i );
00983     QString uid( "UID" );
00984     switch( storageFormat( folder ) ) {
00985     case StorageIcalVcard:
00986         if( vPartFoundAndDecoded( msg, s ) ) {
00987             vPartMicroParser( s, uid );
00988             ok = true;
00989         }
00990         break;
00991     case StorageXML:
00992         if ( kolabXMLFoundAndDecoded( *msg, folderKolabMimeType( folder->storage()->contentsType() ), s ) ) {
00993           uid = msg->subject();
00994           ok = true;
00995         }
00996         break;
00997     }
00998     if ( ok ) {
00999         kdDebug(5006) << "Emitting DCOP signal incidenceDeleted( "
01000                       << type << ", " << folder->location() << ", " << uid
01001                       << " )" << endl;
01002         incidenceDeleted( type, folder->location(), uid );
01003     }
01004     if( unget ) folder->unGetMsg(i);
01005   } else
01006     kdError(5006) << "Not a groupware folder" << endl;
01007 }
01008 
01009 // KMail orders a refresh
01010 void KMailICalIfaceImpl::slotRefresh( const QString& type )
01011 {
01012   if( mUseResourceIMAP ) {
01013     signalRefresh( type, QString::null /* PENDING(bo) folder->location() */ );
01014     kdDebug(5006) << "Emitting DCOP signal signalRefresh( " << type << " )" << endl;
01015   }
01016 }
01017 
01018 // This is among other things called when an expunge of a folder happens
01019 void KMailICalIfaceImpl::slotRefreshFolder( KMFolder* folder)
01020 {
01021   // TODO: The resources would of course be better off, if only this
01022   // folder would need refreshing. Currently it just orders a reload of
01023   // the type of the folder
01024   if( mUseResourceIMAP && folder ) {
01025     if( folder == mCalendar || folder == mContacts
01026         || folder == mNotes || folder == mTasks
01027         || folder == mJournals || mExtraFolders.find( folder->location() ) ) {
01028       // Refresh the folder of this type
01029       KMail::FolderContentsType ct = folder->storage()->contentsType();
01030       slotRefresh( s_folderContentsType[ct].contentsTypeStr );
01031     }
01032   }
01033 }
01034 
01035 /****************************
01036  * The folder and message stuff code
01037  */
01038 
01039 KMFolder* KMailICalIfaceImpl::folderFromType( const QString& type,
01040                                               const QString& folder )
01041 {
01042   if( mUseResourceIMAP ) {
01043     KMFolder* f = 0;
01044     if ( !folder.isEmpty() ) {
01045       f = extraFolder( type, folder );
01046       if ( f )
01047         return f;
01048     }
01049 
01050     if( type == "Calendar" ) f = mCalendar;
01051     else if( type == "Contact" ) f = mContacts;
01052     else if( type == "Note" ) f = mNotes;
01053     else if( type == "Task" || type == "Todo" ) f = mTasks;
01054     else if( type == "Journal" ) f = mJournals;
01055 
01056     if ( f && ( folder.isEmpty() || folder == f->location() ) )
01057       return f;
01058 
01059     kdError(5006) << "No folder ( " << type << ", " << folder << " )\n";
01060   }
01061 
01062   return 0;
01063 }
01064 
01065 
01066 // Returns true if folder is a resource folder. If the resource isn't enabled
01067 // this always returns false
01068 bool KMailICalIfaceImpl::isResourceFolder( KMFolder* folder ) const
01069 {
01070   return mUseResourceIMAP && folder &&
01071     ( isStandardResourceFolder( folder ) || mExtraFolders.find( folder->location() )!=0 );
01072 }
01073 
01074 bool KMailICalIfaceImpl::isStandardResourceFolder( KMFolder* folder ) const
01075 {
01076   return ( folder == mCalendar || folder == mTasks || folder == mJournals ||
01077            folder == mNotes || folder == mContacts );
01078 }
01079 
01080 bool KMailICalIfaceImpl::hideResourceFolder( KMFolder* folder ) const
01081 {
01082   return mHideFolders && isResourceFolder( folder );
01083 }
01084 
01085 bool KMailICalIfaceImpl::hideResourceAccountRoot( KMFolder* folder ) const
01086 {
01087   KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
01088   bool hide = dimapFolder && mHideFolders 
01089        && (int)dimapFolder->account()->id() == GlobalSettings::self()->theIMAPResourceAccount()
01090        && GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount();
01091   return hide;
01092 
01093 }
01094 
01095 KFolderTreeItem::Type KMailICalIfaceImpl::folderType( KMFolder* folder ) const
01096 {
01097   if( mUseResourceIMAP && folder ) {
01098     if( folder == mCalendar || folder == mContacts
01099         || folder == mNotes || folder == mTasks
01100         || folder == mJournals || mExtraFolders.find( folder->location() ) ) {
01101       KMail::FolderContentsType ct = folder->storage()->contentsType();
01102       return s_folderContentsType[ct].treeItemType;
01103     }
01104   }
01105 
01106   return KFolderTreeItem::Other;
01107 }
01108 
01109 // Global tables of foldernames is different languages
01110 // For now: 0->English, 1->German, 2->French, 3->Dutch
01111 static QMap<KFolderTreeItem::Type,QString> folderNames[4];
01112 QString KMailICalIfaceImpl::folderName( KFolderTreeItem::Type type, int language ) const
01113 {
01114   // With the XML storage, folders are always (internally) named in English
01115   if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
01116     language = 0;
01117 
01118   static bool folderNamesSet = false;
01119   if( !folderNamesSet ) {
01120     folderNamesSet = true;
01121     /* NOTE: If you add something here, you also need to update
01122        GroupwarePage in configuredialog.cpp */
01123 
01124     // English
01125     folderNames[0][KFolderTreeItem::Calendar] = QString::fromLatin1("Calendar");
01126     folderNames[0][KFolderTreeItem::Tasks] = QString::fromLatin1("Tasks");
01127     folderNames[0][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
01128     folderNames[0][KFolderTreeItem::Contacts] = QString::fromLatin1("Contacts");
01129     folderNames[0][KFolderTreeItem::Notes] = QString::fromLatin1("Notes");
01130 
01131     // German
01132     folderNames[1][KFolderTreeItem::Calendar] = QString::fromLatin1("Kalender");
01133     folderNames[1][KFolderTreeItem::Tasks] = QString::fromLatin1("Aufgaben");
01134     folderNames[1][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
01135     folderNames[1][KFolderTreeItem::Contacts] = QString::fromLatin1("Kontakte");
01136     folderNames[1][KFolderTreeItem::Notes] = QString::fromLatin1("Notizen");
01137 
01138     // French
01139     folderNames[2][KFolderTreeItem::Calendar] = QString::fromLatin1("Calendrier");
01140     folderNames[2][KFolderTreeItem::Tasks] = QString::fromLatin1("Tâches");
01141     folderNames[2][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
01142     folderNames[2][KFolderTreeItem::Contacts] = QString::fromLatin1("Contacts");
01143     folderNames[2][KFolderTreeItem::Notes] = QString::fromLatin1("Notes");
01144 
01145     // Dutch
01146     folderNames[3][KFolderTreeItem::Calendar] = QString::fromLatin1("Agenda");
01147     folderNames[3][KFolderTreeItem::Tasks] = QString::fromLatin1("Taken");
01148     folderNames[3][KFolderTreeItem::Journals] = QString::fromLatin1("Logboek");
01149     folderNames[3][KFolderTreeItem::Contacts] = QString::fromLatin1("Contactpersonen");
01150     folderNames[3][KFolderTreeItem::Notes] = QString::fromLatin1("Notities");
01151   }
01152 
01153   if( language < 0 || language > 3 ) {
01154     return folderNames[mFolderLanguage][type];
01155   }
01156   else {
01157     return folderNames[language][type];
01158   }
01159 }
01160 
01161 
01162 // Find message matching a given UID
01163 KMMessage *KMailICalIfaceImpl::findMessageByUID( const QString& uid, KMFolder* folder )
01164 {
01165   if( !folder || !mUIDToSerNum.contains( uid ) ) return 0;
01166   int i;
01167   KMFolder *aFolder;
01168   KMMsgDict::instance()->getLocation( mUIDToSerNum[uid], &aFolder, &i );
01169   Q_ASSERT( aFolder == folder );
01170   return folder->getMsg( i );
01171 }
01172 
01173 // Find message matching a given serial number
01174 KMMessage *KMailICalIfaceImpl::findMessageBySerNum( Q_UINT32 serNum, KMFolder* folder )
01175 {
01176   if( !folder ) return 0;
01177 
01178   KMMessage *message = 0;
01179   KMFolder* aFolder = 0;
01180   int index;
01181   KMMsgDict::instance()->getLocation( serNum, &aFolder, &index );
01182   if( aFolder && aFolder != folder ) {
01183     kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) found it in folder " << aFolder->location() << ", expected " << folder->location() << endl;
01184   } else {
01185     if( aFolder )
01186       message = aFolder->getMsg( index );
01187     if (!message)
01188       kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) invalid serial number\n" << endl;
01189   }
01190   return message;
01191 }
01192 
01193 void KMailICalIfaceImpl::deleteMsg( KMMessage *msg )
01194 {
01195   if( !msg ) return;
01196   // Commands are now delayed; can't use that anymore, we need immediate deletion
01197   //( new KMDeleteMsgCommand( msg->parent(), msg ) )->start();
01198   KMFolder *srcFolder = msg->parent();
01199   int idx = srcFolder->find(msg);
01200   assert(idx != -1);
01201   srcFolder->removeMsg(idx);
01202   delete msg;
01203   addFolderChange( srcFolder, Contents );
01204 }
01205 
01206 void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder,
01207                                                     KMail::FolderContentsType contentsType )
01208 {
01209   if ( !mUseResourceIMAP )
01210     return;
01211 //  kdDebug(5006) << "folderContentsTypeChanged( " << folder->name()
01212 //                << ", " << contentsType << ")\n";
01213 
01214   // The builtins can't change type
01215   if ( isStandardResourceFolder( folder ) )
01216     return;
01217 
01218   // Check if already know that 'extra folder'
01219   const QString location = folder->location();
01220   ExtraFolder* ef = mExtraFolders.find( location );
01221   if ( ef && ef->folder ) {
01222     // Notify that the old folder resource is no longer available
01223     subresourceDeleted(folderContentsType( folder->storage()->contentsType() ), location );
01224 
01225     if ( contentsType == 0 ) {
01226       // Delete the old entry, stop listening and stop here
01227       mExtraFolders.remove( location );
01228       folder->disconnect( this );
01229       return;
01230     }
01231     // So the type changed to another groupware type, ok.
01232   } else {
01233     if ( ef && !ef->folder ) // deleted folder, clean up
01234       mExtraFolders.remove( location );
01235     if ( contentsType == 0 )
01236         return;
01237 
01238     //kdDebug(5006) << "registering " << location << " as extra folder" << endl;
01239     // Make a new entry for the list
01240     ef = new ExtraFolder( folder );
01241     mExtraFolders.insert( location, ef );
01242 
01243     FolderInfo info = readFolderInfo( folder );
01244     mFolderInfoMap.insert( folder, info );
01245 
01246     // Adjust the folder names of all foo.default folders.
01247     // German users will get Kalender as the name of all default Calendar folders,
01248     // including their own, so that the default calendar folder of their Japanese
01249     // coworker appears as /user/hirohito/Kalender, although Hirohito sees his folder
01250     // in Japanese. On the server the folders are always in English.
01251     if ( folder->folderType() == KMFolderTypeCachedImap ) {
01252       QString annotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
01253       kdDebug(5006) << "folderContentsTypeChanged: " << folder->name() << " has annotation " << annotation << endl;
01254       if ( annotation == QString( s_folderContentsType[contentsType].annotation ) + ".default" )
01255         folder->setLabel( localizedDefaultFolderName( contentsType ) );
01256     }
01257 
01258     connectFolder( folder );
01259   }
01260   // Tell about the new resource
01261   subresourceAdded( folderContentsType( contentsType ), location, folder->prettyURL() );
01262 }
01263 
01264 KMFolder* KMailICalIfaceImpl::extraFolder( const QString& type,
01265                                            const QString& folder )
01266 {
01267   // If an extra folder exists that matches the type and folder location,
01268   // use that
01269   int t = folderContentsType( type );
01270   if ( t < 1 || t > 5 )
01271     return 0;
01272 
01273   ExtraFolder* ef = mExtraFolders.find( folder );
01274   if ( ef && ef->folder && ef->folder->storage()->contentsType() == t )
01275     return ef->folder;
01276 
01277   return 0;
01278 }
01279 
01280 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( KMFolder* folder ) const
01281 {
01282   FolderInfoMap::ConstIterator it = mFolderInfoMap.find( folder );
01283   if ( it != mFolderInfoMap.end() )
01284     return (*it).mStorageFormat;
01285   return globalStorageFormat();
01286 }
01287 
01288 void KMailICalIfaceImpl::setStorageFormat( KMFolder* folder, StorageFormat format )
01289 {
01290   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01291   if ( it != mFolderInfoMap.end() ) {
01292     (*it).mStorageFormat = format;
01293   } else {
01294     FolderInfo info( format, NoChange );
01295     mFolderInfoMap.insert( folder, info );
01296   }
01297   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01298   configGroup.writeEntry( folder->idString() + "-storageFormat",
01299                           format == StorageXML ? "xml" : "icalvcard" );
01300 }
01301 
01302 void KMailICalIfaceImpl::addFolderChange( KMFolder* folder, FolderChanges changes )
01303 {
01304   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01305   if ( it != mFolderInfoMap.end() ) {
01306     (*it).mChanges = static_cast<FolderChanges>( (*it).mChanges | changes );
01307   } else { // Otherwise, well, it's a folder we don't care about.
01308     kdDebug(5006) << "addFolderChange: nothing known about folder " << folder->location() << endl;
01309   }
01310   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01311   configGroup.writeEntry( folder->idString() + "-changes", (*it).mChanges );
01312 }
01313 
01314 KMailICalIfaceImpl::FolderInfo KMailICalIfaceImpl::readFolderInfo( const KMFolder * const folder ) const
01315 {
01316   KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
01317   QString str = configGroup.readEntry( folder->idString() + "-storageFormat", "unset" );
01318   FolderInfo info;
01319   if ( str == "unset" ) {
01320     info.mStorageFormat = globalStorageFormat();
01321     configGroup.writeEntry( folder->idString() + "-storageFormat",
01322                             info.mStorageFormat == StorageXML ? "xml" : "icalvcard" );
01323   } else {
01324     info.mStorageFormat = ( str == "xml" ) ? StorageXML : StorageIcalVcard;
01325   }
01326   info.mChanges = (FolderChanges) configGroup.readNumEntry( folder->idString() + "-changes" );
01327   return info;
01328 }
01329 
01330 
01331 void KMailICalIfaceImpl::folderSynced( KMFolder* folder, const KURL& folderURL )
01332 {
01333   FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
01334   if ( it != mFolderInfoMap.end() && (*it).mChanges ) {
01335     handleFolderSynced( folder, folderURL, (*it).mChanges );
01336     (*it).mChanges = NoChange;
01337   }
01338 }
01339 
01340 void KMailICalIfaceImpl::handleFolderSynced( KMFolder* folder,
01341                                              const KURL& folderURL,
01342                                              int _changes )
01343 {
01344   // This is done here instead of in the resource, because
01345   // there could be 0, 1, or N kolab resources at this point.
01346   // We can hack the N case, but not the 0 case.
01347   // So the idea of a DCOP signal for this wouldn't work.
01348   if ( ( _changes & KMailICalIface::Contents ) ||
01349        ( _changes & KMailICalIface::ACL ) ) {
01350     if ( storageFormat( folder ) == StorageXML && folder->storage()->contentsType() == KMail::ContentsTypeCalendar )
01351       triggerKolabFreeBusy( folderURL );
01352   }
01353 }
01354 
01355 void KMailICalIfaceImpl::folderDeletedOnServer( const KURL& folderURL )
01356 {
01357   triggerKolabFreeBusy( folderURL );
01358 }
01359 
01360 void KMailICalIfaceImpl::triggerKolabFreeBusy( const KURL& folderURL )
01361 {
01362   /* Steffen said: you must issue an authenticated HTTP GET request to
01363      https://kolabserver/freebusy/trigger/user@domain/Folder/NestedFolder.pfb
01364      (replace .pfb with .xpfb for extended fb lists). */
01365   KURL httpURL( folderURL );
01366   // Keep username ("user@domain"), pass, and host from the imap url
01367   httpURL.setProtocol( "https" );
01368   httpURL.setPort( 0 ); // remove imap port
01369 
01370   // IMAP path is either /INBOX/<path> or /user/someone/<path>
01371   QString path = folderURL.path( -1 );
01372   Q_ASSERT( path.startsWith( "/" ) );
01373   int secondSlash = path.find( '/', 1 );
01374   if ( secondSlash == -1 ) {
01375     kdWarning() << "KCal::ResourceKolab::fromKMailFolderSynced path is too short: " << path << endl;
01376     return;
01377   }
01378   if ( path.startsWith( "/INBOX/", false ) ) {
01379     // If INBOX, replace it with the username (which is user@domain)
01380     path = path.mid( secondSlash );
01381     path.prepend( folderURL.user() );
01382   } else {
01383     // If user, just remove it. So we keep the IMAP-returned username.
01384     // This assumes it's a known user on the same domain.
01385     path = path.mid( secondSlash );
01386   }
01387 
01388   httpURL.setPath( "/freebusy/trigger/" + path + ".pfb" );
01389   httpURL.setQuery( QString::null );
01390   // Ensure that we encode everything with UTF8
01391   httpURL = KURL( httpURL.url(0,106), 106 );
01392   kdDebug() << "Triggering PFB update for " << folderURL << " : getting " << httpURL << endl;
01393   // "Fire and forget". No need for error handling, nor for explicit deletion.
01394   // Maybe we should try to prevent launching it if it's already running (for this URL) though.
01395   /*KIO::Job* job =*/ KIO::get( httpURL, false, false /*no progress info*/ );
01396 }
01397 
01398 void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder )
01399 {
01400   if ( isResourceFolder( folder ) ) {
01401     const QString location = folder->location();
01402     const QString contentsTypeStr = folderContentsType( folder->storage()->contentsType() );
01403     subresourceDeleted( contentsTypeStr, location );
01404 
01405     subresourceAdded( contentsTypeStr, location, folder->prettyURL()  /*,
01406                       !folder->isReadOnly() , folderIsAlarmRelevant( folder ) TODO */ );
01407 
01408   }
01409 }
01410 
01411 // Must only be connected to a signal from KMFolder!
01412 void KMailICalIfaceImpl::slotFolderRenamed()
01413 {
01414   const KMFolder* folder = static_cast<const KMFolder *>( sender() );
01415   slotFolderPropertiesChanged( const_cast<KMFolder*>( folder ) );
01416 }
01417 
01418 void KMailICalIfaceImpl::slotFolderLocationChanged( const QString &oldLocation,
01419                                                     const QString &newLocation )
01420 {
01421   KMFolder *folder = findResourceFolder( oldLocation );
01422   ExtraFolder* ef = mExtraFolders.find( oldLocation );
01423   if ( ef ) {
01424     // reuse the ExtraFolder entry, but adjust the key
01425     mExtraFolders.setAutoDelete( false );
01426     mExtraFolders.remove( oldLocation );
01427     mExtraFolders.setAutoDelete( true );
01428     mExtraFolders.insert( newLocation, ef );
01429   }
01430   if (  folder )
01431     subresourceDeleted( folderContentsType(  folder->storage()->contentsType() ), oldLocation );
01432 
01433 }
01434 
01435 KMFolder* KMailICalIfaceImpl::findResourceFolder( const QString& resource )
01436 {
01437   // Try the standard folders
01438   if( mCalendar && mCalendar->location() == resource )
01439     return mCalendar;
01440   if ( mContacts && mContacts->location() == resource )
01441     return mContacts;
01442   if ( mNotes && mNotes->location() == resource )
01443     return mNotes;
01444   if ( mTasks && mTasks->location() == resource )
01445     return mTasks;
01446   if ( mJournals && mJournals->location() == resource )
01447     return mJournals;
01448 
01449   // No luck. Try the extrafolders
01450   ExtraFolder* ef = mExtraFolders.find( resource );
01451   if ( ef )
01452     return ef->folder;
01453 
01454   // No luck at all
01455   return 0;
01456 }
01457 
01458 /****************************
01459  * The config stuff
01460  */
01461 
01462 void KMailICalIfaceImpl::readConfig()
01463 {
01464   bool enabled = GlobalSettings::self()->theIMAPResourceEnabled();
01465 
01466   if( !enabled ) {
01467     if( mUseResourceIMAP == true ) {
01468       // Shutting down
01469       mUseResourceIMAP = false;
01470       cleanup();
01471       reloadFolderTree();
01472     }
01473     return;
01474   }
01475   mUseResourceIMAP = enabled;
01476 
01477   // Read remaining options
01478   const bool hideFolders = GlobalSettings::self()->hideGroupwareFolders();
01479   QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();
01480 
01481   // Find the folder parent
01482   KMFolderDir* folderParentDir;
01483   KMFolderType folderType;
01484   KMFolder* folderParent = kmkernel->findFolderById( parentName );
01485   if( folderParent == 0 ) {
01486     // Parent folder not found. It was probably deleted. The user will have to
01487     // configure things again.
01488     kdDebug(5006) << "Groupware folder " << parentName << " not found. Groupware functionality disabled" << endl;
01489     // Or maybe the inbox simply wasn't created on the first startup
01490     KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
01491     Q_ASSERT( account );
01492     if ( account ) {
01493       // just in case we were connected already
01494       disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01495                this, SLOT( slotCheckDone() ) );
01496       connect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01497                this, SLOT( slotCheckDone() ) );
01498     }
01499     mUseResourceIMAP = false;
01500     // We can't really call cleanup(), if those folders were completely deleted.
01501     mCalendar = 0;
01502     mTasks    = 0;
01503     mJournals = 0;
01504     mContacts = 0;
01505     mNotes    = 0;
01506     return;
01507   } else {
01508     folderParentDir = folderParent->createChildFolder();
01509     folderType = folderParent->folderType();
01510   }
01511 
01512   // Make sure the folder parent has the subdirs
01513   // Globally there are 3 cases: nothing found, some stuff found by type/name heuristics, or everything found OK
01514   bool noneFound = true;
01515   bool mustFix = false; // true when at least one was found by heuristics
01516   QValueVector<StandardFolderSearchResult> results( KMail::ContentsTypeLast + 1 );
01517   for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
01518     if ( i != KMail::ContentsTypeMail ) {
01519       results[i] = findStandardResourceFolder( folderParentDir, static_cast<KMail::FolderContentsType>(i) );
01520       if ( results[i].found == StandardFolderSearchResult::FoundAndStandard )
01521         noneFound = false;
01522       else if ( results[i].found == StandardFolderSearchResult::FoundByType ||
01523                 results[i].found == StandardFolderSearchResult::FoundByName ) {
01524         mustFix = true;
01525         noneFound = false;
01526       } else // NotFound
01527         mustFix = true;
01528     }
01529   }
01530 
01531   // Check if something changed
01532   if( mUseResourceIMAP && !noneFound && !mustFix && mFolderParentDir == folderParentDir
01533       && mFolderType == folderType ) {
01534     // Nothing changed
01535     if ( hideFolders != mHideFolders ) {
01536       // Well, the folder hiding has changed
01537       mHideFolders = hideFolders;
01538       reloadFolderTree();
01539     }
01540     return;
01541   }
01542 
01543   if( noneFound || mustFix ) {
01544     QString msg;
01545     QString parentFolderName = folderParent != 0 ? folderParent->name() : folderParentDir->name();
01546     if ( noneFound ) {
01547       // No subfolder was found, so ask if we can make them
01548       msg = i18n("KMail will now create the required groupware folders"
01549                  " as subfolders of %1; if you do not want this, cancel"
01550                  " and the IMAP resource will be disabled").arg(parentFolderName);
01551     } else {
01552       // Some subfolders were found, be more precise
01553       QString operations = "<ul>";
01554       for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
01555         if ( i != KMail::ContentsTypeMail ) {
01556           QString typeName = localizedDefaultFolderName( static_cast<KMail::FolderContentsType>( i ) );
01557           if ( results[i].found == StandardFolderSearchResult::NotFound )
01558             operations += "<li>" + i18n( "%1: no folder found. It will be created." ).arg( typeName ) + "</li>";
01559           else if ( results[i].found == StandardFolderSearchResult::FoundByType || results[i].found == StandardFolderSearchResult::FoundByName )
01560             operations += "<li>" + i18n( "%1: found folder %2. It will be set as the main groupware folder." ).
01561                           arg( typeName ).arg( results[i].folder->label() ) + "</li>";
01562         }
01563       }
01564       operations += "</ul>";
01565 
01566       msg = i18n("<qt>KMail found the following groupware folders in %1 and needs to perform the following operations: %2"
01567                  "<br>If you do not want this, cancel"
01568                  " and the IMAP resource will be disabled").arg(parentFolderName, operations);
01569 
01570     }
01571 
01572     if( KMessageBox::questionYesNo( 0, msg,
01573                                     i18n("Standard Groupware Folders"), KStdGuiItem::cont(), KStdGuiItem::cancel() ) == KMessageBox::No ) {
01574 
01575       GlobalSettings::self()->setTheIMAPResourceEnabled( false );
01576       mUseResourceIMAP = false;
01577       mFolderParentDir = 0;
01578       mFolderParent = 0;
01579       reloadFolderTree();
01580       return;
01581     }
01582   }
01583 
01584   // Make the new settings work
01585   mUseResourceIMAP = true;
01586   mFolderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
01587   if( mFolderLanguage > 3 ) mFolderLanguage = 0;
01588   mFolderParentDir = folderParentDir;
01589   mFolderParent = folderParent;
01590   mFolderType = folderType;
01591   mHideFolders = hideFolders;
01592 
01593   // Close the previous folders
01594   cleanup();
01595 
01596   // Set the new folders
01597   mCalendar = initFolder( KMail::ContentsTypeCalendar );
01598   mTasks    = initFolder( KMail::ContentsTypeTask );
01599   mJournals = initFolder( KMail::ContentsTypeJournal );
01600   mContacts = initFolder( KMail::ContentsTypeContact );
01601   mNotes    = initFolder( KMail::ContentsTypeNote );
01602 
01603   // Store final annotation (with .default) so that we won't ask again on next startup
01604   if ( mCalendar->folderType() == KMFolderTypeCachedImap )
01605     static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType();
01606   if ( mTasks->folderType() == KMFolderTypeCachedImap )
01607     static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType();
01608   if ( mJournals->folderType() == KMFolderTypeCachedImap )
01609     static_cast<KMFolderCachedImap *>( mJournals->storage() )->updateAnnotationFolderType();
01610   if ( mContacts->folderType() == KMFolderTypeCachedImap )
01611     static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType();
01612   if ( mNotes->folderType() == KMFolderTypeCachedImap )
01613     static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType();
01614 
01615   // BEGIN TILL TODO The below only uses the dimap folder manager, which
01616   // will fail for all other folder types. Adjust.
01617 
01618   kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
01619   kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl;
01620   kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;
01621 
01622   // Find all extra folders
01623   QStringList folderNames;
01624   QValueList<QGuardedPtr<KMFolder> > folderList;
01625   kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
01626   for(QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
01627       it != folderList.end(); ++it)
01628   {
01629     FolderStorage* storage = (*it)->storage();
01630     if ( storage->contentsType() != 0 ) {
01631       folderContentsTypeChanged( *it, storage->contentsType() );
01632     }
01633   }
01634 
01635   // If we just created them, they might have been registered as extra folders temporarily.
01636   // -> undo that.
01637   mExtraFolders.remove( mCalendar->location() );
01638   mExtraFolders.remove( mTasks->location() );
01639   mExtraFolders.remove( mJournals->location() );
01640   mExtraFolders.remove( mContacts->location() );
01641   mExtraFolders.remove( mNotes->location() );
01642 
01643   // END TILL TODO
01644 
01645   subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label() );
01646   subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label() );
01647   subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(), mJournals->label() );
01648   subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label() );
01649   subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label() );
01650 
01651   reloadFolderTree();
01652 }
01653 
01654 void KMailICalIfaceImpl::slotCheckDone()
01655 {
01656   QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();
01657   KMFolder* folderParent = kmkernel->findFolderById( parentName );
01658   //kdDebug(5006) << k_funcinfo << " folderParent=" << folderParent << endl;
01659   if ( folderParent )  // cool it exists now
01660   {
01661     KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
01662     if ( account )
01663       disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01664                   this, SLOT( slotCheckDone() ) );
01665     readConfig();
01666   }
01667 }
01668 
01669 KMFolder* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType )
01670 {
01671   // Figure out what type of folder this is supposed to be
01672   KMFolderType type = mFolderType;
01673   if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
01674 
01675   KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
01676   //kdDebug(5006) << "KMailICalIfaceImpl::initFolder " << folderName( itemType ) << endl;
01677 
01678   // Find the folder
01679   StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType );
01680   KMFolder* folder = result.folder;
01681 
01682   if ( !folder ) {
01683     // The folder isn't there yet - create it
01684     folder =
01685       mFolderParentDir->createFolder( localizedDefaultFolderName( contentsType ), false, type );
01686     if( mFolderType == KMFolderTypeImap ) {
01687       KMFolderImap* parentFolder = static_cast<KMFolderImap*>( mFolderParent->storage() );
01688       parentFolder->createFolder( localizedDefaultFolderName( contentsType ) );
01689       static_cast<KMFolderImap*>( folder->storage() )->setAccount( parentFolder->account() );
01690     }
01691     // Groupware folder created, use the global setting for storage format
01692     setStorageFormat( folder, globalStorageFormat() );
01693   } else {
01694     FolderInfo info = readFolderInfo( folder );
01695     mFolderInfoMap.insert( folder, info );
01696     //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location()  << endl;
01697   }
01698 
01699   if( folder->canAccess() != 0 ) {
01700     KMessageBox::sorry(0, i18n("You do not have read/write permission to your %1 folder.")
01701                        .arg( folderName( itemType ) ) );
01702     return 0;
01703   }
01704   folder->storage()->setContentsType( contentsType );
01705   folder->setSystemFolder( true );
01706   folder->storage()->writeConfig();
01707   folder->open("ifacefolder");
01708   connectFolder( folder );
01709   return folder;
01710 }
01711 
01712 void KMailICalIfaceImpl::connectFolder( KMFolder* folder )
01713 {
01714   // avoid multiple connections
01715   disconnect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
01716               this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
01717   disconnect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
01718               this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
01719   disconnect( folder, SIGNAL( expunged( KMFolder* ) ),
01720               this, SLOT( slotRefreshFolder( KMFolder* ) ) );
01721   disconnect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
01722               this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
01723   disconnect( folder, SIGNAL( nameChanged() ),
01724               this, SLOT( slotFolderRenamed() ) );
01725   disconnect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
01726               this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );
01727 
01728   // Setup the signals to listen for changes
01729   connect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
01730            this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
01731   connect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
01732            this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
01733   connect( folder, SIGNAL( expunged( KMFolder* ) ),
01734            this, SLOT( slotRefreshFolder( KMFolder* ) ) );
01735   connect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
01736            this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
01737   connect( folder, SIGNAL( nameChanged() ),
01738            this, SLOT( slotFolderRenamed() ) );
01739   connect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
01740            this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );
01741 
01742 }
01743 
01744 static void cleanupFolder( KMFolder* folder, KMailICalIfaceImpl* _this )
01745 {
01746   if( folder ) {
01747     folder->setSystemFolder( false );
01748     folder->disconnect( _this );
01749     folder->close("ifacefolder");
01750   }
01751 }
01752 
01753 void KMailICalIfaceImpl::cleanup()
01754 {
01755   cleanupFolder( mContacts, this );
01756   cleanupFolder( mCalendar, this );
01757   cleanupFolder( mNotes, this );
01758   cleanupFolder( mTasks, this );
01759   cleanupFolder( mJournals, this );
01760 
01761   mContacts = mCalendar = mNotes = mTasks = mJournals = 0;
01762 }
01763 
01764 QString KMailICalIfaceImpl::folderPixmap( KFolderTreeItem::Type type ) const
01765 {
01766   if( !mUseResourceIMAP )
01767     return QString::null;
01768 
01769   if( type == KFolderTreeItem::Contacts )
01770     return QString::fromLatin1( "kmgroupware_folder_contacts" );
01771   else if( type == KFolderTreeItem::Calendar )
01772     return QString::fromLatin1( "kmgroupware_folder_calendar" );
01773   else if( type == KFolderTreeItem::Notes )
01774     return QString::fromLatin1( "kmgroupware_folder_notes" );
01775   else if( type == KFolderTreeItem::Tasks )
01776     return QString::fromLatin1( "kmgroupware_folder_tasks" );
01777   else if( type == KFolderTreeItem::Journals )
01778     return QString::fromLatin1( "kmgroupware_folder_journals" );
01779 
01780   return QString::null;
01781 }
01782 
01783 static void reloadFolderTree()
01784 {
01785   // Make the folder tree show the icons or not
01786   kmkernel->folderMgr()->contentsChanged();
01787 }
01788 
01789 // This is a very light-weight and fast 'parser' to retrieve
01790 // a data entry from a vCal taking continuation lines
01791 // into account
01792 static void vPartMicroParser( const QString& str, QString& s )
01793 {
01794   QString line;
01795   uint len = str.length();
01796 
01797   for( uint i=0; i<len; ++i){
01798     if( str[i] == '\r' || str[i] == '\n' ){
01799       if( str[i] == '\r' )
01800         ++i;
01801       if( i+1 < len && str[i+1] == ' ' ){
01802         // found a continuation line, skip it's leading blanc
01803         ++i;
01804       }else{
01805         // found a logical line end, process the line
01806         if( line.startsWith( s ) ) {
01807           s = line.mid( s.length() + 1 );
01808           return;
01809         }
01810         line = "";
01811       }
01812     } else {
01813       line += str[i];
01814     }
01815   }
01816 
01817   // Not found. Clear it
01818   s.truncate(0);
01819 }
01820 
01821 // Returns the first child folder having the given annotation
01822 static KMFolder* findFolderByAnnotation( KMFolderDir* folderParentDir, const QString& annotation )
01823 {
01824     QPtrListIterator<KMFolderNode> it( *folderParentDir );
01825     for ( ; it.current(); ++it ) {
01826       if ( !it.current()->isDir() ) {
01827         KMFolder* folder = static_cast<KMFolder *>( it.current() );
01828         if ( folder->folderType() == KMFolderTypeCachedImap ) {
01829           QString folderAnnotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
01830           //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl;
01831           if ( folderAnnotation == annotation )
01832             return folder;
01833         }
01834       }
01835     }
01836     return 0;
01837 }
01838 
01839 KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType )
01840 {
01841   if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
01842   {
01843     // Look for a folder with an annotation like "event.default"
01844     KMFolder* folder = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) + ".default" );
01845     if ( folder )
01846       return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundAndStandard );
01847 
01848     // Fallback: look for a folder with an annotation like "event"
01849     folder = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) );
01850     if ( folder )
01851       return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundByType );
01852 
01853     // Fallback: look for the folder by name (we'll need to change its type)
01854     KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) );
01855     if ( node && !node->isDir() )
01856       return StandardFolderSearchResult( static_cast<KMFolder *>( node ), StandardFolderSearchResult::FoundByName );
01857 
01858     kdDebug(5006) << "findStandardResourceFolder: found no resource folder for " << s_folderContentsType[contentsType].annotation << endl;
01859     return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
01860   }
01861   else // icalvcard: look up standard resource folders by name
01862   {
01863     KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
01864     unsigned int folderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
01865     if( folderLanguage > 3 ) folderLanguage = 0;
01866     KMFolderNode* node = folderParentDir->hasNamedFolder( folderName( itemType, folderLanguage ) );
01867     if ( !node || node->isDir() )
01868       return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
01869     return StandardFolderSearchResult( static_cast<KMFolder*>( node ), StandardFolderSearchResult::FoundAndStandard );
01870   }
01871 }
01872 
01873 void KMailICalIfaceImpl::setResourceQuiet(bool q)
01874 {
01875   mResourceQuiet = q;
01876 }
01877 
01878 bool KMailICalIfaceImpl::isResourceQuiet() const
01879 {
01880   return mResourceQuiet;
01881 }
01882 
01883 #include "kmailicalifaceimpl.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys