kmail Library API Documentation

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