kmail

kmailicalifaceimpl.cpp

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