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 TQt library by Trolltech AS, Norway (or with modified versions 00026 of TQt that use the same license as TQt), 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 TQt. 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 <tqfile.h> 00065 #include <tqmap.h> 00066 #include <tqtextcodec.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 TQMap<TQString, TQString> *KMailICalIfaceImpl::mSubResourceUINamesMap = new TQMap<TQString, TQString>; 00080 00081 // Local helper methods 00082 static void vPartMicroParser( const TQString& str, TQString& 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 TQString folderContentsType( KMail::FolderContentsType type ) 00102 { 00103 return s_folderContentsType[type].contentsTypeStr; 00104 } 00105 00106 static TQString 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 TQString& 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 TQString 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" ), TQObject( 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, TQT_SIGNAL( configChanged() ), this, TQT_SLOT( readConfig() ) ); 00165 connect( kmkernel, TQT_SIGNAL( folderRemoved( KMFolder* ) ), 00166 this, TQT_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 && TQString( 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 TQString& 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 TQString& attachmentURL, 00243 const TQString& attachmentName, 00244 const TQString& 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 TQString fileName( url.path() ); 00254 TQFile file( fileName ); 00255 if( file.open( IO_ReadOnly ) ) { 00256 TQByteArray 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 TQCString sType = attachmentMimetype.left( iSlash ).latin1(); 00265 const TQCString sSubtype = attachmentMimetype.mid( iSlash+1 ).latin1(); 00266 msgPart.setTypeStr( sType ); 00267 msgPart.setSubtypeStr( sSubtype ); 00268 TQCString ctd("attachment;\n filename=\""); 00269 ctd.append( attachmentName.latin1() ); 00270 ctd.append("\""); 00271 msgPart.setContentDisposition( ctd ); 00272 TQValueList<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 TQString& mimetype, TQString& s ) 00311 { 00312 const int iSlash = mimetype.find('/'); 00313 const TQCString sType = mimetype.left( iSlash ).latin1(); 00314 const TQCString 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( TQTextCodec::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 TQString& 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 TQString 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 TQ_UINT32 KMailICalIfaceImpl::addIncidenceKolab( KMFolder& folder, 00406 const TQString& subject, 00407 const TQString& plainTextBody, 00408 const TQMap<TQCString, TQString>& customHeaders, 00409 const TQStringList& attachmentURLs, 00410 const TQStringList& attachmentNames, 00411 const TQStringList& attachmentMimetypes ) 00412 { 00413 kdDebug(5006) << "KMailICalIfaceImpl::addIncidenceKolab( " << attachmentNames << " )" << endl; 00414 00415 TQ_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 TQMap<TQCString, TQString>::ConstIterator ith = customHeaders.begin(); 00425 const TQMap<TQCString, TQString>::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 TQStringList::ConstIterator itmime = attachmentMimetypes.begin(); 00445 TQStringList::ConstIterator iturl = attachmentURLs.begin(); 00446 for( TQStringList::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 TQString& resource, 00480 TQ_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 TQString& mimetype, 00512 const TQString& 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 TQMap<TQ_UINT32, TQString> KMailICalIfaceImpl::incidencesKolab( const TQString& mimetype, 00534 const TQString& resource, 00535 int startIndex, 00536 int nbMessages ) 00537 { 00541 00542 TQMap<TQ_UINT32, TQString> 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 kdDebug(5006) << k_funcinfo << "Getting incidences (" << mimetype << ") for folder " << f->label() 00555 << ", starting with index " << startIndex << ", " << nbMessages << " messages." << endl; 00556 kdDebug(5006) << "The folder has " << f->count() << " messages." << endl; 00557 00558 int stopIndex = nbMessages == -1 ? f->count() : 00559 TQMIN( f->count(), startIndex + nbMessages ); 00560 00561 for(int i = startIndex; i < stopIndex; ++i) { 00562 #if 0 00563 bool unget = !f->isMessage(i); 00564 KMMessage* msg = f->getMsg( i ); 00565 #else // faster 00566 KMMessage* msg = f->storage()->readTemporaryMsg(i); 00567 #endif 00568 if ( msg ) { 00569 const int iSlash = mimetype.find('/'); 00570 const TQCString sType = mimetype.left( iSlash ).latin1(); 00571 const TQCString sSubtype = mimetype.mid( iSlash+1 ).latin1(); 00572 if ( sType.isEmpty() || sSubtype.isEmpty() ) { 00573 kdError(5006) << mimetype << " not an type/subtype combination" << endl; 00574 } else { 00575 DwBodyPart* dwPart = findBodyPartByMimeType( *msg, sType, sSubtype ); 00576 if ( dwPart ) { 00577 KMMessagePart msgPart; 00578 KMMessage::bodyPart(dwPart, &msgPart); 00579 aMap.insert(msg->getMsgSerNum(), msgPart.bodyToUnicode( TQTextCodec::codecForName( "utf8" ) )); 00580 } else { 00581 // Check if the whole message has the right types. This is what 00582 // happens in the case of ical storage, where the whole mail is 00583 // the data 00584 const TQCString type( msg->typeStr() ); 00585 const TQCString subtype( msg->subtypeStr() ); 00586 if (type.lower() == sType && subtype.lower() == sSubtype ) { 00587 aMap.insert( msg->getMsgSerNum(), msg->bodyToUnicode() ); 00588 } 00589 // This is *not* an error: it may be that not all of the messages 00590 // have a message part that is matching the wanted MIME type 00591 } 00592 } 00593 #if 0 00594 if( unget ) f->unGetMsg(i); 00595 #else 00596 delete msg; 00597 #endif 00598 } else { 00599 kdDebug(5006) << k_funcinfo << " Unable to retrieve message " << i << " for incidence!" << endl; 00600 } 00601 } 00602 f->close( "incidences" ); 00603 return aMap; 00604 } 00605 00606 00607 /* Called when a message that was downloaded from an online imap folder 00608 * arrives. Needed when listing incidences on online account folders. */ 00609 // TODO: Till, port me 00610 void KMailICalIfaceImpl::slotMessageRetrieved( KMMessage* msg ) 00611 { 00612 if( !msg ) return; 00613 00614 KMFolder *parent = msg->parent(); 00615 Q_ASSERT( parent ); 00616 TQ_UINT32 sernum = msg->getMsgSerNum(); 00617 00618 // do we have an accumulator for this folder? 00619 Accumulator *ac = mAccumulators.find( parent->location() ); 00620 if( ac ) { 00621 TQString s; 00622 if ( !vPartFoundAndDecoded( msg, s ) ) return; 00623 TQString uid( "UID" ); 00624 vPartMicroParser( s, uid ); 00625 const TQ_UINT32 sernum = msg->getMsgSerNum(); 00626 mUIDToSerNum.insert( uid, sernum ); 00627 ac->add( s ); 00628 if( ac->isFull() ) { 00629 /* if this was the last one we were waiting for, tell the resource 00630 * about the new incidences and clean up. */ 00631 //asyncLoadResult( ac->incidences, ac->type, ac->folder ); 00632 mAccumulators.remove( ac->folder ); // autodelete 00633 } 00634 } else { 00635 /* We are not accumulating for this folder, so this one was added 00636 * by KMail. Do your thang. */ 00637 slotIncidenceAdded( msg->parent(), msg->getMsgSerNum() ); 00638 } 00639 00640 if ( mTheUnGetMes.contains( sernum ) ) { 00641 mTheUnGetMes.remove( sernum ); 00642 int i = 0; 00643 KMFolder* folder = 0; 00644 KMMsgDict::instance()->getLocation( sernum, &folder, &i ); 00645 folder->unGetMsg( i ); 00646 } 00647 } 00648 00649 static int dimapAccountCount() 00650 { 00651 KMail::AccountManager *mgr = kmkernel->acctMgr(); 00652 KMAccount *account = mgr->first(); 00653 int count = 0; 00654 while ( account ) { 00655 if ( dynamic_cast<KMAcctCachedImap*>( account ) ) 00656 ++count; 00657 account = mgr->next(); 00658 } 00659 return count; 00660 } 00661 00662 int KMailICalIfaceImpl::dimapAccounts() 00663 { 00664 return dimapAccountCount(); 00665 } 00666 00667 static TQString subresourceLabelForPresentation( const KMFolder * folder ) 00668 { 00669 if( KMailICalIfaceImpl::getResourceMap()->contains( folder->location() ) ) { 00670 return folder->label(); 00671 } 00672 00673 TQString label = folder->prettyURL(); 00674 TQStringList parts = TQStringList::split( TQString::fromLatin1("/"), label ); 00675 00676 // In the common special case of some other user's folder shared with us 00677 // the url looks like "Server Name/user/$USERNAME/Folder/Name". Make 00678 // those a bit nicer. 00679 if ( parts[1] == TQString::fromLatin1("user") ) { 00680 TQStringList remainder(parts); 00681 remainder.pop_front(); 00682 remainder.pop_front(); 00683 remainder.pop_front(); 00684 label = i18n("%1's %2") 00685 .arg( parts[2] ) 00686 .arg( remainder.join( TQString::fromLatin1("/") ) ); 00687 } 00688 // Another special case is our own folders, under the imap INBOX, make 00689 // those prettier too 00690 const KMFolder *parent = folder; 00691 while ( parent->parent() && parent->parent()->owner() ) { 00692 parent = parent->parent()->owner(); 00693 if ( parent->isSystemFolder() ) { 00694 TQStringList remainder(parts); 00695 remainder.pop_front(); 00696 remainder.pop_front(); 00697 if ( dimapAccountCount() > 1 ) { 00698 // Fix kolab issue 2531 folder->storage() )->account() can be null 00699 if( folder->storage() && static_cast<const KMFolderCachedImap*>( folder->storage() )->account() ) { 00700 label = i18n( "My %1 (%2)") 00701 .arg( remainder.join( TQString::fromLatin1("/") ), 00702 static_cast<const KMFolderCachedImap*>( folder->storage() )->account()->name() ); 00703 } else { 00704 label = i18n("My %1") 00705 .arg( remainder.join( TQString::fromLatin1("/") ) ); 00706 } 00707 } else { 00708 label = i18n("My %1") 00709 .arg( remainder.join( TQString::fromLatin1("/") ) ); 00710 } 00711 break; 00712 } 00713 } 00714 return label; 00715 } 00716 00717 /* list all available subresources */ 00718 TQValueList<KMailICalIfaceImpl::SubResource> KMailICalIfaceImpl::subresourcesKolab( const TQString& contentsType ) 00719 { 00720 TQValueList<SubResource> subResources; 00721 00722 // Add the default one 00723 KMFolder* f = folderFromType( contentsType, TQString() ); 00724 if ( f ) { 00725 subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ), 00726 f->isWritable(), folderIsAlarmRelevant( f ) ) ); 00727 kdDebug(5006) << "Adding(1) folder " << f->location() << " " << 00728 ( !f->isWritable() ? "readonly" : "" ) << endl; 00729 } 00730 00731 // get the extra ones 00732 const KMail::FolderContentsType t = folderContentsType( contentsType ); 00733 TQDictIterator<ExtraFolder> it( mExtraFolders ); 00734 for ( ; it.current(); ++it ){ 00735 f = it.current()->folder; 00736 if ( f && f->storage()->contentsType() == t ) { 00737 subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ), 00738 f->isWritable(), folderIsAlarmRelevant( f ) ) ); 00739 kdDebug(5006) << "Adding(2) folder " << f->location() << " " << 00740 ( !f->isWritable() ? "readonly" : "" ) << endl; 00741 } 00742 } 00743 00744 if ( subResources.isEmpty() ) 00745 kdDebug(5006) << "subresourcesKolab: No folder found for " << contentsType << endl; 00746 return subResources; 00747 } 00748 00749 bool KMailICalIfaceImpl::triggerSync( const TQString& contentsType ) 00750 { 00751 kdDebug(5006) << k_funcinfo << endl; 00752 TQValueList<KMailICalIfaceImpl::SubResource> folderList = subresourcesKolab( contentsType ); 00753 for ( TQValueList<KMailICalIfaceImpl::SubResource>::const_iterator it( folderList.begin() ), 00754 end( folderList.end() ); 00755 it != end ; ++it ) { 00756 KMFolder * const f = findResourceFolder( (*it).location ); 00757 if ( !f ) continue; 00758 if ( f->folderType() == KMFolderTypeImap || f->folderType() == KMFolderTypeCachedImap ) { 00759 if ( !kmkernel->askToGoOnline() ) { 00760 return false; 00761 } 00762 } 00763 00764 if ( f->folderType() == KMFolderTypeImap ) { 00765 KMFolderImap *imap = static_cast<KMFolderImap*>( f->storage() ); 00766 imap->getAndCheckFolder(); 00767 } else if ( f->folderType() == KMFolderTypeCachedImap ) { 00768 KMFolderCachedImap* cached = static_cast<KMFolderCachedImap*>( f->storage() ); 00769 if ( cached->account() ) { 00770 cached->account()->processNewMailInFolder( f ); 00771 } 00772 } 00773 } 00774 return true; 00775 } 00776 00777 /* Used by the resource to query whether folders are writable. */ 00778 bool KMailICalIfaceImpl::isWritableFolder( const TQString& type, 00779 const TQString& resource ) 00780 { 00781 KMFolder* f = folderFromType( type, resource ); 00782 if ( !f ) 00783 // Definitely not writable 00784 return false; 00785 00786 return f->isWritable(); 00787 } 00788 00789 /* Used by the resource to query the storage format of the folder. */ 00790 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( const TQString& resource ) 00791 { 00792 StorageFormat format; 00793 KMFolder* f = findResourceFolder( resource ); 00794 if ( f ) 00795 format = storageFormat( f ); 00796 else 00797 format = globalStorageFormat(); 00798 return format; 00799 } 00800 00815 TQ_UINT32 KMailICalIfaceImpl::update( const TQString& resource, 00816 TQ_UINT32 sernum, 00817 const TQString& subject, 00818 const TQString& plainTextBody, 00819 const TQMap<TQCString, TQString>& customHeaders, 00820 const TQStringList& attachmentURLs, 00821 const TQStringList& attachmentMimetypes, 00822 const TQStringList& attachmentNames, 00823 const TQStringList& deletedAttachments ) 00824 { 00825 TQ_UINT32 rc = 0; 00826 00827 if( !mUseResourceIMAP ) 00828 return rc; 00829 00830 Q_ASSERT( !resource.isEmpty() ); 00831 00832 kdDebug(5006) << "KMailICalIfaceImpl::update( " << resource << ", " << sernum << " )\n"; 00833 kdDebug(5006) << attachmentURLs << "\n"; 00834 kdDebug(5006) << attachmentMimetypes << "\n"; 00835 kdDebug(5006) << attachmentNames << "\n"; 00836 kdDebug(5006) << "deleted attachments:" << deletedAttachments << "\n"; 00837 00838 // Find the folder 00839 KMFolder* f = findResourceFolder( resource ); 00840 if( !f ) { 00841 kdError(5006) << "update(" << resource << ") : Not an IMAP resource folder" << endl; 00842 return rc; 00843 } 00844 00845 f->open( "ifaceupdate" ); 00846 00847 KMMessage* msg = 0; 00848 if ( sernum != 0 ) { 00849 msg = findMessageBySerNum( sernum, f ); 00850 if ( !msg ) return 0; 00851 // Message found - make a copy and update it: 00852 KMMessage* newMsg = new KMMessage( *msg ); 00853 newMsg->setSubject( subject ); 00854 TQMap<TQCString, TQString>::ConstIterator ith = customHeaders.begin(); 00855 const TQMap<TQCString, TQString>::ConstIterator ithEnd = customHeaders.begin(); 00856 for ( ; ith != ithEnd ; ++ith ) 00857 newMsg->setHeaderField( ith.key(), ith.data() ); 00858 newMsg->setParent( 0 ); // workaround strange line in KMMsgBase::assign. newMsg is not in any folder yet. 00859 // Note that plainTextBody isn't used in this branch. We assume it's still valid from when the mail was created. 00860 00861 // Delete some attachments according to list 00862 for( TQStringList::ConstIterator it = deletedAttachments.begin(); 00863 it != deletedAttachments.end(); 00864 ++it ){ 00865 if( !deleteAttachment( *newMsg, *it ) ){ 00866 // Note: It is _not_ an error if an attachment was already deleted. 00867 } 00868 } 00869 00870 const KMail::FolderContentsType t = f->storage()->contentsType(); 00871 const TQCString type = msg->typeStr(); 00872 const TQCString subtype = msg->subtypeStr(); 00873 const bool messageWasIcalVcardFormat = ( type.lower() == "text" && 00874 ( subtype.lower() == "calendar" || subtype.lower() == "x-vcard" ) ); 00875 00876 if ( storageFormat( f ) == StorageIcalVcard ) { 00877 //kdDebug(5006) << k_funcinfo << " StorageFormatIcalVcard " << endl; 00878 if ( !messageWasIcalVcardFormat ) { 00879 setIcalVcardContentTypeHeader( newMsg, t, f ); 00880 } 00881 newMsg->setBodyEncoded( plainTextBody.utf8() ); 00882 } else if ( storageFormat( f ) == StorageXML ) { 00883 if ( messageWasIcalVcardFormat ) { 00884 // this was originally an ical event, but the folder changed to xml, 00885 // convert 00886 setXMLContentTypeHeader( newMsg, plainTextBody ); 00887 } 00888 //kdDebug(5006) << k_funcinfo << " StorageFormatXML " << endl; 00889 // Add all attachments by reading them from their temp. files 00890 TQStringList::ConstIterator iturl = attachmentURLs.begin(); 00891 TQStringList::ConstIterator itmime = attachmentMimetypes.begin(); 00892 TQStringList::ConstIterator itname = attachmentNames.begin(); 00893 for( ; 00894 iturl != attachmentURLs.end() 00895 && itmime != attachmentMimetypes.end() 00896 && itname != attachmentNames.end(); 00897 ++iturl, ++itname, ++itmime ){ 00898 bool byname = !(*itmime).startsWith( "application/x-vnd.kolab." ); 00899 if( !updateAttachment( *newMsg, *iturl, *itname, *itmime, byname ) ){ 00900 kdDebug(5006) << "Attachment error, can not update attachment " << *iturl << endl; 00901 break; 00902 } 00903 } 00904 } 00905 00906 //debugBodyParts( "in update, before cleanup", *newMsg ); 00907 00908 // This is necessary for the headers to be readable later on 00909 newMsg->cleanupHeader(); 00910 00911 //debugBodyParts( "in update, after cleanup", *newMsg ); 00912 00913 deleteMsg( msg ); 00914 if ( f->addMsg( newMsg ) == 0 ) { 00915 // Message stored 00916 rc = newMsg->getMsgSerNum(); 00917 kdDebug(5006) << "forget about " << sernum << ", it's " << rc << " now" << endl; 00918 } 00919 addFolderChange( f, Contents ); 00920 syncFolder( f ); 00921 } else { 00922 // Message not found - store it newly 00923 rc = addIncidenceKolab( *f, subject, plainTextBody, customHeaders, 00924 attachmentURLs, 00925 attachmentNames, 00926 attachmentMimetypes ); 00927 } 00928 00929 f->close("ifaceupdate"); 00930 return rc; 00931 } 00932 00933 KURL KMailICalIfaceImpl::getAttachment( const TQString& resource, 00934 TQ_UINT32 sernum, 00935 const TQString& filename ) 00936 { 00937 // This finds the attachment with the filename, saves it to a 00938 // temp file and returns a URL to it. It's up to the resource 00939 // to delete the tmp file later. 00940 if( !mUseResourceIMAP ) 00941 return KURL(); 00942 00943 kdDebug(5006) << "KMailICalIfaceImpl::getAttachment( " 00944 << resource << ", " << sernum << ", " << filename << " )\n"; 00945 00946 // Find the folder 00947 KMFolder* f = findResourceFolder( resource ); 00948 if( !f ) { 00949 kdError(5006) << "getAttachment(" << resource << ") : Not an IMAP resource folder" << endl; 00950 return KURL(); 00951 } 00952 if ( storageFormat( f ) != StorageXML ) { 00953 kdError(5006) << "getAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl; 00954 return KURL(); 00955 } 00956 00957 KURL url; 00958 00959 bool bOK = false; 00960 bool quiet = mResourceQuiet; 00961 mResourceQuiet = true; 00962 00963 KMMessage* msg = findMessageBySerNum( sernum, f ); 00964 if( msg ) { 00965 // Message found - look for the attachment: 00966 00967 DwBodyPart* part = findBodyPart( *msg, filename ); 00968 if ( part ) { 00969 // Save the contents of the attachment. 00970 KMMessagePart aPart; 00971 msg->bodyPart( part, &aPart ); 00972 TQByteArray rawData( aPart.bodyDecodedBinary() ); 00973 00974 KTempFile file; 00975 file.file()->writeBlock( rawData.data(), rawData.size() ); 00976 00977 url.setPath( file.name() ); 00978 00979 bOK = true; 00980 } 00981 00982 if( !bOK ){ 00983 kdDebug(5006) << "Attachment " << filename << " not found." << endl; 00984 } 00985 }else{ 00986 kdDebug(5006) << "Message not found." << endl; 00987 } 00988 00989 mResourceQuiet = quiet; 00990 return url; 00991 } 00992 00993 TQString KMailICalIfaceImpl::attachmentMimetype( const TQString & resource, 00994 TQ_UINT32 sernum, 00995 const TQString & filename ) 00996 { 00997 if( !mUseResourceIMAP ) 00998 return TQString(); 00999 KMFolder* f = findResourceFolder( resource ); 01000 if( !f || storageFormat( f ) != StorageXML ) { 01001 kdError(5006) << "attachmentMimetype(" << resource << ") : Wrong folder" << endl; 01002 return TQString(); 01003 } 01004 01005 KMMessage* msg = findMessageBySerNum( sernum, f ); 01006 if( msg ) { 01007 // Message found - look for the attachment: 01008 DwBodyPart* part = findBodyPart( *msg, filename ); 01009 if ( part ) { 01010 KMMessagePart kmPart; 01011 msg->bodyPart( part, &kmPart ); 01012 return TQString( kmPart.typeStr() ) + "/" + TQString( kmPart.subtypeStr() ); 01013 } else { 01014 kdDebug(5006) << "Attachment " << filename << " not found." << endl; 01015 } 01016 } else { 01017 kdDebug(5006) << "Message not found." << endl; 01018 } 01019 01020 return TQString(); 01021 } 01022 01023 TQStringList KMailICalIfaceImpl::listAttachments(const TQString & resource, TQ_UINT32 sernum) 01024 { 01025 TQStringList rv; 01026 if( !mUseResourceIMAP ) 01027 return rv; 01028 01029 // Find the folder 01030 KMFolder* f = findResourceFolder( resource ); 01031 if( !f ) { 01032 kdError(5006) << "listAttachments(" << resource << ") : Not an IMAP resource folder" << endl; 01033 return rv; 01034 } 01035 if ( storageFormat( f ) != StorageXML ) { 01036 kdError(5006) << "listAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl; 01037 return rv; 01038 } 01039 01040 KMMessage* msg = findMessageBySerNum( sernum, f ); 01041 if( msg ) { 01042 for ( DwBodyPart* part = msg->getFirstDwBodyPart(); part; part = part->Next() ) { 01043 if ( part->hasHeaders() ) { 01044 TQString name; 01045 DwMediaType& contentType = part->Headers().ContentType(); 01046 if ( TQString( contentType.SubtypeStr().c_str() ).startsWith( "x-vnd.kolab." ) 01047 || TQString( contentType.SubtypeStr().c_str() ).contains( "tnef" ) ) 01048 continue; 01049 if ( !part->Headers().ContentDisposition().Filename().empty() ) 01050 name = part->Headers().ContentDisposition().Filename().c_str(); 01051 else if ( !contentType.Name().empty() ) 01052 name = contentType.Name().c_str(); 01053 if ( !name.isEmpty() ) 01054 rv.append( name ); 01055 } 01056 } 01057 } else { 01058 kdDebug(5006) << "Message not found." << endl; 01059 } 01060 01061 return rv; 01062 } 01063 01064 01065 // ============================================================================ 01066 01067 /* KMail part of the interface. These slots are connected to the resource 01068 * folders and inform us of folders or incidences in them changing, being 01069 * added or going away. */ 01070 01071 void KMailICalIfaceImpl::slotFolderRemoved( KMFolder* folder ) 01072 { 01073 // pretend the folder just changed back to the mail type, which 01074 // does the right thing, namely remove resource 01075 folderContentsTypeChanged( folder, KMail::ContentsTypeMail ); 01076 KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); 01077 configGroup.deleteEntry( folder->idString() + "-storageFormat" ); 01078 configGroup.deleteEntry( folder->idString() + "-changes" ); 01079 } 01080 01081 // KMail added a file to one of the groupware folders 01082 void KMailICalIfaceImpl::slotIncidenceAdded( KMFolder* folder, 01083 TQ_UINT32 sernum ) 01084 { 01085 if( mResourceQuiet || !mUseResourceIMAP ) 01086 return; 01087 01088 // kdDebug(5006) << "KMailICalIfaceImpl::slotIncidenceAdded" << endl; 01089 TQString type = folderContentsType( folder->storage()->contentsType() ); 01090 if( type.isEmpty() ) { 01091 kdError(5006) << "Not an IMAP resource folder" << endl; 01092 return; 01093 } 01094 // Get the index of the mail 01095 int i = 0; 01096 KMFolder* aFolder = 0; 01097 KMMsgDict::instance()->getLocation( sernum, &aFolder, &i ); 01098 assert( folder == aFolder ); 01099 01100 bool unget = !folder->isMessage( i ); 01101 TQString s; 01102 TQString uid( "UID" ); 01103 KMMessage *msg = folder->getMsg( i ); 01104 if( !msg ) return; 01105 if( msg->isComplete() ) { 01106 01107 bool ok = false; 01108 StorageFormat format = storageFormat( folder ); 01109 switch( format ) { 01110 case StorageIcalVcard: 01111 // Read the iCal or vCard 01112 ok = vPartFoundAndDecoded( msg, s ); 01113 if ( ok ) 01114 vPartMicroParser( s, uid ); 01115 break; 01116 case StorageXML: 01117 // Read the XML from the attachment with the given mimetype 01118 if ( kolabXMLFoundAndDecoded( *msg, 01119 folderKolabMimeType( folder->storage()->contentsType() ), s ) ) { 01120 uid = msg->subject(); 01121 ok = true; 01122 } 01123 break; 01124 } 01125 if ( !ok ) { 01126 if ( unget ) 01127 folder->unGetMsg( i ); 01128 return; 01129 } 01130 const TQ_UINT32 sernum = msg->getMsgSerNum(); 01131 mUIDToSerNum.insert( uid, sernum ); 01132 01133 // tell the resource if we didn't trigger this ourselves 01134 if ( mInTransit.contains( uid ) ) { 01135 mInTransit.remove( uid ); 01136 } 01137 incidenceAdded( type, folder->location(), sernum, format, s ); 01138 } else { 01139 // go get the rest of it, then try again 01140 // TODO: Till, port me 01141 if ( unget ) mTheUnGetMes.insert( msg->getMsgSerNum(), true ); 01142 FolderJob *job = msg->parent()->createJob( msg ); 01143 connect( job, TQT_SIGNAL( messageRetrieved( KMMessage* ) ), 01144 this, TQT_SLOT( slotMessageRetrieved( KMMessage* ) ) ); 01145 job->start(); 01146 return; 01147 } 01148 if( unget ) folder->unGetMsg(i); 01149 } 01150 01151 // KMail deleted a file 01152 void KMailICalIfaceImpl::slotIncidenceDeleted( KMFolder* folder, 01153 TQ_UINT32 sernum ) 01154 { 01155 if( mResourceQuiet || !mUseResourceIMAP ) 01156 return; 01157 01158 TQString type = folderContentsType( folder->storage()->contentsType() ); 01159 //kdDebug(5006) << folder << " " << type << " " << sernum << endl; 01160 if( !type.isEmpty() ) { 01161 // Get the index of the mail 01162 int i = 0; 01163 KMFolder* aFolder = 0; 01164 KMMsgDict::instance()->getLocation( sernum, &aFolder, &i ); 01165 assert( folder == aFolder ); 01166 01167 // Read the iCal or vCard 01168 bool unget = !folder->isMessage( i ); 01169 TQString s; 01170 bool ok = false; 01171 KMMessage* msg = folder->getMsg( i ); 01172 TQString uid( "UID" ); 01173 switch( storageFormat( folder ) ) { 01174 case StorageIcalVcard: 01175 if( vPartFoundAndDecoded( msg, s ) ) { 01176 vPartMicroParser( s, uid ); 01177 ok = true; 01178 } 01179 break; 01180 case StorageXML: 01181 if ( kolabXMLFoundAndDecoded( *msg, folderKolabMimeType( folder->storage()->contentsType() ), s ) ) { 01182 uid = msg->subject(); 01183 ok = true; 01184 } 01185 break; 01186 } 01187 if ( ok ) { 01188 kdDebug(5006) << "Emitting DCOP signal incidenceDeleted( " 01189 << type << ", " << folder->location() << ", " << uid 01190 << " )" << endl; 01191 incidenceDeleted( type, folder->location(), uid ); 01192 } 01193 if( unget ) folder->unGetMsg(i); 01194 } else 01195 kdError(5006) << "Not a groupware folder" << endl; 01196 } 01197 01198 // KMail orders a refresh 01199 void KMailICalIfaceImpl::slotRefresh( const TQString& type ) 01200 { 01201 if( mUseResourceIMAP ) { 01202 signalRefresh( type, TQString() /* PENDING(bo) folder->location() */ ); 01203 kdDebug(5006) << "Emitting DCOP signal signalRefresh( " << type << " )" << endl; 01204 } 01205 } 01206 01207 // This is among other things called when an expunge of a folder happens 01208 void KMailICalIfaceImpl::slotRefreshFolder( KMFolder* folder) 01209 { 01210 // TODO: The resources would of course be better off, if only this 01211 // folder would need refreshing. Currently it just orders a reload of 01212 // the type of the folder 01213 if( mUseResourceIMAP && folder ) { 01214 if( folder == mCalendar || folder == mContacts 01215 || folder == mNotes || folder == mTasks 01216 || folder == mJournals || mExtraFolders.find( folder->location() ) ) { 01217 // Refresh the folder of this type 01218 KMail::FolderContentsType ct = folder->storage()->contentsType(); 01219 slotRefresh( s_folderContentsType[ct].contentsTypeStr ); 01220 } 01221 } 01222 } 01223 01224 /**************************** 01225 * The folder and message stuff code 01226 */ 01227 01228 KMFolder* KMailICalIfaceImpl::folderFromType( const TQString& type, 01229 const TQString& folder ) 01230 { 01231 if( mUseResourceIMAP ) { 01232 KMFolder* f = 0; 01233 if ( !folder.isEmpty() ) { 01234 f = extraFolder( type, folder ); 01235 if ( f ) 01236 return f; 01237 } 01238 01239 if( type == "Calendar" ) f = mCalendar; 01240 else if( type == "Contact" ) f = mContacts; 01241 else if( type == "Note" ) f = mNotes; 01242 else if( type == "Task" || type == "Todo" ) f = mTasks; 01243 else if( type == "Journal" ) f = mJournals; 01244 01245 if ( f && ( folder.isEmpty() || folder == f->location() ) ) 01246 return f; 01247 01248 kdError(5006) << "No folder ( " << type << ", " << folder << " )\n"; 01249 } 01250 01251 return 0; 01252 } 01253 01254 01255 // Returns true if folder is a resource folder. If the resource isn't enabled 01256 // this always returns false 01257 bool KMailICalIfaceImpl::isResourceFolder( KMFolder* folder ) const 01258 { 01259 return mUseResourceIMAP && folder && 01260 ( isStandardResourceFolder( folder ) || mExtraFolders.find( folder->location() )!=0 ); 01261 } 01262 01263 bool KMailICalIfaceImpl::isStandardResourceFolder( KMFolder* folder ) const 01264 { 01265 return ( folder == mCalendar || folder == mTasks || folder == mJournals || 01266 folder == mNotes || folder == mContacts ); 01267 } 01268 01269 bool KMailICalIfaceImpl::hideResourceFolder( KMFolder* folder ) const 01270 { 01271 return mHideFolders && isResourceFolder( folder ); 01272 } 01273 01274 bool KMailICalIfaceImpl::hideResourceAccountRoot( KMFolder* folder ) const 01275 { 01276 KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() ); 01277 bool hide = dimapFolder && mHideFolders 01278 && (int)dimapFolder->account()->id() == GlobalSettings::self()->theIMAPResourceAccount() 01279 && GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount(); 01280 return hide; 01281 01282 } 01283 01284 KFolderTreeItem::Type KMailICalIfaceImpl::folderType( KMFolder* folder ) const 01285 { 01286 if( mUseResourceIMAP && folder ) { 01287 if( folder == mCalendar || folder == mContacts 01288 || folder == mNotes || folder == mTasks 01289 || folder == mJournals || mExtraFolders.find( folder->location() ) ) { 01290 KMail::FolderContentsType ct = folder->storage()->contentsType(); 01291 return s_folderContentsType[ct].treeItemType; 01292 } 01293 } 01294 01295 return KFolderTreeItem::Other; 01296 } 01297 01298 // Global tables of foldernames is different languages 01299 // For now: 0->English, 1->German, 2->French, 3->Dutch 01300 static TQMap<KFolderTreeItem::Type,TQString> folderNames[4]; 01301 TQString KMailICalIfaceImpl::folderName( KFolderTreeItem::Type type, int language ) const 01302 { 01303 // With the XML storage, folders are always (internally) named in English 01304 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ) 01305 language = 0; 01306 01307 static bool folderNamesSet = false; 01308 if( !folderNamesSet ) { 01309 folderNamesSet = true; 01310 /* NOTE: If you add something here, you also need to update 01311 GroupwarePage in configuredialog.cpp */ 01312 01313 // English 01314 folderNames[0][KFolderTreeItem::Calendar] = TQString::fromLatin1("Calendar"); 01315 folderNames[0][KFolderTreeItem::Tasks] = TQString::fromLatin1("Tasks"); 01316 folderNames[0][KFolderTreeItem::Journals] = TQString::fromLatin1("Journal"); 01317 folderNames[0][KFolderTreeItem::Contacts] = TQString::fromLatin1("Contacts"); 01318 folderNames[0][KFolderTreeItem::Notes] = TQString::fromLatin1("Notes"); 01319 01320 // German 01321 folderNames[1][KFolderTreeItem::Calendar] = TQString::fromLatin1("Kalender"); 01322 folderNames[1][KFolderTreeItem::Tasks] = TQString::fromLatin1("Aufgaben"); 01323 folderNames[1][KFolderTreeItem::Journals] = TQString::fromLatin1("Journal"); 01324 folderNames[1][KFolderTreeItem::Contacts] = TQString::fromLatin1("Kontakte"); 01325 folderNames[1][KFolderTreeItem::Notes] = TQString::fromLatin1("Notizen"); 01326 01327 // French 01328 folderNames[2][KFolderTreeItem::Calendar] = TQString::fromLatin1("Calendrier"); 01329 // Tasks = Tâches (â == 0xE2 in latin1) 01330 folderNames[2][KFolderTreeItem::Tasks] = TQString::fromLatin1("T\342ches"); 01331 folderNames[2][KFolderTreeItem::Journals] = TQString::fromLatin1("Journal"); 01332 folderNames[2][KFolderTreeItem::Contacts] = TQString::fromLatin1("Contacts"); 01333 folderNames[2][KFolderTreeItem::Notes] = TQString::fromLatin1("Notes"); 01334 01335 // Dutch 01336 folderNames[3][KFolderTreeItem::Calendar] = TQString::fromLatin1("Agenda"); 01337 folderNames[3][KFolderTreeItem::Tasks] = TQString::fromLatin1("Taken"); 01338 folderNames[3][KFolderTreeItem::Journals] = TQString::fromLatin1("Logboek"); 01339 folderNames[3][KFolderTreeItem::Contacts] = TQString::fromLatin1("Contactpersonen"); 01340 folderNames[3][KFolderTreeItem::Notes] = TQString::fromLatin1("Notities"); 01341 } 01342 01343 if( language < 0 || language > 3 ) { 01344 return folderNames[mFolderLanguage][type]; 01345 } 01346 else { 01347 return folderNames[language][type]; 01348 } 01349 } 01350 01351 01352 // Find message matching a given UID 01353 KMMessage *KMailICalIfaceImpl::findMessageByUID( const TQString& uid, KMFolder* folder ) 01354 { 01355 if( !folder || !mUIDToSerNum.contains( uid ) ) return 0; 01356 int i; 01357 KMFolder *aFolder; 01358 KMMsgDict::instance()->getLocation( mUIDToSerNum[uid], &aFolder, &i ); 01359 Q_ASSERT( aFolder == folder ); 01360 return folder->getMsg( i ); 01361 } 01362 01363 // Find message matching a given serial number 01364 KMMessage *KMailICalIfaceImpl::findMessageBySerNum( TQ_UINT32 serNum, KMFolder* folder ) 01365 { 01366 if( !folder ) return 0; 01367 01368 KMMessage *message = 0; 01369 KMFolder* aFolder = 0; 01370 int index; 01371 KMMsgDict::instance()->getLocation( serNum, &aFolder, &index ); 01372 01373 if( aFolder && aFolder != folder ) { 01374 kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) found it in folder " << aFolder->location() << ", expected " << folder->location() << endl; 01375 } else { 01376 if( aFolder ) 01377 message = aFolder->getMsg( index ); 01378 if (!message) 01379 kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) invalid serial number\n" << endl; 01380 } 01381 return message; 01382 } 01383 01384 void KMailICalIfaceImpl::deleteMsg( KMMessage *msg ) 01385 { 01386 if( !msg ) return; 01387 // Commands are now delayed; can't use that anymore, we need immediate deletion 01388 //( new KMDeleteMsgCommand( msg->parent(), msg ) )->start(); 01389 KMFolder *srcFolder = msg->parent(); 01390 int idx = srcFolder->find(msg); 01391 assert(idx != -1); 01392 // kill existing jobs since we are about to delete the message 01393 srcFolder->ignoreJobsForMessage( msg ); 01394 if ( !msg->transferInProgress() ) { 01395 srcFolder->removeMsg(idx); 01396 delete msg; 01397 } else { 01398 kdDebug(5006) << k_funcinfo << "Message cannot be deleted now because it is currently in use " << msg << endl; 01399 msg->deleteWhenUnused(); 01400 } 01401 addFolderChange( srcFolder, Contents ); 01402 } 01403 01404 void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder, 01405 KMail::FolderContentsType contentsType ) 01406 { 01407 if ( !mUseResourceIMAP ) 01408 return; 01409 // kdDebug(5006) << "folderContentsTypeChanged( " << folder->name() 01410 // << ", " << contentsType << ")\n"; 01411 01412 // The builtins can't change type 01413 if ( isStandardResourceFolder( folder ) ) 01414 return; 01415 01416 // Check if already know that 'extra folder' 01417 const TQString location = folder->location(); 01418 ExtraFolder* ef = mExtraFolders.find( location ); 01419 if ( ef && ef->folder ) { 01420 // Notify that the old folder resource is no longer available 01421 subresourceDeleted(folderContentsType( folder->storage()->contentsType() ), location ); 01422 01423 if ( contentsType == KMail::ContentsTypeMail ) { 01424 // Delete the old entry, stop listening and stop here 01425 mExtraFolders.remove( location ); 01426 folder->disconnect( this ); 01427 return; 01428 } 01429 // So the type changed to another groupware type, ok. 01430 } else { 01431 if ( ef && !ef->folder ) // deleted folder, clean up 01432 mExtraFolders.remove( location ); 01433 if ( contentsType == KMail::ContentsTypeMail ) 01434 return; 01435 01436 //kdDebug(5006) << "registering " << location << " as extra folder" << endl; 01437 // Make a new entry for the list 01438 ef = new ExtraFolder( folder ); 01439 mExtraFolders.insert( location, ef ); 01440 01441 FolderInfo info = readFolderInfo( folder ); 01442 mFolderInfoMap.insert( folder, info ); 01443 01444 // Adjust the folder names of all foo.default folders. 01445 // German users will get Kalender as the name of all default Calendar folders, 01446 // including their own, so that the default calendar folder of their Japanese 01447 // coworker appears as /user/hirohito/Kalender, although Hirohito sees his folder 01448 // in Japanese. On the server the folders are always in English. 01449 if ( folder->folderType() == KMFolderTypeCachedImap ) { 01450 TQString annotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType(); 01451 kdDebug(5006) << "folderContentsTypeChanged: " << folder->name() << " has annotation " << annotation << endl; 01452 if ( annotation == TQString( s_folderContentsType[contentsType].annotation ) + ".default" ) 01453 folder->setLabel( localizedDefaultFolderName( contentsType ) ); 01454 } 01455 01456 connectFolder( folder ); 01457 } 01458 // Tell about the new resource 01459 subresourceAdded( folderContentsType( contentsType ), location, subresourceLabelForPresentation(folder), 01460 folder->isWritable(), folderIsAlarmRelevant( folder ) ); 01461 } 01462 01463 KMFolder* KMailICalIfaceImpl::extraFolder( const TQString& type, 01464 const TQString& folder ) 01465 { 01466 // If an extra folder exists that matches the type and folder location, 01467 // use that 01468 int t = folderContentsType( type ); 01469 if ( t < 1 || t > 5 ) 01470 return 0; 01471 01472 ExtraFolder* ef = mExtraFolders.find( folder ); 01473 if ( ef && ef->folder && ef->folder->storage()->contentsType() == t ) 01474 return ef->folder; 01475 01476 return 0; 01477 } 01478 01479 KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( KMFolder* folder ) const 01480 { 01481 FolderInfoMap::ConstIterator it = mFolderInfoMap.find( folder ); 01482 if ( it != mFolderInfoMap.end() ) 01483 return (*it).mStorageFormat; 01484 return globalStorageFormat(); 01485 } 01486 01487 void KMailICalIfaceImpl::setStorageFormat( KMFolder* folder, StorageFormat format ) 01488 { 01489 FolderInfoMap::Iterator it = mFolderInfoMap.find( folder ); 01490 if ( it != mFolderInfoMap.end() ) { 01491 (*it).mStorageFormat = format; 01492 } else { 01493 FolderInfo info( format, NoChange ); 01494 mFolderInfoMap.insert( folder, info ); 01495 } 01496 KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); 01497 configGroup.writeEntry( folder->idString() + "-storageFormat", 01498 format == StorageXML ? "xml" : "icalvcard" ); 01499 } 01500 01501 void KMailICalIfaceImpl::addFolderChange( KMFolder* folder, FolderChanges changes ) 01502 { 01503 FolderInfoMap::Iterator it = mFolderInfoMap.find( folder ); 01504 if ( it != mFolderInfoMap.end() ) { 01505 (*it).mChanges = static_cast<FolderChanges>( (*it).mChanges | changes ); 01506 } else { // Otherwise, well, it's a folder we don't care about. 01507 kdDebug(5006) << "addFolderChange: nothing known about folder " << folder->location() << endl; 01508 } 01509 KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); 01510 configGroup.writeEntry( folder->idString() + "-changes", (*it).mChanges ); 01511 } 01512 01513 KMailICalIfaceImpl::FolderInfo KMailICalIfaceImpl::readFolderInfo( const KMFolder * const folder ) const 01514 { 01515 KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); 01516 TQString str = configGroup.readEntry( folder->idString() + "-storageFormat", "unset" ); 01517 FolderInfo info; 01518 if ( str == "unset" ) { 01519 info.mStorageFormat = globalStorageFormat(); 01520 configGroup.writeEntry( folder->idString() + "-storageFormat", 01521 info.mStorageFormat == StorageXML ? "xml" : "icalvcard" ); 01522 } else { 01523 info.mStorageFormat = ( str == "xml" ) ? StorageXML : StorageIcalVcard; 01524 } 01525 info.mChanges = (FolderChanges) configGroup.readNumEntry( folder->idString() + "-changes" ); 01526 return info; 01527 } 01528 01529 01530 void KMailICalIfaceImpl::folderSynced( KMFolder* folder, const KURL& folderURL ) 01531 { 01532 FolderInfoMap::Iterator it = mFolderInfoMap.find( folder ); 01533 if ( it != mFolderInfoMap.end() && (*it).mChanges ) { 01534 handleFolderSynced( folder, folderURL, (*it).mChanges ); 01535 (*it).mChanges = NoChange; 01536 } 01537 } 01538 01539 void KMailICalIfaceImpl::handleFolderSynced( KMFolder* folder, 01540 const KURL& folderURL, 01541 int _changes ) 01542 { 01543 // This is done here instead of in the resource, because 01544 // there could be 0, 1, or N kolab resources at this point. 01545 // We can hack the N case, but not the 0 case. 01546 // So the idea of a DCOP signal for this wouldn't work. 01547 if ( ( _changes & KMailICalIface::Contents ) || 01548 ( _changes & KMailICalIface::ACL ) ) { 01549 if ( storageFormat( folder ) == StorageXML && folder->storage()->contentsType() == KMail::ContentsTypeCalendar ) 01550 triggerKolabFreeBusy( folderURL ); 01551 } 01552 } 01553 01554 void KMailICalIfaceImpl::folderDeletedOnServer( const KURL& folderURL ) 01555 { 01556 triggerKolabFreeBusy( folderURL ); 01557 } 01558 01559 void KMailICalIfaceImpl::triggerKolabFreeBusy( const KURL& folderURL ) 01560 { 01561 /* Steffen said: you must issue an authenticated HTTP GET request to 01562 https://kolabserver/freebusy/trigger/user@domain/Folder/NestedFolder.pfb 01563 (replace .pfb with .xpfb for extended fb lists). */ 01564 KURL httpURL( folderURL ); 01565 // Keep username ("user@domain"), pass, and host from the imap url 01566 httpURL.setProtocol( "https" ); 01567 httpURL.setPort( 0 ); // remove imap port 01568 01569 // IMAP path is either /INBOX/<path> or /user/someone/<path> 01570 TQString path = folderURL.path( -1 ); 01571 Q_ASSERT( path.startsWith( "/" ) ); 01572 int secondSlash = path.find( '/', 1 ); 01573 if ( secondSlash == -1 ) { 01574 kdWarning() << "KCal::ResourceKolab::fromKMailFolderSynced path is too short: " << path << endl; 01575 return; 01576 } 01577 if ( path.startsWith( "/INBOX/", false ) ) { 01578 // If INBOX, replace it with the username (which is user@domain) 01579 path = path.mid( secondSlash ); 01580 path.prepend( folderURL.user() ); 01581 } else { 01582 // If user, just remove it. So we keep the IMAP-returned username. 01583 // This assumes it's a known user on the same domain. 01584 path = path.mid( secondSlash ); 01585 } 01586 01587 httpURL.setPath( "/freebusy/trigger/" + path + ".pfb" ); 01588 httpURL.setQuery( TQString() ); 01589 // Ensure that we encode everything with UTF8 01590 httpURL = KURL( httpURL.url(0,106), 106 ); 01591 kdDebug() << "Triggering PFB update for " << folderURL << " : getting " << httpURL << endl; 01592 // "Fire and forget". No need for error handling, nor for explicit deletion. 01593 // Maybe we should try to prevent launching it if it's already running (for this URL) though. 01594 /*KIO::Job* job =*/ KIO::get( httpURL, false, false /*no progress info*/ ); 01595 } 01596 01597 void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder ) 01598 { 01599 if ( isResourceFolder( folder ) ) { 01600 const TQString location = folder->location(); 01601 const TQString contentsTypeStr = folderContentsType( folder->storage()->contentsType() ); 01602 subresourceDeleted( contentsTypeStr, location ); 01603 01604 subresourceAdded( contentsTypeStr, location, subresourceLabelForPresentation( folder ), 01605 folder->isWritable(), folderIsAlarmRelevant( folder ) ); 01606 } 01607 } 01608 01609 // Must only be connected to a signal from KMFolder! 01610 void KMailICalIfaceImpl::slotFolderRenamed() 01611 { 01612 const KMFolder* folder = static_cast<const KMFolder *>( sender() ); 01613 slotFolderPropertiesChanged( const_cast<KMFolder*>( folder ) ); 01614 } 01615 01616 void KMailICalIfaceImpl::slotFolderLocationChanged( const TQString &oldLocation, 01617 const TQString &newLocation ) 01618 { 01619 KMFolder *folder = findResourceFolder( oldLocation ); 01620 ExtraFolder* ef = mExtraFolders.find( oldLocation ); 01621 if ( ef ) { 01622 // reuse the ExtraFolder entry, but adjust the key 01623 mExtraFolders.setAutoDelete( false ); 01624 mExtraFolders.remove( oldLocation ); 01625 mExtraFolders.setAutoDelete( true ); 01626 mExtraFolders.insert( newLocation, ef ); 01627 } 01628 if ( folder ) 01629 subresourceDeleted( folderContentsType( folder->storage()->contentsType() ), oldLocation ); 01630 01631 } 01632 01633 KMFolder* KMailICalIfaceImpl::findResourceFolder( const TQString& resource ) 01634 { 01635 // Try the standard folders 01636 if( mCalendar && mCalendar->location() == resource ) 01637 return mCalendar; 01638 if ( mContacts && mContacts->location() == resource ) 01639 return mContacts; 01640 if ( mNotes && mNotes->location() == resource ) 01641 return mNotes; 01642 if ( mTasks && mTasks->location() == resource ) 01643 return mTasks; 01644 if ( mJournals && mJournals->location() == resource ) 01645 return mJournals; 01646 01647 // No luck. Try the extrafolders 01648 ExtraFolder* ef = mExtraFolders.find( resource ); 01649 if ( ef ) 01650 return ef->folder; 01651 01652 // No luck at all 01653 return 0; 01654 } 01655 01656 void KMailICalIfaceImpl::changeResourceUIName( const TQString &folderPath, const TQString &newName ) 01657 { 01658 kdDebug() << "Folder path " << folderPath << endl; 01659 KMFolder *f = findResourceFolder( folderPath ); 01660 if ( f ) { 01661 KMailICalIfaceImpl::getResourceMap()->insert( folderPath, newName ); 01662 kmkernel->folderMgr()->renameFolder( f, newName ); 01663 KConfigGroup configGroup( kmkernel->config(), "Resource UINames" ); 01664 configGroup.writeEntry( folderPath, newName ); 01665 } 01666 } 01667 01668 // Builds a folder list from the dimap and the local folder list. 01669 static void createFolderList( TQStringList &folderNames, TQValueList<TQGuardedPtr<KMFolder> > &folderList ) 01670 { 01671 TQStringList dimapFolderNames; 01672 TQStringList localFolderNames; 01673 TQValueList<TQGuardedPtr<KMFolder> > dimapFolderList; 01674 TQValueList<TQGuardedPtr<KMFolder> > localFolderList; 01675 kmkernel->dimapFolderMgr()->createFolderList( &dimapFolderNames, &dimapFolderList ); 01676 kmkernel->folderMgr()->createFolderList( &localFolderNames, &localFolderList ); 01677 folderNames += dimapFolderNames; 01678 folderNames += localFolderNames; 01679 folderList += dimapFolderList; 01680 folderList += localFolderList; 01681 } 01682 01683 /**************************** 01684 * The config stuff 01685 */ 01686 01687 void KMailICalIfaceImpl::readConfig() 01688 { 01689 bool enabled = GlobalSettings::self()->theIMAPResourceEnabled() && 01690 ( GlobalSettings::self()->theIMAPResourceAccount() != 0 ); 01691 01692 if( !enabled ) { 01693 if( mUseResourceIMAP == true ) { 01694 // Shutting down 01695 mUseResourceIMAP = false; 01696 cleanup(); 01697 reloadFolderTree(); 01698 } 01699 return; 01700 } 01701 mUseResourceIMAP = enabled; 01702 01703 // Read remaining options 01704 const bool hideFolders = GlobalSettings::self()->hideGroupwareFolders(); 01705 TQString parentName = GlobalSettings::self()->theIMAPResourceFolderParent(); 01706 01707 // Find the folder parent 01708 KMFolderDir* folderParentDir; 01709 KMFolderType folderType; 01710 KMFolder* folderParent = kmkernel->findFolderById( parentName ); 01711 if( folderParent == 0 ) { 01712 // Parent folder not found. It was probably deleted. The user will have to 01713 // configure things again. 01714 kdDebug(5006) << "Groupware folder " << parentName << " not found. Groupware functionality disabled" << endl; 01715 // Or maybe the inbox simply wasn't created on the first startup 01716 KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() ); 01717 Q_ASSERT( account ); 01718 if ( account ) { 01719 // just in case we were connected already 01720 disconnect( account, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), 01721 this, TQT_SLOT( slotCheckDone() ) ); 01722 connect( account, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), 01723 this, TQT_SLOT( slotCheckDone() ) ); 01724 } 01725 mUseResourceIMAP = false; 01726 // We can't really call cleanup(), if those folders were completely deleted. 01727 mCalendar = 0; 01728 mTasks = 0; 01729 mJournals = 0; 01730 mContacts = 0; 01731 mNotes = 0; 01732 return; 01733 } else { 01734 folderParentDir = folderParent->createChildFolder(); 01735 folderType = folderParent->folderType(); 01736 } 01737 01738 KMAcctCachedImap::GroupwareType groupwareType = dynamic_cast<KMFolderCachedImap *>( folderParent->storage() )->account()->groupwareType(); 01739 01740 if ( groupwareType == KMAcctCachedImap::GroupwareKolab ) { 01741 // Make sure the folder parent has the subdirs 01742 // Globally there are 3 cases: nothing found, some stuff found by type/name heuristics, or everything found OK 01743 bool noneFound = true; 01744 bool mustFix = false; // true when at least one was found by heuristics 01745 TQValueVector<StandardFolderSearchResult> results( KMail::ContentsTypeLast + 1 ); 01746 for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) { 01747 if ( i != KMail::ContentsTypeMail ) { 01748 results[i] = findStandardResourceFolder( folderParentDir, static_cast<KMail::FolderContentsType>(i) ); 01749 if ( results[i].found == StandardFolderSearchResult::FoundAndStandard ) 01750 noneFound = false; 01751 else if ( results[i].found == StandardFolderSearchResult::FoundByType || 01752 results[i].found == StandardFolderSearchResult::FoundByName ) { 01753 mustFix = true; 01754 noneFound = false; 01755 } else // NotFound 01756 mustFix = true; 01757 } 01758 } 01759 01760 // Check if something changed 01761 if( mUseResourceIMAP && !noneFound && !mustFix && mFolderParentDir == folderParentDir 01762 && mFolderType == folderType ) { 01763 // Nothing changed 01764 if ( hideFolders != mHideFolders ) { 01765 // Well, the folder hiding has changed 01766 mHideFolders = hideFolders; 01767 reloadFolderTree(); 01768 } 01769 return; 01770 } 01771 01772 if( noneFound || mustFix ) { 01773 TQString msg; 01774 TQString parentFolderName = folderParent != 0 ? folderParent->name() : folderParentDir->name(); 01775 if ( noneFound ) { 01776 // No subfolder was found, so ask if we can make them 01777 msg = i18n("KMail will now create the required groupware folders" 01778 " as subfolders of %1; if you do not want this, cancel" 01779 " and the IMAP resource will be disabled").arg(parentFolderName); 01780 } else { 01781 // Some subfolders were found, be more precise 01782 TQString operations = "<ul>"; 01783 for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) { 01784 if ( i != KMail::ContentsTypeMail ) { 01785 TQString typeName = localizedDefaultFolderName( static_cast<KMail::FolderContentsType>( i ) ); 01786 if ( results[i].found == StandardFolderSearchResult::NotFound ) 01787 operations += "<li>" + i18n( "%1: no folder found. It will be created." ).arg( typeName ) + "</li>"; 01788 else if ( results[i].found == StandardFolderSearchResult::FoundByType || results[i].found == StandardFolderSearchResult::FoundByName ) 01789 operations += "<li>" + i18n( "%1: found folder %2. It will be set as the main groupware folder." ). 01790 arg( typeName ).arg( results[i].folder->label() ) + "</li>"; 01791 } 01792 } 01793 operations += "</ul>"; 01794 01795 msg = i18n("<qt>KMail found the following groupware folders in %1 and needs to perform the following operations: %2" 01796 "<br>If you do not want this, cancel" 01797 " and the IMAP resource will be disabled").arg(parentFolderName, operations); 01798 01799 } 01800 01801 if( KMessageBox::questionYesNo( 0, msg, 01802 i18n("Standard Groupware Folders"), KStdGuiItem::cont(), KStdGuiItem::cancel() ) == KMessageBox::No ) { 01803 01804 GlobalSettings::self()->setTheIMAPResourceEnabled( false ); 01805 mUseResourceIMAP = false; 01806 mFolderParentDir = 0; 01807 mFolderParent = 0; 01808 reloadFolderTree(); 01809 return; 01810 } 01811 } 01812 01813 // Make the new settings work 01814 mUseResourceIMAP = true; 01815 mFolderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage(); 01816 if( mFolderLanguage > 3 ) mFolderLanguage = 0; 01817 mFolderParentDir = folderParentDir; 01818 mFolderParent = folderParent; 01819 mFolderType = folderType; 01820 mHideFolders = hideFolders; 01821 01822 // Close the previous folders 01823 cleanup(); 01824 01825 // Set the new folders 01826 mCalendar = initFolder( KMail::ContentsTypeCalendar ); 01827 mTasks = initFolder( KMail::ContentsTypeTask ); 01828 mJournals = initFolder( KMail::ContentsTypeJournal ); 01829 mContacts = initFolder( KMail::ContentsTypeContact ); 01830 mNotes = initFolder( KMail::ContentsTypeNote ); 01831 01832 // Store final annotation (with .default) so that we won't ask again on next startup 01833 if ( mCalendar->folderType() == KMFolderTypeCachedImap ) 01834 static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType(); 01835 if ( mTasks->folderType() == KMFolderTypeCachedImap ) 01836 static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType(); 01837 if ( mJournals->folderType() == KMFolderTypeCachedImap ) 01838 static_cast<KMFolderCachedImap *>( mJournals->storage() )->updateAnnotationFolderType(); 01839 if ( mContacts->folderType() == KMFolderTypeCachedImap ) 01840 static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType(); 01841 if ( mNotes->folderType() == KMFolderTypeCachedImap ) 01842 static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType(); 01843 01844 //kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl; 01845 //kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl; 01846 //kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl; 01847 01848 // Find all extra folders 01849 TQStringList folderNames; 01850 TQValueList<TQGuardedPtr<KMFolder> > folderList; 01851 createFolderList( folderNames, folderList ); 01852 for( TQValueList<TQGuardedPtr<KMFolder> >::iterator it = folderList.begin(); 01853 it != folderList.end(); ++it ) 01854 { 01855 FolderStorage *storage = (*it)->storage(); 01856 KMFolderCachedImap* dimapStorage = dynamic_cast<KMFolderCachedImap*>( storage ); 01857 if ( storage && storage->contentsType() != 0 ) { 01858 if ( dimapStorage ) 01859 dimapStorage->updateAnnotationFolderType(); 01860 folderContentsTypeChanged( *it, storage->contentsType() ); 01861 } 01862 } 01863 01864 // If we just created them, they might have been registered as extra folders temporarily. 01865 // -> undo that. 01866 mExtraFolders.remove( mCalendar->location() ); 01867 mExtraFolders.remove( mTasks->location() ); 01868 mExtraFolders.remove( mJournals->location() ); 01869 mExtraFolders.remove( mContacts->location() ); 01870 mExtraFolders.remove( mNotes->location() ); 01871 01872 subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true ); 01873 subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true ); 01874 subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(), mJournals->label(), true, false ); 01875 subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false ); 01876 subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false ); 01877 } else if ( groupwareType == KMAcctCachedImap::GroupwareScalix ) { 01878 // Make the new settings work 01879 mUseResourceIMAP = true; 01880 mFolderParentDir = folderParentDir; 01881 mFolderParent = folderParent; 01882 mFolderType = folderType; 01883 mHideFolders = false; 01884 01885 // Close the previous folders 01886 cleanup(); 01887 01888 // Set the new folders 01889 mCalendar = initScalixFolder( KMail::ContentsTypeCalendar ); 01890 mTasks = initScalixFolder( KMail::ContentsTypeTask ); 01891 mJournals = 0; 01892 mContacts = initScalixFolder( KMail::ContentsTypeContact ); 01893 mNotes = initScalixFolder( KMail::ContentsTypeNote ); 01894 01895 // Store final annotation (with .default) so that we won't ask again on next startup 01896 if ( mCalendar->folderType() == KMFolderTypeCachedImap ) 01897 static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType(); 01898 if ( mTasks->folderType() == KMFolderTypeCachedImap ) 01899 static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType(); 01900 if ( mContacts->folderType() == KMFolderTypeCachedImap ) 01901 static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType(); 01902 if ( mNotes->folderType() == KMFolderTypeCachedImap ) 01903 static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType(); 01904 01905 // BEGIN TILL TODO The below only uses the dimap folder manager, which 01906 // will fail for all other folder types. Adjust. 01907 01908 kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl; 01909 kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl; 01910 kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl; 01911 01912 // Find all extra folders 01913 TQStringList folderNames; 01914 TQValueList<TQGuardedPtr<KMFolder> > folderList; 01915 kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList); 01916 TQValueList<TQGuardedPtr<KMFolder> >::iterator it; 01917 for(it = folderList.begin(); it != folderList.end(); ++it) 01918 { 01919 FolderStorage *storage = (*it)->storage(); 01920 01921 if ( (*it)->folderType() == KMFolderTypeCachedImap ) { 01922 KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( storage ); 01923 01924 const TQString attributes = imapFolder->folderAttributes(); 01925 if ( attributes.contains( "X-FolderClass" ) ) { 01926 if ( !attributes.contains( "X-SpecialFolder" ) || (*it)->location().contains( "@" ) ) { 01927 const Scalix::FolderAttributeParser parser( attributes ); 01928 if ( !parser.folderClass().isEmpty() ) { 01929 FolderContentsType type = Scalix::Utils::scalixIdToContentsType( parser.folderClass() ); 01930 imapFolder->setContentsType( type ); 01931 folderContentsTypeChanged( *it, type ); 01932 } 01933 } 01934 } 01935 } 01936 } 01937 01938 // If we just created them, they might have been registered as extra folders temporarily. 01939 // -> undo that. 01940 mExtraFolders.remove( mCalendar->location() ); 01941 mExtraFolders.remove( mTasks->location() ); 01942 mExtraFolders.remove( mContacts->location() ); 01943 mExtraFolders.remove( mNotes->location() ); 01944 01945 // END TILL TODO 01946 01947 subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true ); 01948 subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true ); 01949 subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false ); 01950 subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false ); 01951 } 01952 01953 KConfig *config = kmkernel->config(); 01954 config->setGroup("Resource UINames"); 01955 *KMailICalIfaceImpl::mSubResourceUINamesMap = config->entryMap( "Resource UINames" ); 01956 01957 reloadFolderTree(); 01958 } 01959 01960 void KMailICalIfaceImpl::slotCheckDone() 01961 { 01962 TQString parentName = GlobalSettings::self()->theIMAPResourceFolderParent(); 01963 KMFolder* folderParent = kmkernel->findFolderById( parentName ); 01964 //kdDebug(5006) << k_funcinfo << " folderParent=" << folderParent << endl; 01965 if ( folderParent ) // cool it exists now 01966 { 01967 KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() ); 01968 if ( account ) 01969 disconnect( account, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), 01970 this, TQT_SLOT( slotCheckDone() ) ); 01971 readConfig(); 01972 } 01973 } 01974 01975 KMFolder* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType ) 01976 { 01977 // Figure out what type of folder this is supposed to be 01978 KMFolderType type = mFolderType; 01979 if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir; 01980 01981 KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType; 01982 //kdDebug(5006) << "KMailICalIfaceImpl::initFolder " << folderName( itemType ) << endl; 01983 01984 // Find the folder 01985 StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType ); 01986 01987 // deal with multiple default groupware folders 01988 if ( result.folders.count() > 1 && result.found == StandardFolderSearchResult::FoundAndStandard ) { 01989 TQStringList labels; 01990 for ( TQValueList<KMFolder*>::ConstIterator it = result.folders.begin(); it != result.folders.end(); ++it ) 01991 labels << (*it)->prettyURL(); 01992 const TQString selected = KInputDialog::getItem( i18n("Default folder"), 01993 i18n("There are multiple %1 default folders, please choose one:") 01994 .arg( localizedDefaultFolderName( contentsType ) ), labels ); 01995 if ( !selected.isEmpty() ) 01996 result.folder = result.folders[ labels.findIndex( selected ) ]; 01997 } 01998 01999 KMFolder* folder = result.folder; 02000 02001 if ( !folder ) { 02002 // The folder isn't there yet - create it 02003 folder = 02004 mFolderParentDir->createFolder( localizedDefaultFolderName( contentsType ), false, type ); 02005 if( mFolderType == KMFolderTypeImap ) { 02006 KMFolderImap* parentFolder = static_cast<KMFolderImap*>( mFolderParent->storage() ); 02007 parentFolder->createFolder( localizedDefaultFolderName( contentsType ) ); 02008 static_cast<KMFolderImap*>( folder->storage() )->setAccount( parentFolder->account() ); 02009 } 02010 // Groupware folder created, use the global setting for storage format 02011 setStorageFormat( folder, globalStorageFormat() ); 02012 } else { 02013 FolderInfo info = readFolderInfo( folder ); 02014 mFolderInfoMap.insert( folder, info ); 02015 //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location() << endl; 02016 } 02017 02018 if( folder->canAccess() != 0 ) { 02019 KMessageBox::sorry(0, i18n("You do not have read/write permission to your %1 folder.") 02020 .arg( folderName( itemType ) ) ); 02021 return 0; 02022 } 02023 folder->storage()->setContentsType( contentsType ); 02024 folder->setSystemFolder( true ); 02025 folder->storage()->writeConfig(); 02026 folder->open("ifacefolder"); 02027 connectFolder( folder ); 02028 return folder; 02029 } 02030 02031 KMFolder* KMailICalIfaceImpl::initScalixFolder( KMail::FolderContentsType contentsType ) 02032 { 02033 // Figure out what type of folder this is supposed to be 02034 KMFolderType type = mFolderType; 02035 if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir; 02036 02037 KMFolder* folder = 0; 02038 02039 // Find all extra folders 02040 TQStringList folderNames; 02041 TQValueList<TQGuardedPtr<KMFolder> > folderList; 02042 Q_ASSERT( kmkernel ); 02043 Q_ASSERT( kmkernel->dimapFolderMgr() ); 02044 kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList); 02045 TQValueList<TQGuardedPtr<KMFolder> >::iterator it = folderList.begin(); 02046 for(; it != folderList.end(); ++it) 02047 { 02048 FolderStorage *storage = (*it)->storage(); 02049 02050 if ( (*it)->folderType() == KMFolderTypeCachedImap ) { 02051 KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( storage ); 02052 02053 const TQString attributes = imapFolder->folderAttributes(); 02054 if ( attributes.contains( "X-SpecialFolder" ) ) { 02055 const Scalix::FolderAttributeParser parser( attributes ); 02056 if ( contentsType == Scalix::Utils::scalixIdToContentsType( parser.folderClass() ) ) { 02057 folder = *it; 02058 break; 02059 } 02060 } 02061 } 02062 } 02063 02064 if ( !folder ) { 02065 return 0; 02066 } else { 02067 FolderInfo info = readFolderInfo( folder ); 02068 mFolderInfoMap.insert( folder, info ); 02069 //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location() << endl; 02070 } 02071 02072 if( folder->canAccess() != 0 ) { 02073 KMessageBox::sorry(0, i18n("You do not have read/write permission to your folder.") ); 02074 return 0; 02075 } 02076 folder->storage()->setContentsType( contentsType ); 02077 folder->setSystemFolder( true ); 02078 folder->storage()->writeConfig(); 02079 folder->open( "scalixfolder" ); 02080 connectFolder( folder ); 02081 return folder; 02082 } 02083 02084 void KMailICalIfaceImpl::connectFolder( KMFolder* folder ) 02085 { 02086 // avoid multiple connections 02087 disconnect( folder, TQT_SIGNAL( msgAdded( KMFolder*, TQ_UINT32 ) ), 02088 this, TQT_SLOT( slotIncidenceAdded( KMFolder*, TQ_UINT32 ) ) ); 02089 disconnect( folder, TQT_SIGNAL( msgRemoved( KMFolder*, TQ_UINT32 ) ), 02090 this, TQT_SLOT( slotIncidenceDeleted( KMFolder*, TQ_UINT32 ) ) ); 02091 disconnect( folder, TQT_SIGNAL( expunged( KMFolder* ) ), 02092 this, TQT_SLOT( slotRefreshFolder( KMFolder* ) ) ); 02093 disconnect( folder->storage(), TQT_SIGNAL( readOnlyChanged( KMFolder* ) ), 02094 this, TQT_SLOT( slotFolderPropertiesChanged( KMFolder* ) ) ); 02095 disconnect( folder, TQT_SIGNAL( nameChanged() ), 02096 this, TQT_SLOT( slotFolderRenamed() ) ); 02097 disconnect( folder->storage(), TQT_SIGNAL( locationChanged( const TQString&, const TQString&) ), 02098 this, TQT_SLOT( slotFolderLocationChanged( const TQString&, const TQString&) ) ); 02099 02100 // Setup the signals to listen for changes 02101 connect( folder, TQT_SIGNAL( msgAdded( KMFolder*, TQ_UINT32 ) ), 02102 this, TQT_SLOT( slotIncidenceAdded( KMFolder*, TQ_UINT32 ) ) ); 02103 connect( folder, TQT_SIGNAL( msgRemoved( KMFolder*, TQ_UINT32 ) ), 02104 this, TQT_SLOT( slotIncidenceDeleted( KMFolder*, TQ_UINT32 ) ) ); 02105 connect( folder, TQT_SIGNAL( expunged( KMFolder* ) ), 02106 this, TQT_SLOT( slotRefreshFolder( KMFolder* ) ) ); 02107 connect( folder->storage(), TQT_SIGNAL( readOnlyChanged( KMFolder* ) ), 02108 this, TQT_SLOT( slotFolderPropertiesChanged( KMFolder* ) ) ); 02109 connect( folder, TQT_SIGNAL( nameChanged() ), 02110 this, TQT_SLOT( slotFolderRenamed() ) ); 02111 connect( folder->storage(), TQT_SIGNAL( locationChanged( const TQString&, const TQString&) ), 02112 this, TQT_SLOT( slotFolderLocationChanged( const TQString&, const TQString&) ) ); 02113 02114 } 02115 02116 static void cleanupFolder( KMFolder* folder, KMailICalIfaceImpl* _this ) 02117 { 02118 if( folder ) { 02119 folder->setSystemFolder( false ); 02120 folder->disconnect( _this ); 02121 folder->close("ifacefolder"); 02122 } 02123 } 02124 02125 void KMailICalIfaceImpl::cleanup() 02126 { 02127 cleanupFolder( mContacts, this ); 02128 cleanupFolder( mCalendar, this ); 02129 cleanupFolder( mNotes, this ); 02130 cleanupFolder( mTasks, this ); 02131 cleanupFolder( mJournals, this ); 02132 02133 mContacts = mCalendar = mNotes = mTasks = mJournals = 0; 02134 } 02135 02136 TQString KMailICalIfaceImpl::folderPixmap( KFolderTreeItem::Type type ) const 02137 { 02138 if( !mUseResourceIMAP ) 02139 return TQString(); 02140 02141 if( type == KFolderTreeItem::Contacts ) 02142 return TQString::fromLatin1( "kmgroupware_folder_contacts" ); 02143 else if( type == KFolderTreeItem::Calendar ) 02144 return TQString::fromLatin1( "kmgroupware_folder_calendar" ); 02145 else if( type == KFolderTreeItem::Notes ) 02146 return TQString::fromLatin1( "kmgroupware_folder_notes" ); 02147 else if( type == KFolderTreeItem::Tasks ) 02148 return TQString::fromLatin1( "kmgroupware_folder_tasks" ); 02149 else if( type == KFolderTreeItem::Journals ) 02150 return TQString::fromLatin1( "kmgroupware_folder_journals" ); 02151 02152 return TQString(); 02153 } 02154 02155 static void reloadFolderTree() 02156 { 02157 // Make the folder tree show the icons or not 02158 kmkernel->folderMgr()->contentsChanged(); 02159 } 02160 02161 // This is a very light-weight and fast 'parser' to retrieve 02162 // a data entry from a vCal taking continuation lines 02163 // into account 02164 static void vPartMicroParser( const TQString& str, TQString& s ) 02165 { 02166 TQString line; 02167 uint len = str.length(); 02168 02169 for( uint i=0; i<len; ++i){ 02170 if( str[i] == '\r' || str[i] == '\n' ){ 02171 if( str[i] == '\r' ) 02172 ++i; 02173 if( i+1 < len && str[i+1] == ' ' ){ 02174 // found a continuation line, skip it's leading blanc 02175 ++i; 02176 }else{ 02177 // found a logical line end, process the line 02178 if( line.startsWith( s ) ) { 02179 s = line.mid( s.length() + 1 ); 02180 return; 02181 } 02182 line = ""; 02183 } 02184 } else { 02185 line += str[i]; 02186 } 02187 } 02188 02189 // Not found. Clear it 02190 s.truncate(0); 02191 } 02192 02193 // Returns the first child folder having the given annotation 02194 static TQValueList<KMFolder*> findFolderByAnnotation( KMFolderDir* folderParentDir, const TQString& annotation ) 02195 { 02196 TQValueList<KMFolder*> rv; 02197 TQPtrListIterator<KMFolderNode> it( *folderParentDir ); 02198 for ( ; it.current(); ++it ) { 02199 if ( !it.current()->isDir() ) { 02200 KMFolder* folder = static_cast<KMFolder *>( it.current() ); 02201 if ( folder->folderType() == KMFolderTypeCachedImap ) { 02202 TQString folderAnnotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType(); 02203 //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl; 02204 if ( folderAnnotation == annotation ) 02205 rv.append( folder ); 02206 } 02207 } 02208 } 02209 return rv; 02210 } 02211 02212 KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType ) 02213 { 02214 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ) 02215 { 02216 // Look for a folder with an annotation like "event.default" 02217 TQValueList<KMFolder*> folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) + ".default" ); 02218 if ( !folders.isEmpty() ) 02219 return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundAndStandard ); 02220 02221 // Fallback: look for a folder with an annotation like "event" 02222 folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) ); 02223 if ( !folders.isEmpty() ) 02224 return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundByType ); 02225 02226 // Fallback: look for the folder by name (we'll need to change its type) 02227 KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) ); 02228 if ( node && !node->isDir() ) 02229 return StandardFolderSearchResult( static_cast<KMFolder *>( node ), StandardFolderSearchResult::FoundByName ); 02230 02231 kdDebug(5006) << "findStandardResourceFolder: found no resource folder for " << s_folderContentsType[contentsType].annotation << endl; 02232 return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound ); 02233 } 02234 else // icalvcard: look up standard resource folders by name 02235 { 02236 KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType; 02237 unsigned int folderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage(); 02238 if( folderLanguage > 3 ) folderLanguage = 0; 02239 KMFolderNode* node = folderParentDir->hasNamedFolder( folderName( itemType, folderLanguage ) ); 02240 if ( !node || node->isDir() ) 02241 return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound ); 02242 return StandardFolderSearchResult( static_cast<KMFolder*>( node ), StandardFolderSearchResult::FoundAndStandard ); 02243 } 02244 } 02245 02246 /* We treat all folders as relevant wrt alarms for which we have Administer 02247 * rights or for which the "Incidences relevant for everyone" annotation has 02248 * been set. It can be reasonably assumed that those are "ours". All local folders 02249 * must be ours anyhow. */ 02250 bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder *folder ) 02251 { 02252 bool administerRights = true; 02253 bool relevantForOwner = true; 02254 bool relevantForEveryone = false; 02255 if ( folder->folderType() == KMFolderTypeImap ) { 02256 const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() ); 02257 administerRights = 02258 imapFolder->userRightsState() != KMail::ACLJobs::Ok || 02259 imapFolder->userRights() & KMail::ACLJobs::Administer; 02260 } 02261 if ( folder->folderType() == KMFolderTypeCachedImap ) { 02262 const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() ); 02263 administerRights = 02264 dimapFolder->userRightsState() != KMail::ACLJobs::Ok || 02265 dimapFolder->userRights() & KMail::ACLJobs::Administer; 02266 relevantForOwner = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor () == KMFolderCachedImap::IncForAdmins ); 02267 relevantForEveryone = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor() == KMFolderCachedImap::IncForReaders ); 02268 } 02269 #if 0 02270 kdDebug(5006) << k_funcinfo << endl; 02271 kdDebug(5006) << "Folder: " << folder->label() << " has administer rights: " << administerRights << endl; 02272 kdDebug(5006) << "and is relevant for owner: " << relevantForOwner << endl; 02273 kdDebug(5006) << "and relevant for everyone: " << relevantForEveryone << endl; 02274 #endif 02275 return ( administerRights && relevantForOwner ) || relevantForEveryone; 02276 } 02277 02278 void KMailICalIfaceImpl::setResourceQuiet(bool q) 02279 { 02280 mResourceQuiet = q; 02281 } 02282 02283 bool KMailICalIfaceImpl::isResourceQuiet() const 02284 { 02285 return mResourceQuiet; 02286 } 02287 02288 02289 bool KMailICalIfaceImpl::addSubresource( const TQString& resource, 02290 const TQString& parent, 02291 const TQString& contentsType ) 02292 { 02293 kdDebug(5006) << "Adding subresource to parent: " << parent << " with name: " << resource << endl; 02294 kdDebug(5006) << "contents type: " << contentsType << endl; 02295 KMFolder *folder = findResourceFolder( parent ); 02296 KMFolderDir *parentFolderDir = !parent.isEmpty() && folder ? folder->createChildFolder(): mFolderParentDir; 02297 if ( !parentFolderDir || parentFolderDir->hasNamedFolder( resource ) ) return false; 02298 02299 TQString msg; 02300 if ( parentFolderDir->owner() && !parentFolderDir->owner()->isValidName( resource, msg ) ) { 02301 KMessageBox::error( 0, msg ); 02302 return false; 02303 } 02304 02305 KMFolderType type = mFolderType; 02306 if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir; 02307 02308 KMFolder* newFolder = parentFolderDir->createFolder( resource, false, type ); 02309 if ( !newFolder ) return false; 02310 if( mFolderType == KMFolderTypeImap ) 02311 static_cast<KMFolderImap*>( folder->storage() )->createFolder( resource ); 02312 02313 StorageFormat defaultFormat = GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard; 02314 setStorageFormat( newFolder, folder ? storageFormat( folder ) : defaultFormat ); 02315 newFolder->storage()->setContentsType( folderContentsType( contentsType ) ); 02316 newFolder->storage()->writeConfig(); 02317 newFolder->open( "ical_subresource" ); 02318 connectFolder( newFolder ); 02319 reloadFolderTree(); 02320 02321 return true; 02322 } 02323 02324 bool KMailICalIfaceImpl::removeSubresource( const TQString& location ) 02325 { 02326 kdDebug(5006) << k_funcinfo << endl; 02327 02328 KMFolder *folder = findResourceFolder( location ); 02329 02330 // We don't allow the default folders to be deleted, so check for 02331 // those first. It would be nicer to produce a more meaningful error, 02332 // or prevent deletion of the builtin folders from the gui already. 02333 if ( !folder || isStandardResourceFolder( folder ) ) 02334 return false; 02335 02336 // the folder will be removed, which implies closed, so make sure 02337 // nothing is using it anymore first 02338 subresourceDeleted( folderContentsType( folder->storage()->contentsType() ), location ); 02339 mExtraFolders.remove( location ); 02340 folder->disconnect( this ); 02341 02342 if ( folder->folderType() == KMFolderTypeImap ) 02343 kmkernel->imapFolderMgr()->remove( folder ); 02344 else if ( folder->folderType() == KMFolderTypeCachedImap ) { 02345 // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2) 02346 KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( folder->storage() ); 02347 KMAcctCachedImap* acct = storage->account(); 02348 if ( acct ) 02349 acct->addDeletedFolder( folder ); 02350 kmkernel->dimapFolderMgr()->remove( folder ); 02351 } 02352 return true; 02353 } 02354 02355 void KMailICalIfaceImpl::syncFolder(KMFolder * folder) const 02356 { 02357 if ( kmkernel->isOffline() || !GlobalSettings::immediatlySyncDIMAPOnGroupwareChanges() ) 02358 return; 02359 KMFolderCachedImap *dimapFolder = dynamic_cast<KMFolderCachedImap*>( folder->storage() ); 02360 if ( !dimapFolder ) 02361 return; 02362 // check if the folder exists already, otherwise sync its parent as well to create it 02363 if ( dimapFolder->imapPath().isEmpty() ) { 02364 if ( folder->parent() && folder->parent()->owner() ) 02365 syncFolder( folder->parent()->owner() ); 02366 else 02367 return; 02368 } 02369 dimapFolder->account()->processNewMailInFolder( folder ); 02370 } 02371 02372 #include "kmailicalifaceimpl.moc"