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