/* This file is part of KMail. Copyright (c) 2003 Steffen Hansen Copyright (c) 2003 - 2004 Bo Thorsen Copyright (c) 2004 Till Adam This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the TQt library by Trolltech AS, Norway (or with modified versions of TQt that use the same license as TQt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than TQt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include #endif #include "kmailicalifaceimpl.h" #include "kmfolder.h" #include "kmfoldertree.h" #include "kmfolderdir.h" #include "kmgroupware.h" #include "kmfoldermgr.h" #include "kmcommands.h" #include "kmfolderindex.h" #include "kmmsgdict.h" #include "kmmsgpart.h" using KMail::AccountManager; #include "kmfolderimap.h" #include "globalsettings.h" #include "accountmanager.h" #include "kmfoldercachedimap.h" #include "kmacctcachedimap.h" #include "acljobs.h" #include "scalix.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KMail; TQMap *KMailICalIfaceImpl::mSubResourceUINamesMap = new TQMap; // Local helper methods static void vPartMicroParser( const TQString& str, TQString& s ); static void reloadFolderTree(); // The index in this array is the KMail::FolderContentsType enum static const struct { const char* contentsTypeStr; // the string used in the DCOP interface const char* mimetype; KFolderTreeItem::Type treeItemType; const char* annotation; const char* translatedName; } s_folderContentsType[] = { { "Mail", "application/x-vnd.kolab.mail", KFolderTreeItem::Other, "mail", I18N_NOOP( "Mail" ) }, { "Calendar", "application/x-vnd.kolab.event", KFolderTreeItem::Calendar, "event", I18N_NOOP( "Calendar" ) }, { "Contact", "application/x-vnd.kolab.contact", KFolderTreeItem::Contacts, "contact", I18N_NOOP( "Contacts" ) }, { "Note", "application/x-vnd.kolab.note", KFolderTreeItem::Notes, "note", I18N_NOOP( "Notes" ) }, { "Task", "application/x-vnd.kolab.task", KFolderTreeItem::Tasks, "task", I18N_NOOP( "Tasks" ) }, { "Journal", "application/x-vnd.kolab.journal", KFolderTreeItem::Journals, "journal", I18N_NOOP( "Journal" ) } }; static TQString folderContentsType( KMail::FolderContentsType type ) { return s_folderContentsType[type].contentsTypeStr; } static TQString folderKolabMimeType( KMail::FolderContentsType type ) { return s_folderContentsType[type].mimetype; } KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::globalStorageFormat() const { return GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard; } static KMail::FolderContentsType folderContentsType( const TQString& type ) { for ( uint i = 0 ; i < sizeof s_folderContentsType / sizeof *s_folderContentsType; ++i ) if ( type == s_folderContentsType[i].contentsTypeStr ) return static_cast( i ); return KMail::ContentsTypeMail; } static TQString localizedDefaultFolderName( KMail::FolderContentsType type ) { return i18n( s_folderContentsType[type].translatedName ); } const char* KMailICalIfaceImpl::annotationForContentsType( KMail::FolderContentsType type ) { return s_folderContentsType[type].annotation; } ExtraFolder::ExtraFolder( KMFolder* f ) : folder( f ) { folder->open("kmailicaliface::extrafolder"); } ExtraFolder::~ExtraFolder() { if ( folder ) folder->close("kmailicaliface::extrafolder"); } /* This interface has three parts to it - libkcal interface; kmail interface; and helper functions. The libkcal interface and the kmail interface have the same three methods: add, delete and refresh. The only difference is that the libkcal interface is used from the IMAP resource in libkcal and the kmail interface is used from the groupware object in kmail. */ KMailICalIfaceImpl::KMailICalIfaceImpl() : DCOPObject( "KMailICalIface" ), TQObject( 0, "KMailICalIfaceImpl" ), mContacts( 0 ), mCalendar( 0 ), mNotes( 0 ), mTasks( 0 ), mJournals( 0 ), mFolderLanguage( 0 ), mFolderParentDir( 0 ), mFolderType( KMFolderTypeUnknown ), mUseResourceIMAP( false ), mResourceQuiet( false ), mHideFolders( true ) { // Listen to config changes connect( kmkernel, TQT_SIGNAL( configChanged() ), this, TQT_SLOT( readConfig() ) ); connect( kmkernel, TQT_SIGNAL( folderRemoved( KMFolder* ) ), this, TQT_SLOT( slotFolderRemoved( KMFolder* ) ) ); mExtraFolders.setAutoDelete( true ); mAccumulators.setAutoDelete( true ); } /* libkcal part of the interface, called from the resources using this * when incidences are added or deleted */ // Helper function to find an attachment of a given mimetype // Can't use KMMessage::findDwBodyPart since it only works with known mimetypes. static DwBodyPart* findBodyPartByMimeType( const KMMessage& msg, const char* sType, const char* sSubtype, bool startsWith = false ) { // quickly searching for our message part: since Kolab parts are // top-level parts we do *not* have to travel into embedded multiparts DwBodyPart* part = msg.getFirstDwBodyPart(); while( part ){ // kdDebug() << part->Headers().ContentType().TypeStr().c_str() << " " // << part->Headers().ContentType().SubtypeStr().c_str() << endl; if ( part->hasHeaders() ) { DwMediaType& contentType = part->Headers().ContentType(); if ( startsWith ) { if ( contentType.TypeStr() == sType && TQString( contentType.SubtypeStr().c_str() ).startsWith( sSubtype ) ) return part; } else if ( contentType.TypeStr() == sType && contentType.SubtypeStr() == sSubtype ) return part; } part = part->Next(); } return 0; } // Helper function to find an attachment with a given filename static DwBodyPart* findBodyPart( const KMMessage& msg, const TQString& attachmentName ) { // quickly searching for our message part: since Kolab parts are // top-level parts we do *not* have to travel into embedded multiparts for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) { //kdDebug(5006) << "findBodyPart: - " << part->Headers().ContentDisposition().Filename().c_str() << endl; if ( part->hasHeaders() && attachmentName == part->Headers().ContentDisposition().Filename().c_str() ) return part; if ( part->hasHeaders() && attachmentName == part->Headers().ContentType().Name().c_str() ) return part; } return 0; } #if 0 static void debugBodyParts( const char* foo, const KMMessage& msg ) { kdDebug(5006) << "--debugBodyParts " << foo << "--" << endl; for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) { if ( part->hasHeaders() ) { kdDebug(5006) << " bodypart: " << part << endl; kdDebug(5006) << " " << part->Headers().AsString().c_str() << endl; } else kdDebug(5006) << " part " << part << " has no headers" << endl; } } #else inline static void debugBodyParts( const char*, const KMMessage& ) {} #endif // Add (or overwrite, resp.) an attachment in an existing mail, // attachments must be local files, they are identified by their names. // If lookupByName if false the attachment to replace is looked up by mimetype. // return value: wrong if attachment could not be added/updated bool KMailICalIfaceImpl::updateAttachment( KMMessage& msg, const TQString& attachmentURL, const TQString& attachmentName, const TQString& attachmentMimetype, bool lookupByName ) { kdDebug(5006) << "KMailICalIfaceImpl::updateAttachment( " << attachmentURL << " )" << endl; bool bOK = false; KURL url( attachmentURL ); if ( url.isValid() && url.isLocalFile() ) { const TQString fileName( url.path() ); TQFile file( fileName ); if( file.open( IO_ReadOnly ) ) { TQByteArray rawData = file.readAll(); file.close(); // create the new message part with data read from temp file KMMessagePart msgPart; msgPart.setName( attachmentName ); const int iSlash = attachmentMimetype.find('/'); const TQCString sType = attachmentMimetype.left( iSlash ).latin1(); const TQCString sSubtype = attachmentMimetype.mid( iSlash+1 ).latin1(); msgPart.setTypeStr( sType ); msgPart.setSubtypeStr( sSubtype ); TQCString ctd("attachment;\n filename=\""); ctd.append( attachmentName.latin1() ); ctd.append("\""); msgPart.setContentDisposition( ctd ); TQValueList dummy; msgPart.setBodyAndGuessCte( rawData, dummy ); msgPart.setPartSpecifier( fileName ); DwBodyPart* newPart = msg.createDWBodyPart( &msgPart ); // This whole method is a bit special. We mix code for writing and code for reading. // E.g. we need to parse the content-disposition again for ContentDisposition().Filename() // to work later on. newPart->Headers().ContentDisposition().Parse(); DwBodyPart* part = lookupByName ? findBodyPart( msg, attachmentName ) : findBodyPartByMimeType( msg, sType, sSubtype ); if ( part ) { // Make sure the replacing body part is pointing // to the same next part as the original body part. newPart->SetNext( part->Next() ); // call DwBodyPart::operator = // which calls DwEntity::operator = *part = *newPart; delete newPart; msg.setNeedsAssembly(); kdDebug(5006) << "Attachment " << attachmentName << " updated." << endl; } else { msg.addDwBodyPart( newPart ); kdDebug(5006) << "Attachment " << attachmentName << " added." << endl; } bOK = true; }else{ kdDebug(5006) << "Attachment " << attachmentURL << " can not be read." << endl; } }else{ kdDebug(5006) << "Attachment " << attachmentURL << " not a local file." << endl; } return bOK; } // Look for the attachment with the right mimetype bool KMailICalIfaceImpl::kolabXMLFoundAndDecoded( const KMMessage& msg, const TQString& mimetype, TQString& s ) { const int iSlash = mimetype.find('/'); const TQCString sType = mimetype.left( iSlash ).latin1(); const TQCString sSubtype = mimetype.mid( iSlash+1 ).latin1(); DwBodyPart* part = findBodyPartByMimeType( msg, sType, sSubtype, true /* starts with sSubtype, to accept application/x-vnd.kolab.contact.distlist */ ); if ( part ) { KMMessagePart msgPart; KMMessage::bodyPart(part, &msgPart); s = msgPart.bodyToUnicode( TQTextCodec::codecForName( "utf8" ) ); return true; } return false; } // Delete an attachment in an existing mail. // return value: wrong if attachment could not be deleted // // This code could be optimized: for now we just replace // the attachment by an empty dummy attachment since Mimelib // does not provide an option for deleting attachments yet. bool KMailICalIfaceImpl::deleteAttachment( KMMessage& msg, const TQString& attachmentName ) { kdDebug(5006) << "KMailICalIfaceImpl::deleteAttachment( " << attachmentName << " )" << endl; bool bOK = false; // quickly searching for our message part: since Kolab parts are // top-level parts we do *not* have to travel into embedded multiparts DwBodyPart* part = findBodyPart( msg, attachmentName ); if ( part ) { msg.getTopLevelPart()->Body().RemoveBodyPart( part ); delete part; msg.setNeedsAssembly(); kdDebug(5006) << "Attachment deleted." << endl; bOK = true; } if( !bOK ){ kdDebug(5006) << "Attachment " << attachmentName << " not found." << endl; } return bOK; } static void setIcalVcardContentTypeHeader( KMMessage *msg, KMail::FolderContentsType t, KMFolder *folder ) { KMAcctCachedImap::GroupwareType groupwareType = KMAcctCachedImap::GroupwareKolab; KMFolderCachedImap *imapFolder = dynamic_cast( folder->storage() ); if ( imapFolder ) groupwareType = imapFolder->account()->groupwareType(); msg->setType( DwMime::kTypeText ); if ( t == KMail::ContentsTypeCalendar || t == KMail::ContentsTypeTask || t == KMail::ContentsTypeJournal ) { msg->setSubtype( DwMime::kSubtypeVCal ); if ( groupwareType == KMAcctCachedImap::GroupwareKolab ) msg->setHeaderField("Content-Type", "text/calendar; method=REQUEST; charset=\"utf-8\""); else if ( groupwareType == KMAcctCachedImap::GroupwareScalix ) msg->setHeaderField("Content-Type", "text/calendar; method=PUBLISH; charset=\"UTF-8\""); } else if ( t == KMail::ContentsTypeContact ) { msg->setSubtype( DwMime::kSubtypeXVCard ); if ( groupwareType == KMAcctCachedImap::GroupwareKolab ) msg->setHeaderField( "Content-Type", "Text/X-VCard; charset=\"utf-8\"" ); else if ( groupwareType == KMAcctCachedImap::GroupwareScalix ) msg->setHeaderField( "Content-Type", "application/scalix-properties; charset=\"UTF-8\"" ); } else { kdWarning(5006) << k_funcinfo << "Attempt to write non-groupware contents to folder" << endl; } } static void setXMLContentTypeHeader( KMMessage *msg, const TQString plainTextBody ) { // add a first body part to be displayed by all mailer // than can NOT display Kolab data: no matter if these // mailers are MIME compliant or not KMMessagePart firstPart; firstPart.setType( DwMime::kTypeText ); firstPart.setSubtype( DwMime::kSubtypePlain ); msg->removeHeaderField( "Content-Type" ); msg->setType( DwMime::kTypeMultipart ); msg->setSubtype( DwMime::kSubtypeMixed ); msg->headers().ContentType().CreateBoundary( 0 ); msg->headers().ContentType().Assemble(); firstPart.setBodyFromUnicode( plainTextBody ); msg->addBodyPart( &firstPart ); } // Store a new entry that was received from the resource TQ_UINT32 KMailICalIfaceImpl::addIncidenceKolab( KMFolder& folder, const TQString& subject, const TQString& plainTextBody, const TQMap& customHeaders, const TQStringList& attachmentURLs, const TQStringList& attachmentNames, const TQStringList& attachmentMimetypes ) { kdDebug(5006) << "KMailICalIfaceImpl::addIncidenceKolab( " << attachmentNames << " )" << endl; TQ_UINT32 sernum = 0; bool bAttachOK = true; // Make a new message for the incidence KMMessage* msg = new KMMessage(); msg->initHeader(); msg->setSubject( subject ); msg->setAutomaticFields( true ); TQMap::ConstIterator ith = customHeaders.begin(); const TQMap::ConstIterator ithEnd = customHeaders.end(); for ( ; ith != ithEnd ; ++ith ) { msg->setHeaderField( ith.key(), ith.data() ); } // In case of the ical format, simply add the plain text content with the // right content type if ( storageFormat( &folder ) == StorageXML ) { setXMLContentTypeHeader( msg, plainTextBody ); } else if ( storageFormat( &folder ) == StorageIcalVcard ) { const KMail::FolderContentsType t = folder.storage()->contentsType(); setIcalVcardContentTypeHeader( msg, t, &folder ); msg->setBodyEncoded( plainTextBody.utf8() ); } else { kdWarning(5006) << k_funcinfo << "Attempt to write to folder with unknown storage type" << endl; } Q_ASSERT( attachmentMimetypes.count() == attachmentURLs.count() ); Q_ASSERT( attachmentNames.count() == attachmentURLs.count() ); // Add all attachments by reading them from their temp. files TQStringList::ConstIterator itmime = attachmentMimetypes.begin(); TQStringList::ConstIterator iturl = attachmentURLs.begin(); for( TQStringList::ConstIterator itname = attachmentNames.begin(); itname != attachmentNames.end() && itmime != attachmentMimetypes.end() && iturl != attachmentURLs.end(); ++itname, ++iturl, ++itmime ){ bool byname = !(*itmime).startsWith( "application/x-vnd.kolab." ); if( !updateAttachment( *msg, *iturl, *itname, *itmime, byname ) ){ kdWarning(5006) << "Attachment error, can not add Incidence." << endl; bAttachOK = false; break; } } if( bAttachOK ){ // Mark the message as read and store it in the folder msg->cleanupHeader(); //debugBodyParts( "after cleanup", *msg ); msg->touch(); if ( folder.addMsg( msg ) == 0 ) // Message stored sernum = msg->getMsgSerNum(); kdDebug(5006) << "addIncidenceKolab(): Message done and saved. Sernum: " << sernum << endl; //debugBodyParts( "after addMsg", *msg ); addFolderChange( &folder, Contents ); syncFolder( &folder ); } else kdError(5006) << "addIncidenceKolab(): Message *NOT* saved!\n"; return sernum; } bool KMailICalIfaceImpl::deleteIncidenceKolab( const TQString& resource, TQ_UINT32 sernum ) { // Find the message from the serial number and delete it. if( !mUseResourceIMAP ) return false; kdDebug(5006) << "KMailICalIfaceImpl::deleteIncidenceKolab( " << resource << ", " << sernum << ")\n"; // Find the folder KMFolder* f = findResourceFolder( resource ); if( !f ) { kdError(5006) << "deleteIncidenceKolab(" << resource << ") : Not an IMAP resource folder" << endl; return false; } bool rc = false; KMMessage* msg = findMessageBySerNum( sernum, f ); if( msg ) { // Message found - delete it and return happy deleteMsg( msg ); syncFolder( f ); rc = true; } else { kdDebug(5006) << "Message not found, cannot remove serNum " << sernum << endl; } return rc; } int KMailICalIfaceImpl::incidencesKolabCount( const TQString& mimetype, const TQString& resource ) { Q_UNUSED( mimetype ); // honouring that would be too slow... if( !mUseResourceIMAP ) return 0; KMFolder* f = findResourceFolder( resource ); if( !f ) { kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl; return 0; } f->open("kolabcount"); int n = f->count(); f->close("kolabcount"); kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolabCount( " << resource << " ) returned " << n << endl; return n; } TQMap KMailICalIfaceImpl::incidencesKolab( const TQString& mimetype, const TQString& resource, int startIndex, int nbMessages ) { /// Get the mimetype attachments from this folder. Returns a /// TQMap with serialNumber/attachment pairs. /// (serial numbers of the mail are provided for easier later update) TQMap aMap; if( !mUseResourceIMAP ) return aMap; KMFolder* f = findResourceFolder( resource ); if( !f ) { kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl; return aMap; } f->open( "incidences" ); kdDebug(5006) << k_funcinfo << "Getting incidences (" << mimetype << ") for folder " << f->label() << ", starting with index " << startIndex << ", " << nbMessages << " messages." << endl; kdDebug(5006) << "The folder has " << f->count() << " messages." << endl; int stopIndex = nbMessages == -1 ? f->count() : TQMIN( f->count(), startIndex + nbMessages ); for(int i = startIndex; i < stopIndex; ++i) { #if 0 bool unget = !f->isMessage(i); KMMessage* msg = f->getMsg( i ); #else // faster KMMessage* msg = f->storage()->readTemporaryMsg(i); #endif if ( msg ) { const int iSlash = mimetype.find('/'); const TQCString sType = mimetype.left( iSlash ).latin1(); const TQCString sSubtype = mimetype.mid( iSlash+1 ).latin1(); if ( sType.isEmpty() || sSubtype.isEmpty() ) { kdError(5006) << mimetype << " not an type/subtype combination" << endl; } else { DwBodyPart* dwPart = findBodyPartByMimeType( *msg, sType, sSubtype ); if ( dwPart ) { KMMessagePart msgPart; KMMessage::bodyPart(dwPart, &msgPart); aMap.insert(msg->getMsgSerNum(), msgPart.bodyToUnicode( TQTextCodec::codecForName( "utf8" ) )); } else { // Check if the whole message has the right types. This is what // happens in the case of ical storage, where the whole mail is // the data const TQCString type( msg->typeStr() ); const TQCString subtype( msg->subtypeStr() ); if (type.lower() == sType && subtype.lower() == sSubtype ) { aMap.insert( msg->getMsgSerNum(), msg->bodyToUnicode() ); } // This is *not* an error: it may be that not all of the messages // have a message part that is matching the wanted MIME type } } #if 0 if( unget ) f->unGetMsg(i); #else delete msg; #endif } else { kdDebug(5006) << k_funcinfo << " Unable to retrieve message " << i << " for incidence!" << endl; } } f->close( "incidences" ); return aMap; } /* Called when a message that was downloaded from an online imap folder * arrives. Needed when listing incidences on online account folders. */ // TODO: Till, port me void KMailICalIfaceImpl::slotMessageRetrieved( KMMessage* msg ) { if( !msg ) return; KMFolder *parent = msg->parent(); Q_ASSERT( parent ); TQ_UINT32 sernum = msg->getMsgSerNum(); // do we have an accumulator for this folder? Accumulator *ac = mAccumulators.find( parent->location() ); if( ac ) { TQString s; if ( !vPartFoundAndDecoded( msg, s ) ) return; TQString uid( "UID" ); vPartMicroParser( s, uid ); const TQ_UINT32 sernum = msg->getMsgSerNum(); mUIDToSerNum.insert( uid, sernum ); ac->add( s ); if( ac->isFull() ) { /* if this was the last one we were waiting for, tell the resource * about the new incidences and clean up. */ //asyncLoadResult( ac->incidences, ac->type, ac->folder ); mAccumulators.remove( ac->folder ); // autodelete } } else { /* We are not accumulating for this folder, so this one was added * by KMail. Do your thang. */ slotIncidenceAdded( msg->parent(), msg->getMsgSerNum() ); } if ( mTheUnGetMes.contains( sernum ) ) { mTheUnGetMes.remove( sernum ); int i = 0; KMFolder* folder = 0; KMMsgDict::instance()->getLocation( sernum, &folder, &i ); folder->unGetMsg( i ); } } static int dimapAccountCount() { KMail::AccountManager *mgr = kmkernel->acctMgr(); KMAccount *account = mgr->first(); int count = 0; while ( account ) { if ( dynamic_cast( account ) ) ++count; account = mgr->next(); } return count; } int KMailICalIfaceImpl::dimapAccounts() { return dimapAccountCount(); } static TQString subresourceLabelForPresentation( const KMFolder * folder ) { if( KMailICalIfaceImpl::getResourceMap()->contains( folder->location() ) ) { return folder->label(); } TQString label = folder->prettyURL(); TQStringList parts = TQStringList::split( TQString::fromLatin1("/"), label ); // In the common special case of some other user's folder shared with us // the url looks like "Server Name/user/$USERNAME/Folder/Name". Make // those a bit nicer. if ( parts[1] == TQString::fromLatin1("user") ) { TQStringList remainder(parts); remainder.pop_front(); remainder.pop_front(); remainder.pop_front(); label = i18n("%1's %2") .arg( parts[2] ) .arg( remainder.join( TQString::fromLatin1("/") ) ); } // Another special case is our own folders, under the imap INBOX, make // those prettier too const KMFolder *parent = folder; while ( parent->parent() && parent->parent()->owner() ) { parent = parent->parent()->owner(); if ( parent->isSystemFolder() ) { TQStringList remainder(parts); remainder.pop_front(); remainder.pop_front(); if ( dimapAccountCount() > 1 ) { // Fix kolab issue 2531 folder->storage() )->account() can be null if( folder->storage() && static_cast( folder->storage() )->account() ) { label = i18n( "My %1 (%2)") .arg( remainder.join( TQString::fromLatin1("/") ), static_cast( folder->storage() )->account()->name() ); } else { label = i18n("My %1") .arg( remainder.join( TQString::fromLatin1("/") ) ); } } else { label = i18n("My %1") .arg( remainder.join( TQString::fromLatin1("/") ) ); } break; } } return label; } /* list all available subresources */ TQValueList KMailICalIfaceImpl::subresourcesKolab( const TQString& contentsType ) { TQValueList subResources; // Add the default one KMFolder* f = folderFromType( contentsType, TQString() ); if ( f ) { subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ), f->isWritable(), folderIsAlarmRelevant( f ) ) ); kdDebug(5006) << "Adding(1) folder " << f->location() << " " << ( !f->isWritable() ? "readonly" : "" ) << endl; } // get the extra ones const KMail::FolderContentsType t = folderContentsType( contentsType ); TQDictIterator it( mExtraFolders ); for ( ; it.current(); ++it ){ f = it.current()->folder; if ( f && f->storage()->contentsType() == t ) { subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ), f->isWritable(), folderIsAlarmRelevant( f ) ) ); kdDebug(5006) << "Adding(2) folder " << f->location() << " " << ( !f->isWritable() ? "readonly" : "" ) << endl; } } if ( subResources.isEmpty() ) kdDebug(5006) << "subresourcesKolab: No folder found for " << contentsType << endl; return subResources; } bool KMailICalIfaceImpl::triggerSync( const TQString& contentsType ) { kdDebug(5006) << k_funcinfo << endl; TQValueList folderList = subresourcesKolab( contentsType ); for ( TQValueList::const_iterator it( folderList.begin() ), end( folderList.end() ); it != end ; ++it ) { KMFolder * const f = findResourceFolder( (*it).location ); if ( !f ) continue; if ( f->folderType() == KMFolderTypeImap || f->folderType() == KMFolderTypeCachedImap ) { if ( !kmkernel->askToGoOnline() ) { return false; } } if ( f->folderType() == KMFolderTypeImap ) { KMFolderImap *imap = static_cast( f->storage() ); imap->getAndCheckFolder(); } else if ( f->folderType() == KMFolderTypeCachedImap ) { KMFolderCachedImap* cached = static_cast( f->storage() ); if ( cached->account() ) { cached->account()->processNewMailInFolder( f ); } } } return true; } /* Used by the resource to query whether folders are writable. */ bool KMailICalIfaceImpl::isWritableFolder( const TQString& type, const TQString& resource ) { KMFolder* f = folderFromType( type, resource ); if ( !f ) // Definitely not writable return false; return f->isWritable(); } /* Used by the resource to query the storage format of the folder. */ KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( const TQString& resource ) { StorageFormat format; KMFolder* f = findResourceFolder( resource ); if ( f ) format = storageFormat( f ); else format = globalStorageFormat(); return format; } /** // This finds the message with serial number "sernum", sets the // xml attachments to hold the contents of "xml", and updates all // attachments. // The mail can have additional attachments, and these are not // touched! They belong to other clients - like Outlook // So we delete all the attachments listed in the // "deletedAttachments" arg, and then update/add all the attachments // given by the urllist attachments. // If the mail does not already exist, id will not be a valid serial // number, and the mail is just added instead. In this case // the deletedAttachments can be forgotten. */ TQ_UINT32 KMailICalIfaceImpl::update( const TQString& resource, TQ_UINT32 sernum, const TQString& subject, const TQString& plainTextBody, const TQMap& customHeaders, const TQStringList& attachmentURLs, const TQStringList& attachmentMimetypes, const TQStringList& attachmentNames, const TQStringList& deletedAttachments ) { TQ_UINT32 rc = 0; if( !mUseResourceIMAP ) return rc; Q_ASSERT( !resource.isEmpty() ); kdDebug(5006) << "KMailICalIfaceImpl::update( " << resource << ", " << sernum << " )\n"; kdDebug(5006) << attachmentURLs << "\n"; kdDebug(5006) << attachmentMimetypes << "\n"; kdDebug(5006) << attachmentNames << "\n"; kdDebug(5006) << "deleted attachments:" << deletedAttachments << "\n"; // Find the folder KMFolder* f = findResourceFolder( resource ); if( !f ) { kdError(5006) << "update(" << resource << ") : Not an IMAP resource folder" << endl; return rc; } f->open( "ifaceupdate" ); KMMessage* msg = 0; if ( sernum != 0 ) { msg = findMessageBySerNum( sernum, f ); if ( !msg ) return 0; // Message found - make a copy and update it: KMMessage* newMsg = new KMMessage( *msg ); newMsg->setSubject( subject ); TQMap::ConstIterator ith = customHeaders.begin(); const TQMap::ConstIterator ithEnd = customHeaders.begin(); for ( ; ith != ithEnd ; ++ith ) newMsg->setHeaderField( ith.key(), ith.data() ); newMsg->setParent( 0 ); // workaround strange line in KMMsgBase::assign. newMsg is not in any folder yet. // Note that plainTextBody isn't used in this branch. We assume it's still valid from when the mail was created. // Delete some attachments according to list for( TQStringList::ConstIterator it = deletedAttachments.begin(); it != deletedAttachments.end(); ++it ){ if( !deleteAttachment( *newMsg, *it ) ){ // Note: It is _not_ an error if an attachment was already deleted. } } const KMail::FolderContentsType t = f->storage()->contentsType(); const TQCString type = msg->typeStr(); const TQCString subtype = msg->subtypeStr(); const bool messageWasIcalVcardFormat = ( type.lower() == "text" && ( subtype.lower() == "calendar" || subtype.lower() == "x-vcard" ) ); if ( storageFormat( f ) == StorageIcalVcard ) { //kdDebug(5006) << k_funcinfo << " StorageFormatIcalVcard " << endl; if ( !messageWasIcalVcardFormat ) { setIcalVcardContentTypeHeader( newMsg, t, f ); } newMsg->setBodyEncoded( plainTextBody.utf8() ); } else if ( storageFormat( f ) == StorageXML ) { if ( messageWasIcalVcardFormat ) { // this was originally an ical event, but the folder changed to xml, // convert setXMLContentTypeHeader( newMsg, plainTextBody ); } //kdDebug(5006) << k_funcinfo << " StorageFormatXML " << endl; // Add all attachments by reading them from their temp. files TQStringList::ConstIterator iturl = attachmentURLs.begin(); TQStringList::ConstIterator itmime = attachmentMimetypes.begin(); TQStringList::ConstIterator itname = attachmentNames.begin(); for( ; iturl != attachmentURLs.end() && itmime != attachmentMimetypes.end() && itname != attachmentNames.end(); ++iturl, ++itname, ++itmime ){ bool byname = !(*itmime).startsWith( "application/x-vnd.kolab." ); if( !updateAttachment( *newMsg, *iturl, *itname, *itmime, byname ) ){ kdDebug(5006) << "Attachment error, can not update attachment " << *iturl << endl; break; } } } //debugBodyParts( "in update, before cleanup", *newMsg ); // This is necessary for the headers to be readable later on newMsg->cleanupHeader(); //debugBodyParts( "in update, after cleanup", *newMsg ); deleteMsg( msg ); if ( f->addMsg( newMsg ) == 0 ) { // Message stored rc = newMsg->getMsgSerNum(); kdDebug(5006) << "forget about " << sernum << ", it's " << rc << " now" << endl; } addFolderChange( f, Contents ); syncFolder( f ); } else { // Message not found - store it newly rc = addIncidenceKolab( *f, subject, plainTextBody, customHeaders, attachmentURLs, attachmentNames, attachmentMimetypes ); } f->close("ifaceupdate"); return rc; } KURL KMailICalIfaceImpl::getAttachment( const TQString& resource, TQ_UINT32 sernum, const TQString& filename ) { // This finds the attachment with the filename, saves it to a // temp file and returns a URL to it. It's up to the resource // to delete the tmp file later. if( !mUseResourceIMAP ) return KURL(); kdDebug(5006) << "KMailICalIfaceImpl::getAttachment( " << resource << ", " << sernum << ", " << filename << " )\n"; // Find the folder KMFolder* f = findResourceFolder( resource ); if( !f ) { kdError(5006) << "getAttachment(" << resource << ") : Not an IMAP resource folder" << endl; return KURL(); } if ( storageFormat( f ) != StorageXML ) { kdError(5006) << "getAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl; return KURL(); } KURL url; bool bOK = false; bool quiet = mResourceQuiet; mResourceQuiet = true; KMMessage* msg = findMessageBySerNum( sernum, f ); if( msg ) { // Message found - look for the attachment: DwBodyPart* part = findBodyPart( *msg, filename ); if ( part ) { // Save the contents of the attachment. KMMessagePart aPart; msg->bodyPart( part, &aPart ); TQByteArray rawData( aPart.bodyDecodedBinary() ); KTempFile file; file.file()->writeBlock( rawData.data(), rawData.size() ); url.setPath( file.name() ); bOK = true; } if( !bOK ){ kdDebug(5006) << "Attachment " << filename << " not found." << endl; } }else{ kdDebug(5006) << "Message not found." << endl; } mResourceQuiet = quiet; return url; } TQString KMailICalIfaceImpl::attachmentMimetype( const TQString & resource, TQ_UINT32 sernum, const TQString & filename ) { if( !mUseResourceIMAP ) return TQString(); KMFolder* f = findResourceFolder( resource ); if( !f || storageFormat( f ) != StorageXML ) { kdError(5006) << "attachmentMimetype(" << resource << ") : Wrong folder" << endl; return TQString(); } KMMessage* msg = findMessageBySerNum( sernum, f ); if( msg ) { // Message found - look for the attachment: DwBodyPart* part = findBodyPart( *msg, filename ); if ( part ) { KMMessagePart kmPart; msg->bodyPart( part, &kmPart ); return TQString( kmPart.typeStr() ) + "/" + TQString( kmPart.subtypeStr() ); } else { kdDebug(5006) << "Attachment " << filename << " not found." << endl; } } else { kdDebug(5006) << "Message not found." << endl; } return TQString(); } TQStringList KMailICalIfaceImpl::listAttachments(const TQString & resource, TQ_UINT32 sernum) { TQStringList rv; if( !mUseResourceIMAP ) return rv; // Find the folder KMFolder* f = findResourceFolder( resource ); if( !f ) { kdError(5006) << "listAttachments(" << resource << ") : Not an IMAP resource folder" << endl; return rv; } if ( storageFormat( f ) != StorageXML ) { kdError(5006) << "listAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl; return rv; } KMMessage* msg = findMessageBySerNum( sernum, f ); if( msg ) { for ( DwBodyPart* part = msg->getFirstDwBodyPart(); part; part = part->Next() ) { if ( part->hasHeaders() ) { TQString name; DwMediaType& contentType = part->Headers().ContentType(); if ( TQString( contentType.SubtypeStr().c_str() ).startsWith( "x-vnd.kolab." ) || TQString( contentType.SubtypeStr().c_str() ).contains( "tnef" ) ) continue; if ( !part->Headers().ContentDisposition().Filename().empty() ) name = part->Headers().ContentDisposition().Filename().c_str(); else if ( !contentType.Name().empty() ) name = contentType.Name().c_str(); if ( !name.isEmpty() ) rv.append( name ); } } } else { kdDebug(5006) << "Message not found." << endl; } return rv; } // ============================================================================ /* KMail part of the interface. These slots are connected to the resource * folders and inform us of folders or incidences in them changing, being * added or going away. */ void KMailICalIfaceImpl::slotFolderRemoved( KMFolder* folder ) { // pretend the folder just changed back to the mail type, which // does the right thing, namely remove resource folderContentsTypeChanged( folder, KMail::ContentsTypeMail ); TDEConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); configGroup.deleteEntry( folder->idString() + "-storageFormat" ); configGroup.deleteEntry( folder->idString() + "-changes" ); } // KMail added a file to one of the groupware folders void KMailICalIfaceImpl::slotIncidenceAdded( KMFolder* folder, TQ_UINT32 sernum ) { if( mResourceQuiet || !mUseResourceIMAP ) return; // kdDebug(5006) << "KMailICalIfaceImpl::slotIncidenceAdded" << endl; TQString type = folderContentsType( folder->storage()->contentsType() ); if( type.isEmpty() ) { kdError(5006) << "Not an IMAP resource folder" << endl; return; } // Get the index of the mail int i = 0; KMFolder* aFolder = 0; KMMsgDict::instance()->getLocation( sernum, &aFolder, &i ); assert( folder == aFolder ); bool unget = !folder->isMessage( i ); TQString s; TQString uid( "UID" ); KMMessage *msg = folder->getMsg( i ); if( !msg ) return; if( msg->isComplete() ) { bool ok = false; StorageFormat format = storageFormat( folder ); switch( format ) { case StorageIcalVcard: // Read the iCal or vCard ok = vPartFoundAndDecoded( msg, s ); if ( ok ) vPartMicroParser( s, uid ); break; case StorageXML: // Read the XML from the attachment with the given mimetype if ( kolabXMLFoundAndDecoded( *msg, folderKolabMimeType( folder->storage()->contentsType() ), s ) ) { uid = msg->subject(); ok = true; } break; } if ( !ok ) { if ( unget ) folder->unGetMsg( i ); return; } const TQ_UINT32 sernum = msg->getMsgSerNum(); mUIDToSerNum.insert( uid, sernum ); // tell the resource if we didn't trigger this ourselves if ( mInTransit.contains( uid ) ) { mInTransit.remove( uid ); } incidenceAdded( type, folder->location(), sernum, format, s ); } else { // go get the rest of it, then try again // TODO: Till, port me if ( unget ) mTheUnGetMes.insert( msg->getMsgSerNum(), true ); FolderJob *job = msg->parent()->createJob( msg ); connect( job, TQT_SIGNAL( messageRetrieved( KMMessage* ) ), this, TQT_SLOT( slotMessageRetrieved( KMMessage* ) ) ); job->start(); return; } if( unget ) folder->unGetMsg(i); } // KMail deleted a file void KMailICalIfaceImpl::slotIncidenceDeleted( KMFolder* folder, TQ_UINT32 sernum ) { if( mResourceQuiet || !mUseResourceIMAP ) return; TQString type = folderContentsType( folder->storage()->contentsType() ); //kdDebug(5006) << folder << " " << type << " " << sernum << endl; if( !type.isEmpty() ) { // Get the index of the mail int i = 0; KMFolder* aFolder = 0; KMMsgDict::instance()->getLocation( sernum, &aFolder, &i ); assert( folder == aFolder ); // Read the iCal or vCard bool unget = !folder->isMessage( i ); TQString s; bool ok = false; KMMessage* msg = folder->getMsg( i ); TQString uid( "UID" ); switch( storageFormat( folder ) ) { case StorageIcalVcard: if( vPartFoundAndDecoded( msg, s ) ) { vPartMicroParser( s, uid ); ok = true; } break; case StorageXML: if ( kolabXMLFoundAndDecoded( *msg, folderKolabMimeType( folder->storage()->contentsType() ), s ) ) { uid = msg->subject(); ok = true; } break; } if ( ok ) { kdDebug(5006) << "Emitting DCOP signal incidenceDeleted( " << type << ", " << folder->location() << ", " << uid << " )" << endl; incidenceDeleted( type, folder->location(), uid ); } if( unget ) folder->unGetMsg(i); } else kdError(5006) << "Not a groupware folder" << endl; } // KMail orders a refresh void KMailICalIfaceImpl::slotRefresh( const TQString& type ) { if( mUseResourceIMAP ) { signalRefresh( type, TQString() /* PENDING(bo) folder->location() */ ); kdDebug(5006) << "Emitting DCOP signal signalRefresh( " << type << " )" << endl; } } // This is among other things called when an expunge of a folder happens void KMailICalIfaceImpl::slotRefreshFolder( KMFolder* folder) { // TODO: The resources would of course be better off, if only this // folder would need refreshing. Currently it just orders a reload of // the type of the folder if( mUseResourceIMAP && folder ) { if( folder == mCalendar || folder == mContacts || folder == mNotes || folder == mTasks || folder == mJournals || mExtraFolders.find( folder->location() ) ) { // Refresh the folder of this type KMail::FolderContentsType ct = folder->storage()->contentsType(); slotRefresh( s_folderContentsType[ct].contentsTypeStr ); } } } /**************************** * The folder and message stuff code */ KMFolder* KMailICalIfaceImpl::folderFromType( const TQString& type, const TQString& folder ) { if( mUseResourceIMAP ) { KMFolder* f = 0; if ( !folder.isEmpty() ) { f = extraFolder( type, folder ); if ( f ) return f; } if( type == "Calendar" ) f = mCalendar; else if( type == "Contact" ) f = mContacts; else if( type == "Note" ) f = mNotes; else if( type == "Task" || type == "Todo" ) f = mTasks; else if( type == "Journal" ) f = mJournals; if ( f && ( folder.isEmpty() || folder == f->location() ) ) return f; kdError(5006) << "No folder ( " << type << ", " << folder << " )\n"; } return 0; } // Returns true if folder is a resource folder. If the resource isn't enabled // this always returns false bool KMailICalIfaceImpl::isResourceFolder( KMFolder* folder ) const { return mUseResourceIMAP && folder && ( isStandardResourceFolder( folder ) || mExtraFolders.find( folder->location() )!=0 ); } bool KMailICalIfaceImpl::isStandardResourceFolder( KMFolder* folder ) const { return ( folder == mCalendar || folder == mTasks || folder == mJournals || folder == mNotes || folder == mContacts ); } bool KMailICalIfaceImpl::hideResourceFolder( KMFolder* folder ) const { return mHideFolders && isResourceFolder( folder ); } bool KMailICalIfaceImpl::hideResourceAccountRoot( KMFolder* folder ) const { KMFolderCachedImap *dimapFolder = dynamic_cast( folder->storage() ); bool hide = dimapFolder && mHideFolders && (int)dimapFolder->account()->id() == GlobalSettings::self()->theIMAPResourceAccount() && GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount(); return hide; } KFolderTreeItem::Type KMailICalIfaceImpl::folderType( KMFolder* folder ) const { if( mUseResourceIMAP && folder ) { if( folder == mCalendar || folder == mContacts || folder == mNotes || folder == mTasks || folder == mJournals || mExtraFolders.find( folder->location() ) ) { KMail::FolderContentsType ct = folder->storage()->contentsType(); return s_folderContentsType[ct].treeItemType; } } return KFolderTreeItem::Other; } // Global tables of foldernames is different languages // For now: 0->English, 1->German, 2->French, 3->Dutch static TQMap folderNames[4]; TQString KMailICalIfaceImpl::folderName( KFolderTreeItem::Type type, int language ) const { // With the XML storage, folders are always (internally) named in English if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ) language = 0; static bool folderNamesSet = false; if( !folderNamesSet ) { folderNamesSet = true; /* NOTE: If you add something here, you also need to update GroupwarePage in configuredialog.cpp */ // English folderNames[0][KFolderTreeItem::Calendar] = TQString::fromLatin1("Calendar"); folderNames[0][KFolderTreeItem::Tasks] = TQString::fromLatin1("Tasks"); folderNames[0][KFolderTreeItem::Journals] = TQString::fromLatin1("Journal"); folderNames[0][KFolderTreeItem::Contacts] = TQString::fromLatin1("Contacts"); folderNames[0][KFolderTreeItem::Notes] = TQString::fromLatin1("Notes"); // German folderNames[1][KFolderTreeItem::Calendar] = TQString::fromLatin1("Kalender"); folderNames[1][KFolderTreeItem::Tasks] = TQString::fromLatin1("Aufgaben"); folderNames[1][KFolderTreeItem::Journals] = TQString::fromLatin1("Journal"); folderNames[1][KFolderTreeItem::Contacts] = TQString::fromLatin1("Kontakte"); folderNames[1][KFolderTreeItem::Notes] = TQString::fromLatin1("Notizen"); // French folderNames[2][KFolderTreeItem::Calendar] = TQString::fromLatin1("Calendrier"); // Tasks = Tâches (â == 0xE2 in latin1) folderNames[2][KFolderTreeItem::Tasks] = TQString::fromLatin1("T\342ches"); folderNames[2][KFolderTreeItem::Journals] = TQString::fromLatin1("Journal"); folderNames[2][KFolderTreeItem::Contacts] = TQString::fromLatin1("Contacts"); folderNames[2][KFolderTreeItem::Notes] = TQString::fromLatin1("Notes"); // Dutch folderNames[3][KFolderTreeItem::Calendar] = TQString::fromLatin1("Agenda"); folderNames[3][KFolderTreeItem::Tasks] = TQString::fromLatin1("Taken"); folderNames[3][KFolderTreeItem::Journals] = TQString::fromLatin1("Logboek"); folderNames[3][KFolderTreeItem::Contacts] = TQString::fromLatin1("Contactpersonen"); folderNames[3][KFolderTreeItem::Notes] = TQString::fromLatin1("Notities"); } if( language < 0 || language > 3 ) { return folderNames[mFolderLanguage][type]; } else { return folderNames[language][type]; } } // Find message matching a given UID KMMessage *KMailICalIfaceImpl::findMessageByUID( const TQString& uid, KMFolder* folder ) { if( !folder || !mUIDToSerNum.contains( uid ) ) return 0; int i; KMFolder *aFolder; KMMsgDict::instance()->getLocation( mUIDToSerNum[uid], &aFolder, &i ); Q_ASSERT( aFolder == folder ); return folder->getMsg( i ); } // Find message matching a given serial number KMMessage *KMailICalIfaceImpl::findMessageBySerNum( TQ_UINT32 serNum, KMFolder* folder ) { if( !folder ) return 0; KMMessage *message = 0; KMFolder* aFolder = 0; int index; KMMsgDict::instance()->getLocation( serNum, &aFolder, &index ); if( aFolder && aFolder != folder ) { kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) found it in folder " << aFolder->location() << ", expected " << folder->location() << endl; } else { if( aFolder ) message = aFolder->getMsg( index ); if (!message) kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) invalid serial number\n" << endl; } return message; } void KMailICalIfaceImpl::deleteMsg( KMMessage *msg ) { if( !msg ) return; // Commands are now delayed; can't use that anymore, we need immediate deletion //( new KMDeleteMsgCommand( msg->parent(), msg ) )->start(); KMFolder *srcFolder = msg->parent(); int idx = srcFolder->find(msg); assert(idx != -1); // kill existing jobs since we are about to delete the message srcFolder->ignoreJobsForMessage( msg ); if ( !msg->transferInProgress() ) { srcFolder->removeMsg(idx); delete msg; } else { kdDebug(5006) << k_funcinfo << "Message cannot be deleted now because it is currently in use " << msg << endl; msg->deleteWhenUnused(); } addFolderChange( srcFolder, Contents ); } void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder, KMail::FolderContentsType contentsType ) { if ( !mUseResourceIMAP ) return; // kdDebug(5006) << "folderContentsTypeChanged( " << folder->name() // << ", " << contentsType << ")\n"; // The builtins can't change type if ( isStandardResourceFolder( folder ) ) return; // Check if already know that 'extra folder' const TQString location = folder->location(); ExtraFolder* ef = mExtraFolders.find( location ); if ( ef && ef->folder ) { // Notify that the old folder resource is no longer available subresourceDeleted(folderContentsType( folder->storage()->contentsType() ), location ); if ( contentsType == KMail::ContentsTypeMail ) { // Delete the old entry, stop listening and stop here mExtraFolders.remove( location ); folder->disconnect( this ); return; } // So the type changed to another groupware type, ok. } else { if ( ef && !ef->folder ) // deleted folder, clean up mExtraFolders.remove( location ); if ( contentsType == KMail::ContentsTypeMail ) return; //kdDebug(5006) << "registering " << location << " as extra folder" << endl; // Make a new entry for the list ef = new ExtraFolder( folder ); mExtraFolders.insert( location, ef ); FolderInfo info = readFolderInfo( folder ); mFolderInfoMap.insert( folder, info ); // Adjust the folder names of all foo.default folders. // German users will get Kalender as the name of all default Calendar folders, // including their own, so that the default calendar folder of their Japanese // coworker appears as /user/hirohito/Kalender, although Hirohito sees his folder // in Japanese. On the server the folders are always in English. if ( folder->folderType() == KMFolderTypeCachedImap ) { TQString annotation = static_cast( folder->storage() )->annotationFolderType(); kdDebug(5006) << "folderContentsTypeChanged: " << folder->name() << " has annotation " << annotation << endl; if ( annotation == TQString( s_folderContentsType[contentsType].annotation ) + ".default" ) folder->setLabel( localizedDefaultFolderName( contentsType ) ); } connectFolder( folder ); } // Tell about the new resource subresourceAdded( folderContentsType( contentsType ), location, subresourceLabelForPresentation(folder), folder->isWritable(), folderIsAlarmRelevant( folder ) ); } KMFolder* KMailICalIfaceImpl::extraFolder( const TQString& type, const TQString& folder ) { // If an extra folder exists that matches the type and folder location, // use that int t = folderContentsType( type ); if ( t < 1 || t > 5 ) return 0; ExtraFolder* ef = mExtraFolders.find( folder ); if ( ef && ef->folder && ef->folder->storage()->contentsType() == t ) return ef->folder; return 0; } KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( KMFolder* folder ) const { FolderInfoMap::ConstIterator it = mFolderInfoMap.find( folder ); if ( it != mFolderInfoMap.end() ) return (*it).mStorageFormat; return globalStorageFormat(); } void KMailICalIfaceImpl::setStorageFormat( KMFolder* folder, StorageFormat format ) { FolderInfoMap::Iterator it = mFolderInfoMap.find( folder ); if ( it != mFolderInfoMap.end() ) { (*it).mStorageFormat = format; } else { FolderInfo info( format, NoChange ); mFolderInfoMap.insert( folder, info ); } TDEConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); configGroup.writeEntry( folder->idString() + "-storageFormat", format == StorageXML ? "xml" : "icalvcard" ); } void KMailICalIfaceImpl::addFolderChange( KMFolder* folder, FolderChanges changes ) { FolderInfoMap::Iterator it = mFolderInfoMap.find( folder ); if ( it != mFolderInfoMap.end() ) { (*it).mChanges = static_cast( (*it).mChanges | changes ); } else { // Otherwise, well, it's a folder we don't care about. kdDebug(5006) << "addFolderChange: nothing known about folder " << folder->location() << endl; } TDEConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); configGroup.writeEntry( folder->idString() + "-changes", (*it).mChanges ); } KMailICalIfaceImpl::FolderInfo KMailICalIfaceImpl::readFolderInfo( const KMFolder * const folder ) const { TDEConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" ); TQString str = configGroup.readEntry( folder->idString() + "-storageFormat", "unset" ); FolderInfo info; if ( str == "unset" ) { info.mStorageFormat = globalStorageFormat(); configGroup.writeEntry( folder->idString() + "-storageFormat", info.mStorageFormat == StorageXML ? "xml" : "icalvcard" ); } else { info.mStorageFormat = ( str == "xml" ) ? StorageXML : StorageIcalVcard; } info.mChanges = (FolderChanges) configGroup.readNumEntry( folder->idString() + "-changes" ); return info; } void KMailICalIfaceImpl::folderSynced( KMFolder* folder, const KURL& folderURL ) { FolderInfoMap::Iterator it = mFolderInfoMap.find( folder ); if ( it != mFolderInfoMap.end() && (*it).mChanges ) { handleFolderSynced( folder, folderURL, (*it).mChanges ); (*it).mChanges = NoChange; } } void KMailICalIfaceImpl::handleFolderSynced( KMFolder* folder, const KURL& folderURL, int _changes ) { // This is done here instead of in the resource, because // there could be 0, 1, or N kolab resources at this point. // We can hack the N case, but not the 0 case. // So the idea of a DCOP signal for this wouldn't work. if ( ( _changes & KMailICalIface::Contents ) || ( _changes & KMailICalIface::ACL ) ) { if ( storageFormat( folder ) == StorageXML && folder->storage()->contentsType() == KMail::ContentsTypeCalendar ) triggerKolabFreeBusy( folderURL ); } } void KMailICalIfaceImpl::folderDeletedOnServer( const KURL& folderURL ) { triggerKolabFreeBusy( folderURL ); } void KMailICalIfaceImpl::triggerKolabFreeBusy( const KURL& folderURL ) { /* Steffen said: you must issue an authenticated HTTP GET request to https://kolabserver/freebusy/trigger/user@domain/Folder/NestedFolder.pfb (replace .pfb with .xpfb for extended fb lists). */ KURL httpURL( folderURL ); // Keep username ("user@domain"), pass, and host from the imap url httpURL.setProtocol( "https" ); httpURL.setPort( 0 ); // remove imap port // IMAP path is either /INBOX/ or /user/someone/ TQString path = folderURL.path( -1 ); Q_ASSERT( path.startsWith( "/" ) ); int secondSlash = path.find( '/', 1 ); if ( secondSlash == -1 ) { kdWarning() << "KCal::ResourceKolab::fromKMailFolderSynced path is too short: " << path << endl; return; } if ( path.startsWith( "/INBOX/", false ) ) { // If INBOX, replace it with the username (which is user@domain) path = path.mid( secondSlash ); path.prepend( folderURL.user() ); } else { // If user, just remove it. So we keep the IMAP-returned username. // This assumes it's a known user on the same domain. path = path.mid( secondSlash ); } httpURL.setPath( "/freebusy/trigger/" + path + ".pfb" ); httpURL.setQuery( TQString() ); // Ensure that we encode everything with UTF8 httpURL = KURL( httpURL.url(0,106), 106 ); kdDebug() << "Triggering PFB update for " << folderURL << " : getting " << httpURL << endl; // "Fire and forget". No need for error handling, nor for explicit deletion. // Maybe we should try to prevent launching it if it's already running (for this URL) though. /*TDEIO::Job* job =*/ TDEIO::get( httpURL, false, false /*no progress info*/ ); } void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder ) { if ( isResourceFolder( folder ) ) { const TQString location = folder->location(); const TQString contentsTypeStr = folderContentsType( folder->storage()->contentsType() ); subresourceDeleted( contentsTypeStr, location ); subresourceAdded( contentsTypeStr, location, subresourceLabelForPresentation( folder ), folder->isWritable(), folderIsAlarmRelevant( folder ) ); } } // Must only be connected to a signal from KMFolder! void KMailICalIfaceImpl::slotFolderRenamed() { const KMFolder* folder = static_cast( sender() ); slotFolderPropertiesChanged( const_cast( folder ) ); } void KMailICalIfaceImpl::slotFolderLocationChanged( const TQString &oldLocation, const TQString &newLocation ) { KMFolder *folder = findResourceFolder( oldLocation ); ExtraFolder* ef = mExtraFolders.find( oldLocation ); if ( ef ) { // reuse the ExtraFolder entry, but adjust the key mExtraFolders.setAutoDelete( false ); mExtraFolders.remove( oldLocation ); mExtraFolders.setAutoDelete( true ); mExtraFolders.insert( newLocation, ef ); } if ( folder ) subresourceDeleted( folderContentsType( folder->storage()->contentsType() ), oldLocation ); } KMFolder* KMailICalIfaceImpl::findResourceFolder( const TQString& resource ) { // Try the standard folders if( mCalendar && mCalendar->location() == resource ) return mCalendar; if ( mContacts && mContacts->location() == resource ) return mContacts; if ( mNotes && mNotes->location() == resource ) return mNotes; if ( mTasks && mTasks->location() == resource ) return mTasks; if ( mJournals && mJournals->location() == resource ) return mJournals; // No luck. Try the extrafolders ExtraFolder* ef = mExtraFolders.find( resource ); if ( ef ) return ef->folder; // No luck at all return 0; } void KMailICalIfaceImpl::changeResourceUIName( const TQString &folderPath, const TQString &newName ) { kdDebug() << "Folder path " << folderPath << endl; KMFolder *f = findResourceFolder( folderPath ); if ( f ) { KMailICalIfaceImpl::getResourceMap()->insert( folderPath, newName ); kmkernel->folderMgr()->renameFolder( f, newName ); TDEConfigGroup configGroup( kmkernel->config(), "Resource UINames" ); configGroup.writeEntry( folderPath, newName ); } } // Builds a folder list from the dimap and the local folder list. static void createFolderList( TQStringList &folderNames, TQValueList > &folderList ) { TQStringList dimapFolderNames; TQStringList localFolderNames; TQValueList > dimapFolderList; TQValueList > localFolderList; kmkernel->dimapFolderMgr()->createFolderList( &dimapFolderNames, &dimapFolderList ); kmkernel->folderMgr()->createFolderList( &localFolderNames, &localFolderList ); folderNames += dimapFolderNames; folderNames += localFolderNames; folderList += dimapFolderList; folderList += localFolderList; } /**************************** * The config stuff */ void KMailICalIfaceImpl::readConfig() { bool enabled = GlobalSettings::self()->theIMAPResourceEnabled() && ( GlobalSettings::self()->theIMAPResourceAccount() != 0 ); if( !enabled ) { if( mUseResourceIMAP == true ) { // Shutting down mUseResourceIMAP = false; cleanup(); reloadFolderTree(); } return; } mUseResourceIMAP = enabled; // Read remaining options const bool hideFolders = GlobalSettings::self()->hideGroupwareFolders(); TQString parentName = GlobalSettings::self()->theIMAPResourceFolderParent(); // Find the folder parent KMFolderDir* folderParentDir; KMFolderType folderType; KMFolder* folderParent = kmkernel->findFolderById( parentName ); if( folderParent == 0 ) { // Parent folder not found. It was probably deleted. The user will have to // configure things again. kdDebug(5006) << "Groupware folder " << parentName << " not found. Groupware functionality disabled" << endl; // Or maybe the inbox simply wasn't created on the first startup KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() ); Q_ASSERT( account ); if ( account ) { // just in case we were connected already disconnect( account, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), this, TQT_SLOT( slotCheckDone() ) ); connect( account, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), this, TQT_SLOT( slotCheckDone() ) ); } mUseResourceIMAP = false; // We can't really call cleanup(), if those folders were completely deleted. mCalendar = 0; mTasks = 0; mJournals = 0; mContacts = 0; mNotes = 0; return; } else { folderParentDir = folderParent->createChildFolder(); folderType = folderParent->folderType(); } KMAcctCachedImap::GroupwareType groupwareType = dynamic_cast( folderParent->storage() )->account()->groupwareType(); if ( groupwareType == KMAcctCachedImap::GroupwareKolab ) { // Make sure the folder parent has the subdirs // Globally there are 3 cases: nothing found, some stuff found by type/name heuristics, or everything found OK bool noneFound = true; bool mustFix = false; // true when at least one was found by heuristics TQValueVector results( KMail::ContentsTypeLast + 1 ); for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) { if ( i != KMail::ContentsTypeMail ) { results[i] = findStandardResourceFolder( folderParentDir, static_cast(i) ); if ( results[i].found == StandardFolderSearchResult::FoundAndStandard ) noneFound = false; else if ( results[i].found == StandardFolderSearchResult::FoundByType || results[i].found == StandardFolderSearchResult::FoundByName ) { mustFix = true; noneFound = false; } else // NotFound mustFix = true; } } // Check if something changed if( mUseResourceIMAP && !noneFound && !mustFix && mFolderParentDir == folderParentDir && mFolderType == folderType ) { // Nothing changed if ( hideFolders != mHideFolders ) { // Well, the folder hiding has changed mHideFolders = hideFolders; reloadFolderTree(); } return; } if( noneFound || mustFix ) { TQString msg; TQString parentFolderName = folderParent != 0 ? folderParent->name() : folderParentDir->name(); if ( noneFound ) { // No subfolder was found, so ask if we can make them msg = i18n("KMail will now create the required groupware folders" " as subfolders of %1; if you do not want this, cancel" " and the IMAP resource will be disabled").arg(parentFolderName); } else { // Some subfolders were found, be more precise TQString operations = "
    "; for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) { if ( i != KMail::ContentsTypeMail ) { TQString typeName = localizedDefaultFolderName( static_cast( i ) ); if ( results[i].found == StandardFolderSearchResult::NotFound ) operations += "
  • " + i18n( "%1: no folder found. It will be created." ).arg( typeName ) + "
  • "; else if ( results[i].found == StandardFolderSearchResult::FoundByType || results[i].found == StandardFolderSearchResult::FoundByName ) operations += "
  • " + i18n( "%1: found folder %2. It will be set as the main groupware folder." ). arg( typeName ).arg( results[i].folder->label() ) + "
  • "; } } operations += "
"; msg = i18n("KMail found the following groupware folders in %1 and needs to perform the following operations: %2" "
If you do not want this, cancel" " and the IMAP resource will be disabled").arg(parentFolderName, operations); } if( KMessageBox::questionYesNo( 0, msg, i18n("Standard Groupware Folders"), KStdGuiItem::cont(), KStdGuiItem::cancel() ) == KMessageBox::No ) { GlobalSettings::self()->setTheIMAPResourceEnabled( false ); mUseResourceIMAP = false; mFolderParentDir = 0; mFolderParent = 0; reloadFolderTree(); return; } } // Make the new settings work mUseResourceIMAP = true; mFolderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage(); if( mFolderLanguage > 3 ) mFolderLanguage = 0; mFolderParentDir = folderParentDir; mFolderParent = folderParent; mFolderType = folderType; mHideFolders = hideFolders; // Close the previous folders cleanup(); // Set the new folders mCalendar = initFolder( KMail::ContentsTypeCalendar ); mTasks = initFolder( KMail::ContentsTypeTask ); mJournals = initFolder( KMail::ContentsTypeJournal ); mContacts = initFolder( KMail::ContentsTypeContact ); mNotes = initFolder( KMail::ContentsTypeNote ); // Store final annotation (with .default) so that we won't ask again on next startup if ( mCalendar->folderType() == KMFolderTypeCachedImap ) static_cast( mCalendar->storage() )->updateAnnotationFolderType(); if ( mTasks->folderType() == KMFolderTypeCachedImap ) static_cast( mTasks->storage() )->updateAnnotationFolderType(); if ( mJournals->folderType() == KMFolderTypeCachedImap ) static_cast( mJournals->storage() )->updateAnnotationFolderType(); if ( mContacts->folderType() == KMFolderTypeCachedImap ) static_cast( mContacts->storage() )->updateAnnotationFolderType(); if ( mNotes->folderType() == KMFolderTypeCachedImap ) static_cast( mNotes->storage() )->updateAnnotationFolderType(); //kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl; //kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl; //kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl; // Find all extra folders TQStringList folderNames; TQValueList > folderList; createFolderList( folderNames, folderList ); for( TQValueList >::iterator it = folderList.begin(); it != folderList.end(); ++it ) { FolderStorage *storage = (*it)->storage(); KMFolderCachedImap* dimapStorage = dynamic_cast( storage ); if ( storage && storage->contentsType() != 0 ) { if ( dimapStorage ) dimapStorage->updateAnnotationFolderType(); folderContentsTypeChanged( *it, storage->contentsType() ); } } // If we just created them, they might have been registered as extra folders temporarily. // -> undo that. mExtraFolders.remove( mCalendar->location() ); mExtraFolders.remove( mTasks->location() ); mExtraFolders.remove( mJournals->location() ); mExtraFolders.remove( mContacts->location() ); mExtraFolders.remove( mNotes->location() ); subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true ); subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true ); subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(), mJournals->label(), true, false ); subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false ); subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false ); } else if ( groupwareType == KMAcctCachedImap::GroupwareScalix ) { // Make the new settings work mUseResourceIMAP = true; mFolderParentDir = folderParentDir; mFolderParent = folderParent; mFolderType = folderType; mHideFolders = false; // Close the previous folders cleanup(); // Set the new folders mCalendar = initScalixFolder( KMail::ContentsTypeCalendar ); mTasks = initScalixFolder( KMail::ContentsTypeTask ); mJournals = 0; mContacts = initScalixFolder( KMail::ContentsTypeContact ); mNotes = initScalixFolder( KMail::ContentsTypeNote ); // Store final annotation (with .default) so that we won't ask again on next startup if ( mCalendar->folderType() == KMFolderTypeCachedImap ) static_cast( mCalendar->storage() )->updateAnnotationFolderType(); if ( mTasks->folderType() == KMFolderTypeCachedImap ) static_cast( mTasks->storage() )->updateAnnotationFolderType(); if ( mContacts->folderType() == KMFolderTypeCachedImap ) static_cast( mContacts->storage() )->updateAnnotationFolderType(); if ( mNotes->folderType() == KMFolderTypeCachedImap ) static_cast( mNotes->storage() )->updateAnnotationFolderType(); // BEGIN TILL TODO The below only uses the dimap folder manager, which // will fail for all other folder types. Adjust. kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl; kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl; kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl; // Find all extra folders TQStringList folderNames; TQValueList > folderList; kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList); TQValueList >::iterator it; for(it = folderList.begin(); it != folderList.end(); ++it) { FolderStorage *storage = (*it)->storage(); if ( (*it)->folderType() == KMFolderTypeCachedImap ) { KMFolderCachedImap *imapFolder = static_cast( storage ); const TQString attributes = imapFolder->folderAttributes(); if ( attributes.contains( "X-FolderClass" ) ) { if ( !attributes.contains( "X-SpecialFolder" ) || (*it)->location().contains( "@" ) ) { const Scalix::FolderAttributeParser parser( attributes ); if ( !parser.folderClass().isEmpty() ) { FolderContentsType type = Scalix::Utils::scalixIdToContentsType( parser.folderClass() ); imapFolder->setContentsType( type ); folderContentsTypeChanged( *it, type ); } } } } } // If we just created them, they might have been registered as extra folders temporarily. // -> undo that. mExtraFolders.remove( mCalendar->location() ); mExtraFolders.remove( mTasks->location() ); mExtraFolders.remove( mContacts->location() ); mExtraFolders.remove( mNotes->location() ); // END TILL TODO subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true ); subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true ); subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(), mContacts->label(), true, false ); subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false ); } TDEConfig *config = kmkernel->config(); config->setGroup("Resource UINames"); *KMailICalIfaceImpl::mSubResourceUINamesMap = config->entryMap( "Resource UINames" ); reloadFolderTree(); } void KMailICalIfaceImpl::slotCheckDone() { TQString parentName = GlobalSettings::self()->theIMAPResourceFolderParent(); KMFolder* folderParent = kmkernel->findFolderById( parentName ); //kdDebug(5006) << k_funcinfo << " folderParent=" << folderParent << endl; if ( folderParent ) // cool it exists now { KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() ); if ( account ) disconnect( account, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), this, TQT_SLOT( slotCheckDone() ) ); readConfig(); } } KMFolder* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType ) { // Figure out what type of folder this is supposed to be KMFolderType type = mFolderType; if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir; KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType; //kdDebug(5006) << "KMailICalIfaceImpl::initFolder " << folderName( itemType ) << endl; // Find the folder StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType ); // deal with multiple default groupware folders if ( result.folders.count() > 1 && result.found == StandardFolderSearchResult::FoundAndStandard ) { TQStringList labels; for ( TQValueList::ConstIterator it = result.folders.begin(); it != result.folders.end(); ++it ) labels << (*it)->prettyURL(); const TQString selected = KInputDialog::getItem( i18n("Default folder"), i18n("There are multiple %1 default folders, please choose one:") .arg( localizedDefaultFolderName( contentsType ) ), labels ); if ( !selected.isEmpty() ) result.folder = result.folders[ labels.findIndex( selected ) ]; } KMFolder* folder = result.folder; if ( !folder ) { // The folder isn't there yet - create it folder = mFolderParentDir->createFolder( localizedDefaultFolderName( contentsType ), false, type ); if( mFolderType == KMFolderTypeImap ) { KMFolderImap* parentFolder = static_cast( mFolderParent->storage() ); parentFolder->createFolder( localizedDefaultFolderName( contentsType ) ); static_cast( folder->storage() )->setAccount( parentFolder->account() ); } // Groupware folder created, use the global setting for storage format setStorageFormat( folder, globalStorageFormat() ); } else { FolderInfo info = readFolderInfo( folder ); mFolderInfoMap.insert( folder, info ); //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location() << endl; } if( folder->canAccess() != 0 ) { KMessageBox::sorry(0, i18n("You do not have read/write permission to your %1 folder.") .arg( folderName( itemType ) ) ); return 0; } folder->storage()->setContentsType( contentsType ); folder->setSystemFolder( true ); folder->storage()->writeConfig(); folder->open("ifacefolder"); connectFolder( folder ); return folder; } KMFolder* KMailICalIfaceImpl::initScalixFolder( KMail::FolderContentsType contentsType ) { // Figure out what type of folder this is supposed to be KMFolderType type = mFolderType; if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir; KMFolder* folder = 0; // Find all extra folders TQStringList folderNames; TQValueList > folderList; Q_ASSERT( kmkernel ); Q_ASSERT( kmkernel->dimapFolderMgr() ); kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList); TQValueList >::iterator it = folderList.begin(); for(; it != folderList.end(); ++it) { FolderStorage *storage = (*it)->storage(); if ( (*it)->folderType() == KMFolderTypeCachedImap ) { KMFolderCachedImap *imapFolder = static_cast( storage ); const TQString attributes = imapFolder->folderAttributes(); if ( attributes.contains( "X-SpecialFolder" ) ) { const Scalix::FolderAttributeParser parser( attributes ); if ( contentsType == Scalix::Utils::scalixIdToContentsType( parser.folderClass() ) ) { folder = *it; break; } } } } if ( !folder ) { return 0; } else { FolderInfo info = readFolderInfo( folder ); mFolderInfoMap.insert( folder, info ); //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location() << endl; } if( folder->canAccess() != 0 ) { KMessageBox::sorry(0, i18n("You do not have read/write permission to your folder.") ); return 0; } folder->storage()->setContentsType( contentsType ); folder->setSystemFolder( true ); folder->storage()->writeConfig(); folder->open( "scalixfolder" ); connectFolder( folder ); return folder; } void KMailICalIfaceImpl::connectFolder( KMFolder* folder ) { // avoid multiple connections disconnect( folder, TQT_SIGNAL( msgAdded( KMFolder*, TQ_UINT32 ) ), this, TQT_SLOT( slotIncidenceAdded( KMFolder*, TQ_UINT32 ) ) ); disconnect( folder, TQT_SIGNAL( msgRemoved( KMFolder*, TQ_UINT32 ) ), this, TQT_SLOT( slotIncidenceDeleted( KMFolder*, TQ_UINT32 ) ) ); disconnect( folder, TQT_SIGNAL( expunged( KMFolder* ) ), this, TQT_SLOT( slotRefreshFolder( KMFolder* ) ) ); disconnect( folder->storage(), TQT_SIGNAL( readOnlyChanged( KMFolder* ) ), this, TQT_SLOT( slotFolderPropertiesChanged( KMFolder* ) ) ); disconnect( folder, TQT_SIGNAL( nameChanged() ), this, TQT_SLOT( slotFolderRenamed() ) ); disconnect( folder->storage(), TQT_SIGNAL( locationChanged( const TQString&, const TQString&) ), this, TQT_SLOT( slotFolderLocationChanged( const TQString&, const TQString&) ) ); // Setup the signals to listen for changes connect( folder, TQT_SIGNAL( msgAdded( KMFolder*, TQ_UINT32 ) ), this, TQT_SLOT( slotIncidenceAdded( KMFolder*, TQ_UINT32 ) ) ); connect( folder, TQT_SIGNAL( msgRemoved( KMFolder*, TQ_UINT32 ) ), this, TQT_SLOT( slotIncidenceDeleted( KMFolder*, TQ_UINT32 ) ) ); connect( folder, TQT_SIGNAL( expunged( KMFolder* ) ), this, TQT_SLOT( slotRefreshFolder( KMFolder* ) ) ); connect( folder->storage(), TQT_SIGNAL( readOnlyChanged( KMFolder* ) ), this, TQT_SLOT( slotFolderPropertiesChanged( KMFolder* ) ) ); connect( folder, TQT_SIGNAL( nameChanged() ), this, TQT_SLOT( slotFolderRenamed() ) ); connect( folder->storage(), TQT_SIGNAL( locationChanged( const TQString&, const TQString&) ), this, TQT_SLOT( slotFolderLocationChanged( const TQString&, const TQString&) ) ); } static void cleanupFolder( KMFolder* folder, KMailICalIfaceImpl* _this ) { if( folder ) { folder->setSystemFolder( false ); folder->disconnect( _this ); folder->close("ifacefolder"); } } void KMailICalIfaceImpl::cleanup() { cleanupFolder( mContacts, this ); cleanupFolder( mCalendar, this ); cleanupFolder( mNotes, this ); cleanupFolder( mTasks, this ); cleanupFolder( mJournals, this ); mContacts = mCalendar = mNotes = mTasks = mJournals = 0; } TQString KMailICalIfaceImpl::folderPixmap( KFolderTreeItem::Type type ) const { if( !mUseResourceIMAP ) return TQString(); if( type == KFolderTreeItem::Contacts ) return TQString::fromLatin1( "kmgroupware_folder_contacts" ); else if( type == KFolderTreeItem::Calendar ) return TQString::fromLatin1( "kmgroupware_folder_calendar" ); else if( type == KFolderTreeItem::Notes ) return TQString::fromLatin1( "kmgroupware_folder_notes" ); else if( type == KFolderTreeItem::Tasks ) return TQString::fromLatin1( "kmgroupware_folder_tasks" ); else if( type == KFolderTreeItem::Journals ) return TQString::fromLatin1( "kmgroupware_folder_journals" ); return TQString(); } static void reloadFolderTree() { // Make the folder tree show the icons or not kmkernel->folderMgr()->contentsChanged(); } // This is a very light-weight and fast 'parser' to retrieve // a data entry from a vCal taking continuation lines // into account static void vPartMicroParser( const TQString& str, TQString& s ) { TQString line; uint len = str.length(); for( uint i=0; i findFolderByAnnotation( KMFolderDir* folderParentDir, const TQString& annotation ) { TQValueList rv; TQPtrListIterator it( *folderParentDir ); for ( ; it.current(); ++it ) { if ( !it.current()->isDir() ) { KMFolder* folder = static_cast( it.current() ); if ( folder->folderType() == KMFolderTypeCachedImap ) { TQString folderAnnotation = static_cast( folder->storage() )->annotationFolderType(); //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl; if ( folderAnnotation == annotation ) rv.append( folder ); } } } return rv; } KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType ) { if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ) { // Look for a folder with an annotation like "event.default" TQValueList folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) + ".default" ); if ( !folders.isEmpty() ) return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundAndStandard ); // Fallback: look for a folder with an annotation like "event" folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) ); if ( !folders.isEmpty() ) return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundByType ); // Fallback: look for the folder by name (we'll need to change its type) KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) ); if ( node && !node->isDir() ) return StandardFolderSearchResult( static_cast( node ), StandardFolderSearchResult::FoundByName ); kdDebug(5006) << "findStandardResourceFolder: found no resource folder for " << s_folderContentsType[contentsType].annotation << endl; return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound ); } else // icalvcard: look up standard resource folders by name { KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType; unsigned int folderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage(); if( folderLanguage > 3 ) folderLanguage = 0; KMFolderNode* node = folderParentDir->hasNamedFolder( folderName( itemType, folderLanguage ) ); if ( !node || node->isDir() ) return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound ); return StandardFolderSearchResult( static_cast( node ), StandardFolderSearchResult::FoundAndStandard ); } } /* We treat all folders as relevant wrt alarms for which we have Administer * rights or for which the "Incidences relevant for everyone" annotation has * been set. It can be reasonably assumed that those are "ours". All local folders * must be ours anyhow. */ bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder *folder ) { bool administerRights = true; bool relevantForOwner = true; bool relevantForEveryone = false; if ( folder->folderType() == KMFolderTypeImap ) { const KMFolderImap *imapFolder = static_cast( folder->storage() ); administerRights = imapFolder->userRightsState() != KMail::ACLJobs::Ok || imapFolder->userRights() & KMail::ACLJobs::Administer; } if ( folder->folderType() == KMFolderTypeCachedImap ) { const KMFolderCachedImap *dimapFolder = static_cast( folder->storage() ); administerRights = dimapFolder->userRightsState() != KMail::ACLJobs::Ok || dimapFolder->userRights() & KMail::ACLJobs::Administer; relevantForOwner = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor () == KMFolderCachedImap::IncForAdmins ); relevantForEveryone = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor() == KMFolderCachedImap::IncForReaders ); } #if 0 kdDebug(5006) << k_funcinfo << endl; kdDebug(5006) << "Folder: " << folder->label() << " has administer rights: " << administerRights << endl; kdDebug(5006) << "and is relevant for owner: " << relevantForOwner << endl; kdDebug(5006) << "and relevant for everyone: " << relevantForEveryone << endl; #endif return ( administerRights && relevantForOwner ) || relevantForEveryone; } void KMailICalIfaceImpl::setResourceQuiet(bool q) { mResourceQuiet = q; } bool KMailICalIfaceImpl::isResourceQuiet() const { return mResourceQuiet; } bool KMailICalIfaceImpl::addSubresource( const TQString& resource, const TQString& parent, const TQString& contentsType ) { kdDebug(5006) << "Adding subresource to parent: " << parent << " with name: " << resource << endl; kdDebug(5006) << "contents type: " << contentsType << endl; KMFolder *folder = findResourceFolder( parent ); KMFolderDir *parentFolderDir = !parent.isEmpty() && folder ? folder->createChildFolder(): mFolderParentDir; if ( !parentFolderDir || parentFolderDir->hasNamedFolder( resource ) ) return false; TQString msg; if ( parentFolderDir->owner() && !parentFolderDir->owner()->isValidName( resource, msg ) ) { KMessageBox::error( 0, msg ); return false; } KMFolderType type = mFolderType; if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir; KMFolder* newFolder = parentFolderDir->createFolder( resource, false, type ); if ( !newFolder ) return false; if( mFolderType == KMFolderTypeImap ) static_cast( folder->storage() )->createFolder( resource ); StorageFormat defaultFormat = GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard; setStorageFormat( newFolder, folder ? storageFormat( folder ) : defaultFormat ); newFolder->storage()->setContentsType( folderContentsType( contentsType ) ); newFolder->storage()->writeConfig(); newFolder->open( "ical_subresource" ); connectFolder( newFolder ); reloadFolderTree(); return true; } bool KMailICalIfaceImpl::removeSubresource( const TQString& location ) { kdDebug(5006) << k_funcinfo << endl; KMFolder *folder = findResourceFolder( location ); // We don't allow the default folders to be deleted, so check for // those first. It would be nicer to produce a more meaningful error, // or prevent deletion of the builtin folders from the gui already. if ( !folder || isStandardResourceFolder( folder ) ) return false; // the folder will be removed, which implies closed, so make sure // nothing is using it anymore first subresourceDeleted( folderContentsType( folder->storage()->contentsType() ), location ); mExtraFolders.remove( location ); folder->disconnect( this ); if ( folder->folderType() == KMFolderTypeImap ) kmkernel->imapFolderMgr()->remove( folder ); else if ( folder->folderType() == KMFolderTypeCachedImap ) { // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2) KMFolderCachedImap* storage = static_cast( folder->storage() ); KMAcctCachedImap* acct = storage->account(); if ( acct ) acct->addDeletedFolder( folder ); kmkernel->dimapFolderMgr()->remove( folder ); } return true; } void KMailICalIfaceImpl::syncFolder(KMFolder * folder) const { if ( kmkernel->isOffline() || !GlobalSettings::immediatlySyncDIMAPOnGroupwareChanges() ) return; KMFolderCachedImap *dimapFolder = dynamic_cast( folder->storage() ); if ( !dimapFolder ) return; // check if the folder exists already, otherwise sync its parent as well to create it if ( dimapFolder->imapPath().isEmpty() ) { if ( folder->parent() && folder->parent()->owner() ) syncFolder( folder->parent()->owner() ); else return; } dimapFolder->account()->processNewMailInFolder( folder ); } #include "kmailicalifaceimpl.moc"