2 This file is part of KMail.
4 Copyright (c) 2003 Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
5 Copyright (c) 2003 - 2004 Bo Thorsen <bo@sonofthor.dk>
6 Copyright (c) 2004 Till Adam <adam@kde.org>
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
23 In addition, as a special exception, the copyright holders give
24 permission to link the code of this program with any edition of
25 the Qt library by Trolltech AS, Norway (or with modified versions
26 of Qt that use the same license as Qt), and distribute linked
27 combinations including the two. You must obey the GNU General
28 Public License in all respects for all of the code used other than
29 Qt. If you modify this file, you may extend this exception to
30 your version of the file, but you are not obligated to do so. If
31 you do not wish to do so, delete this exception statement from
35 #include "kmailicalifaceimpl.h"
37 #include "kmfolderdir.h"
38 #include "kmgroupware.h"
39 #include "kmfoldermgr.h"
40 #include "kmcommands.h"
41 #include "kmfolderindex.h"
42 #include "kmmsgdict.h"
43 #include "kmmsgpart.h"
46 using KMail::AccountManager
;
47 #include "kmfolderimap.h"
48 #include "globalsettings.h"
49 #include "accountmanager.h"
50 #include "kmfoldercachedimap.h"
51 #include "kmacctcachedimap.h"
55 #include <mimelib/enum.h>
56 #include <mimelib/utility.h>
57 #include <mimelib/body.h>
58 #include <mimelib/mimepp.h>
63 #include <QDBusMessage>
64 #include <QDBusConnection>
67 #include <kiconloader.h>
68 #include <kinputdialog.h>
69 #include <kmessagebox.h>
72 #include <ktemporaryfile.h>
73 #include <kconfiggroup.h>
74 #include "groupwareadaptor.h"
76 using namespace KMail
;
78 // Local helper methods
79 static void vPartMicroParser( const QString
& str
, QString
& s
);
80 static void reloadFolderTree();
82 // The index in this array is the KMail::FolderContentsType enum
83 typedef struct _FolderContentsDescriptor
{
84 const char* contentsTypeStr
; // the string used in the D-Bus interface
86 KPIM::FolderTreeWidgetItem::FolderType treeItemType
;
87 const char* annotation
;
88 const char* translatedName
;
89 } FolderContentsDescriptor
;
91 static const FolderContentsDescriptor s_folderContentsType
[] =
94 "Mail", "application/x-vnd.kolab.mail",
95 KPIM::FolderTreeWidgetItem::Other
,
96 "mail", I18N_NOOP2( "type of folder content", "Mail" )
99 "Calendar", "application/x-vnd.kolab.event",
100 KPIM::FolderTreeWidgetItem::Calendar
,
101 "event", I18N_NOOP2( "type of folder content", "Calendar" )
104 "Contact", "application/x-vnd.kolab.contact",
105 KPIM::FolderTreeWidgetItem::Contacts
,
106 "contact", I18N_NOOP2( "type of folder content", "Contacts" )
109 "Note", "application/x-vnd.kolab.note",
110 KPIM::FolderTreeWidgetItem::Notes
,
111 "note", I18N_NOOP2( "type of folder content", "Notes" )
114 "Task", "application/x-vnd.kolab.task",
115 KPIM::FolderTreeWidgetItem::Tasks
,
116 "task", I18N_NOOP2( "type of folder content", "Tasks" )
119 "Journal", "application/x-vnd.kolab.journal",
120 KPIM::FolderTreeWidgetItem::Journals
,
121 "journal", I18N_NOOP2( "type of folder content", "Journal" )
125 static QString
folderContentsType( KMail::FolderContentsType type
)
127 return s_folderContentsType
[type
].contentsTypeStr
;
130 static QString
folderKolabMimeType( KMail::FolderContentsType type
)
132 return s_folderContentsType
[type
].mimetype
;
135 StorageFormat
KMailICalIfaceImpl::globalStorageFormat() const {
136 return GlobalSettings::self()->theIMAPResourceStorageFormat()
137 == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
? StorageXML
: StorageIcalVcard
;
140 static KMail::FolderContentsType
folderContentsType( const QString
& type
)
142 for ( uint i
= 0 ; i
< sizeof s_folderContentsType
/ sizeof *s_folderContentsType
; ++i
)
143 if ( type
== s_folderContentsType
[i
].contentsTypeStr
)
144 return static_cast<KMail::FolderContentsType
>( i
);
145 return KMail::ContentsTypeMail
;
148 static QString
localizedDefaultFolderName( KMail::FolderContentsType type
)
150 return i18nc( "type of folder content", s_folderContentsType
[type
].translatedName
);
153 const char* KMailICalIfaceImpl::annotationForContentsType( KMail::FolderContentsType type
)
155 return s_folderContentsType
[type
].annotation
;
158 ExtraFolder::ExtraFolder( KMFolder
* f
)
161 folder
->open("kmailicaliface::extrafolder");
164 ExtraFolder::~ExtraFolder()
167 folder
->close("kmailicaliface::extrafolder");
172 This interface has three parts to it - libkcal interface;
173 kmail interface; and helper functions.
175 The libkcal interface and the kmail interface have the same three
176 methods: add, delete and refresh. The only difference is that the
177 libkcal interface is used from the IMAP resource in libkcal and
178 the kmail interface is used from the groupware object in kmail.
181 KMailICalIfaceImpl::KMailICalIfaceImpl()
182 : mContacts( 0 ), mCalendar( 0 ), mNotes( 0 ), mTasks( 0 ), mJournals( 0 ),
183 mFolderLanguage( 0 ), mFolderParentDir( 0 ), mFolderType( KMFolderTypeUnknown
),
184 mUseResourceIMAP( false ), mResourceQuiet( false ), mHideFolders( true )
186 setObjectName( "KMailICalIFaceImpl" );
188 // Listen to config changes
189 connect( kmkernel
, SIGNAL( configChanged() ), this, SLOT( readConfig() ) );
190 connect( kmkernel
, SIGNAL( folderRemoved( KMFolder
* ) ),
191 this, SLOT( slotFolderRemoved( KMFolder
* ) ) );
194 KMailICalIfaceImpl::~KMailICalIfaceImpl()
196 qDeleteAll( mExtraFolders
);
197 qDeleteAll( mAccumulators
);
198 mAccumulators
.clear();
199 mExtraFolders
.clear();
202 void KMailICalIfaceImpl::registerWithDBus()
204 KMail::registerGroupwareTypes();
205 QDBusConnection::sessionBus().registerObject( "/Groupware", this, QDBusConnection::ExportAdaptors
);
206 new GroupwareAdaptor( this );
209 /* libkcal part of the interface, called from the resources using this
210 * when incidences are added or deleted */
212 // Helper function to find an attachment of a given mimetype
213 // Can't use KMMessage::findDwBodyPart since it only works with known mimetypes.
214 static DwBodyPart
* findBodyPartByMimeType( const KMMessage
& msg
, const char* sType
, const char* sSubtype
, bool startsWith
= false )
216 // quickly searching for our message part: since Kolab parts are
217 // top-level parts we do *not* have to travel into embedded multiparts
218 DwBodyPart
* part
= msg
.getFirstDwBodyPart();
220 // kDebug() << part->Headers().ContentType().TypeStr().c_str()
221 // << part->Headers().ContentType().SubtypeStr().c_str();
222 if ( part
->hasHeaders() ) {
223 DwMediaType
& contentType
= part
->Headers().ContentType();
225 if ( contentType
.TypeStr() == sType
226 && QString( contentType
.SubtypeStr().c_str() ).startsWith( sSubtype
) )
230 if ( contentType
.TypeStr() == sType
231 && contentType
.SubtypeStr() == sSubtype
)
239 // Helper function to find an attachment with a given filename
240 static DwBodyPart
* findBodyPart( const KMMessage
& msg
, const QString
& attachmentName
)
242 // quickly searching for our message part: since Kolab parts are
243 // top-level parts we do *not* have to travel into embedded multiparts
244 for ( DwBodyPart
* part
= msg
.getFirstDwBodyPart(); part
; part
= part
->Next() ) {
245 //kDebug() << "findBodyPart: -" << part->Headers().ContentDisposition().Filename().c_str();
246 if ( part
->hasHeaders()
247 && attachmentName
== part
->Headers().ContentDisposition().Filename().c_str() )
249 if ( part
->hasHeaders() && attachmentName
== part
->Headers().ContentType().Name().c_str() )
256 static void debugBodyParts( const char* foo
, const KMMessage
& msg
)
258 kDebug() << "--debugBodyParts" << foo
<<"--";
259 for ( DwBodyPart
* part
= msg
.getFirstDwBodyPart(); part
; part
= part
->Next() ) {
260 if ( part
->hasHeaders() ) {
261 kDebug() << " bodypart:" << part
;
262 kDebug() << part
->Headers().AsString().c_str();
265 kDebug() << " part" << part
<<" has no headers";
269 inline static void debugBodyParts( const char*, const KMMessage
& ) {}
273 // Add (or overwrite, resp.) an attachment in an existing mail,
274 // attachments must be local files, they are identified by their names.
275 // If lookupByName if false the attachment to replace is looked up by mimetype.
276 // return value: wrong if attachment could not be added/updated
277 bool KMailICalIfaceImpl::updateAttachment( KMMessage
& msg
,
278 const QString
& attachmentURL
,
279 const QString
& attachmentName
,
280 const QString
& attachmentMimetype
,
283 kDebug() << attachmentURL
;
287 KUrl
url( attachmentURL
);
288 if ( url
.isValid() && url
.isLocalFile() ) {
289 const QString
fileName( url
.path() );
290 QFile
file( fileName
);
291 if( file
.open( QIODevice::ReadOnly
) ) {
292 QByteArray rawData
= file
.readAll();
295 // create the new message part with data read from temp file
296 KMMessagePart msgPart
;
297 msgPart
.setName( attachmentName
);
299 const int iSlash
= attachmentMimetype
.indexOf('/');
300 const QByteArray sType
= attachmentMimetype
.left( iSlash
).toLatin1();
301 const QByteArray sSubtype
= attachmentMimetype
.mid( iSlash
+1 ).toLatin1();
302 msgPart
.setTypeStr( sType
);
303 msgPart
.setSubtypeStr( sSubtype
);
304 QByteArray
ctd("attachment;\n filename=\"");
305 ctd
.append( attachmentName
.toLatin1() );
307 msgPart
.setContentDisposition( ctd
);
309 msgPart
.setBodyAndGuessCte( rawData
, dummy
);
310 msgPart
.setPartSpecifier( fileName
);
312 DwBodyPart
* newPart
= msg
.createDWBodyPart( &msgPart
);
313 // This whole method is a bit special. We mix code for writing and code for reading.
314 // E.g. we need to parse the content-disposition again for ContentDisposition().Filename()
316 newPart
->Headers().ContentDisposition().Parse();
318 DwBodyPart
* part
= lookupByName
? findBodyPart( msg
, attachmentName
)
319 : findBodyPartByMimeType( msg
, sType
, sSubtype
);
321 // Make sure the replacing body part is pointing
322 // to the same next part as the original body part.
323 newPart
->SetNext( part
->Next() );
324 // call DwBodyPart::operator =
325 // which calls DwEntity::operator =
328 msg
.setNeedsAssembly();
329 kDebug() << "Attachment" << attachmentName
<<" updated.";
331 msg
.addDwBodyPart( newPart
);
332 kDebug() << "Attachment" << attachmentName
<<" added.";
336 kDebug() << "Attachment" << attachmentURL
<<" can not be read.";
339 kDebug() << "Attachment" << attachmentURL
<<" not a local file.";
345 // Look for the attachment with the right mimetype
346 bool KMailICalIfaceImpl::kolabXMLFoundAndDecoded( const KMMessage
& msg
, const QString
& mimetype
, QString
& s
)
348 const int iSlash
= mimetype
.indexOf('/');
349 const QByteArray sType
= mimetype
.left( iSlash
).toLatin1();
350 const QByteArray sSubtype
= mimetype
.mid( iSlash
+1 ).toLatin1();
351 DwBodyPart
* part
= findBodyPartByMimeType( msg
, sType
, sSubtype
, true /* starts with sSubtype, to accept application/x-vnd.kolab.contact.distlist */ );
353 KMMessagePart msgPart
;
354 KMMessage::bodyPart(part
, &msgPart
);
355 s
= msgPart
.bodyToUnicode( QTextCodec::codecForName( "utf8" ) );
361 // Delete an attachment in an existing mail.
362 // return value: wrong if attachment could not be deleted
364 // This code could be optimized: for now we just replace
365 // the attachment by an empty dummy attachment since Mimelib
366 // does not provide an option for deleting attachments yet.
367 bool KMailICalIfaceImpl::deleteAttachment( KMMessage
& msg
,
368 const QString
& attachmentName
)
370 kDebug() << attachmentName
;
374 // quickly searching for our message part: since Kolab parts are
375 // top-level parts we do *not* have to travel into embedded multiparts
376 DwBodyPart
* part
= findBodyPart( msg
, attachmentName
);
378 msg
.getTopLevelPart()->Body().RemoveBodyPart( part
);
380 msg
.setNeedsAssembly();
381 kDebug() << "Attachment deleted.";
386 kDebug() << "Attachment" << attachmentName
<<" not found.";
392 static void setIcalVcardContentTypeHeader( KMMessage
*msg
, KMail::FolderContentsType t
, KMFolder
*folder
)
394 KMAcctCachedImap::GroupwareType groupwareType
= KMAcctCachedImap::GroupwareKolab
;
396 KMFolderCachedImap
*imapFolder
= dynamic_cast<KMFolderCachedImap
*>( folder
->storage() );
398 groupwareType
= imapFolder
->account()->groupwareType();
400 msg
->setType( DwMime::kTypeText
);
401 if ( t
== KMail::ContentsTypeCalendar
|| t
== KMail::ContentsTypeTask
402 || t
== KMail::ContentsTypeJournal
) {
403 msg
->setSubtype( DwMime::kSubtypeVCal
);
405 if ( groupwareType
== KMAcctCachedImap::GroupwareKolab
)
406 msg
->setHeaderField("Content-Type",
407 "text/calendar; method=REQUEST; charset=\"utf-8\"");
408 else if ( groupwareType
== KMAcctCachedImap::GroupwareScalix
)
409 msg
->setHeaderField("Content-Type",
410 "text/calendar; method=PUBLISH; charset=\"UTF-8\"");
412 } else if ( t
== KMail::ContentsTypeContact
) {
413 msg
->setSubtype( DwMime::kSubtypeXVCard
);
414 if ( groupwareType
== KMAcctCachedImap::GroupwareKolab
)
415 msg
->setHeaderField( "Content-Type", "Text/X-VCard; charset=\"utf-8\"" );
416 else if ( groupwareType
== KMAcctCachedImap::GroupwareScalix
)
417 msg
->setHeaderField( "Content-Type", "application/scalix-properties; charset=\"UTF-8\"" );
419 kWarning() <<"Attempt to write non-groupware contents to folder";
423 static void setXMLContentTypeHeader( KMMessage
*msg
, const QString
&plainTextBody
)
425 // add a first body part to be displayed by all mailer
426 // than can NOT display Kolab data: no matter if these
427 // mailers are MIME compliant or not
428 KMMessagePart firstPart
;
429 firstPart
.setType( DwMime::kTypeText
);
430 firstPart
.setSubtype( DwMime::kSubtypePlain
);
431 msg
->removeHeaderField( "Content-Type" );
432 msg
->setType( DwMime::kTypeMultipart
);
433 msg
->setSubtype( DwMime::kSubtypeMixed
);
434 msg
->headers().ContentType().CreateBoundary( 0 );
435 msg
->headers().ContentType().Assemble();
436 firstPart
.setBodyFromUnicode( plainTextBody
);
437 msg
->addBodyPart( &firstPart
);
440 // Store a new entry that was received from the resource
441 quint32
KMailICalIfaceImpl::addIncidenceKolab( KMFolder
& folder
,
442 const QString
& subject
,
443 const QString
& plainTextBody
,
444 const KMail::CustomHeader::List
& customHeaders
,
445 const QStringList
& attachmentURLs
,
446 const QStringList
& attachmentNames
,
447 const QStringList
& attachmentMimetypes
)
449 kDebug() << attachmentNames
;
452 bool bAttachOK
= true;
454 // Make a new message for the incidence
455 KMMessage
* msg
= new KMMessage();
457 msg
->setSubject( subject
);
458 msg
->setAutomaticFields( true );
460 foreach ( const CustomHeader
& header
, customHeaders
)
461 msg
->setHeaderField( header
.name
, header
.value
);
462 // In case of the ical format, simply add the plain text content with the
463 // right content type
464 if ( storageFormat( &folder
) == StorageXML
) {
465 setXMLContentTypeHeader( msg
, plainTextBody
);
466 } else if ( storageFormat( &folder
) == StorageIcalVcard
) {
467 const KMail::FolderContentsType t
= folder
.storage()->contentsType();
468 setIcalVcardContentTypeHeader( msg
, t
, &folder
);
469 msg
->setBodyEncoded( plainTextBody
.toUtf8() );
471 kWarning() << "Attempt to write to folder with unknown storage type";
474 Q_ASSERT( attachmentMimetypes
.count() == attachmentURLs
.count() );
475 Q_ASSERT( attachmentNames
.count() == attachmentURLs
.count() );
476 // Add all attachments by reading them from their temp. files
477 QStringList::ConstIterator itmime
= attachmentMimetypes
.begin();
478 QStringList::ConstIterator iturl
= attachmentURLs
.begin();
479 for( QStringList::ConstIterator itname
= attachmentNames
.begin();
480 itname
!= attachmentNames
.end()
481 && itmime
!= attachmentMimetypes
.end()
482 && iturl
!= attachmentURLs
.end();
483 ++itname
, ++iturl
, ++itmime
){
484 bool byname
= !(*itmime
).startsWith( "application/x-vnd.kolab." );
485 if( !updateAttachment( *msg
, *iturl
, *itname
, *itmime
, byname
) ){
486 kWarning() << "Attachment error, can not add Incidence.";
493 // Mark the message as read and store it in the folder
494 msg
->cleanupHeader();
495 //debugBodyParts( "after cleanup", *msg );
497 kDebug( StorageDebug
) << "***<msg>***" << msg
->asString() << "***</msg>***";
498 if ( folder
.addMsg( msg
) == 0 )
500 sernum
= msg
->getMsgSerNum();
501 kDebug() << "Message done and saved. Sernum:" << sernum
;
503 //debugBodyParts( "after addMsg", *msg );
504 addFolderChange( &folder
,ContentsChanged
);
505 syncFolder( &folder
);
507 kError() << "Message *NOT* saved!";
512 bool KMailICalIfaceImpl::deleteIncidenceKolab( const QString
& resource
,
515 // Find the message from the serial number and delete it.
516 if( !mUseResourceIMAP
)
519 kDebug() << resource
<< ", " << sernum
;
522 KMFolder
* f
= findResourceFolder( resource
);
524 kError() << resource
<< ": Not an IMAP resource folder";
530 KMMessage
* msg
= findMessageBySerNum( sernum
, f
);
532 // Message found - delete it and return happy
537 kDebug() << "Message not found, cannot remove serNum" << sernum
;
543 int KMailICalIfaceImpl::incidencesKolabCount( const QString
& mimetype
,
544 const QString
& resource
)
546 Q_UNUSED( mimetype
); // honoring that would be too slow...
548 if( !mUseResourceIMAP
)
551 KMFolder
* f
= findResourceFolder( resource
);
553 kError() <<"incidencesKolab(" << resource
<<") : Not an IMAP resource folder";
557 f
->open( "kolabcount" );
559 f
->close( "kolabcount" );
560 kDebug() << resource
<< " returned" << n
;
564 KMail::SernumDataPair::List
KMailICalIfaceImpl::incidencesKolab( const QString
& mimetype
,
565 const QString
& resource
,
569 /// Get the mimetype attachments from this folder. Returns a
570 /// QMap with serialNumber/attachment pairs.
571 /// (serial numbers of the mail are provided for easier later update)
573 KMail::SernumDataPair::List aMap
;
574 if( !mUseResourceIMAP
)
577 KMFolder
* f
= findResourceFolder( resource
);
579 kError() <<"incidencesKolab(" << resource
<<") : Not an IMAP resource folder";
583 f
->open( "incidences" );
585 int stopIndex
= nbMessages
== -1 ? f
->count() :
586 qMin( f
->count(), startIndex
+ nbMessages
);
587 kDebug() << mimetype
<< "," << resource
<< " from" << startIndex
<< "to" << stopIndex
;
589 for(int i
= startIndex
; i
< stopIndex
; ++i
) {
591 bool unget
= !f
->isMessage(i
);
592 KMMessage
* msg
= f
->getMsg( i
);
594 KMMessage
* msg
= f
->storage()->readTemporaryMsg(i
);
597 const int iSlash
= mimetype
.indexOf('/');
598 const QByteArray sType
= mimetype
.left( iSlash
).toLatin1();
599 const QByteArray sSubtype
= mimetype
.mid( iSlash
+1 ).toLatin1();
600 if ( sType
.isEmpty() || sSubtype
.isEmpty() ) {
601 kError() << mimetype
<<" not an type/subtype combination";
603 DwBodyPart
* dwPart
= findBodyPartByMimeType( *msg
, sType
, sSubtype
);
605 KMMessagePart msgPart
;
606 KMMessage::bodyPart(dwPart
, &msgPart
);
607 aMap
<< SernumDataPair(msg
->getMsgSerNum(), msgPart
.bodyToUnicode( QTextCodec::codecForName( "utf8" ) ));
609 // Check if the whole message has the right types. This is what
610 // happens in the case of ical storage, where the whole mail is
612 const QByteArray
type( msg
->typeStr() );
613 const QByteArray
subtype( msg
->subtypeStr() );
614 if (type
.toLower() == sType
&& subtype
.toLower() == sSubtype
) {
615 aMap
<< SernumDataPair( msg
->getMsgSerNum(), msg
->bodyToUnicode() );
617 // This is *not* an error: it may be that not all of the messages
618 // have a message part that is matching the wanted MIME type
622 if( unget
) f
->unGetMsg(i
);
624 if ( msg
->transferInProgress() )
625 msg
->deleteWhenUnused();
631 f
->close( "incidences" );
636 /* Called when a message that was downloaded from an online imap folder
637 * arrives. Needed when listing incidences on online account folders. */
638 // TODO: Till, port me
639 void KMailICalIfaceImpl::slotMessageRetrieved( KMMessage
* msg
)
643 KMFolder
*parent
= msg
->parent();
645 quint32 sernum
= msg
->getMsgSerNum();
647 // do we have an accumulator for this folder?
648 // TODO (till): Remove this accumulator stuff, it is dead code.
649 Accumulator
*ac
= mAccumulators
.value( parent
->location(), 0 );
652 if ( !vPartFoundAndDecoded( msg
, s
) ) return;
653 QString
uid( "UID" );
654 vPartMicroParser( s
, uid
);
655 const quint32 sernum
= msg
->getMsgSerNum();
656 mUIDToSerNum
.insert( uid
, sernum
);
659 /* if this was the last one we were waiting for, tell the resource
660 * about the new incidences and clean up. */
661 //asyncLoadResult( ac->incidences, ac->type, ac->folder );
662 delete mAccumulators
.take( ac
->folder
);
665 /* We are not accumulating for this folder, so this one was added
666 * by KMail. Do your thang. */
667 slotIncidenceAdded( msg
->parent(), msg
->getMsgSerNum() );
670 if ( mTheUnGetMes
.contains( sernum
) ) {
671 mTheUnGetMes
.remove( sernum
);
673 KMFolder
* folder
= 0;
674 KMMsgDict::instance()->getLocation( sernum
, &folder
, &i
);
675 folder
->unGetMsg( i
);
679 static int dimapAccountCount()
681 KMail::AccountManager
*mgr
= kmkernel
->acctMgr();
682 QList
<KMAccount
*>::iterator accountIt
= mgr
->begin();
684 while ( accountIt
!= mgr
->end() ) {
685 KMAccount
*account
= *accountIt
;
687 if ( dynamic_cast<KMAcctCachedImap
*>( account
) )
693 static QString
subresourceLabelForPresentation( const KMFolder
* folder
)
695 QString label
= folder
->prettyUrl();
696 QStringList parts
= label
.split( QChar('/') );
697 // In the common special case of some other user's folder shared with us
698 // the url looks like "Server Name/user/$USERNAME/Folder/Name". Make
699 // those a bit nicer.
700 if ( parts
[1] == QLatin1String("user") ) {
701 QStringList
remainder(parts
);
702 remainder
.pop_front();
703 remainder
.pop_front();
704 remainder
.pop_front();
705 label
= i18n("%1's %2", parts
[2], remainder
.join( QLatin1String("/") ) );
707 // Another special case is our own folders, under the imap INBOX, make
708 // those prettier too
709 const KMFolder
*parent
= folder
;
710 while ( parent
->parent() && parent
->parent()->owner() ) {
711 parent
= parent
->parent()->owner();
712 if ( parent
->isSystemFolder() ) {
713 QStringList
remainder(parts
);
714 remainder
.pop_front();
715 remainder
.pop_front();
716 if ( dimapAccountCount() > 1 ) {
717 label
= i18n( "My %1 (%2)", remainder
.join( QString::fromLatin1("/") ),
718 static_cast<const KMFolderCachedImap
*>( folder
->storage() )->account()->name() );
720 label
= i18n("My %1", remainder
.join( QString::fromLatin1("/") ) );
728 /* list all available subresources */
729 QList
<SubResource
> KMailICalIfaceImpl::subresourcesKolab( const QString
& contentsType
)
731 QList
<SubResource
> subResources
;
733 // Add the default one
734 KMFolder
* f
= folderFromType( contentsType
, QString() );
736 subResources
.append( SubResource( f
->location(), subresourceLabelForPresentation( f
),
737 !f
->isReadOnly(), folderIsAlarmRelevant( f
) ) );
738 kDebug() << "Adding(1) folder" << f
->location()
739 << ( f
->isReadOnly() ? "readonly" : "" );
742 // get the extra ones
743 const KMail::FolderContentsType t
= folderContentsType( contentsType
);
744 QHash
<QString
, KMail::ExtraFolder
*>::iterator it
= mExtraFolders
.begin();
745 for ( ; it
!= mExtraFolders
.end(); ++it
) {
746 f
= it
.value()->folder
;
747 if ( f
&& f
->storage()->contentsType() == t
) {
748 subResources
.append( SubResource( f
->location(), subresourceLabelForPresentation( f
),
749 !f
->isReadOnly(), folderIsAlarmRelevant( f
) ) );
750 kDebug() << "Adding(2) folder" << f
->location()
751 << ( f
->isReadOnly() ? "readonly" : "" );
755 if ( subResources
.isEmpty() )
756 kDebug() << "No folder found for" << contentsType
;
760 bool KMailICalIfaceImpl::triggerSync( const QString
& contentsType
)
763 QList
<SubResource
> folderList
= subresourcesKolab( contentsType
);
764 for ( QList
<SubResource
>::const_iterator
it( folderList
.begin() ),
765 end( folderList
.end() );
767 KMFolder
* const f
= findResourceFolder( (*it
).location
);
769 if ( f
->folderType() == KMFolderTypeImap
|| f
->folderType() == KMFolderTypeCachedImap
) {
770 if ( !kmkernel
->askToGoOnline() ) {
775 if ( f
->folderType() == KMFolderTypeImap
) {
776 KMFolderImap
*imap
= static_cast<KMFolderImap
*>( f
->storage() );
777 imap
->getAndCheckFolder();
778 } else if ( f
->folderType() == KMFolderTypeCachedImap
) {
779 KMFolderCachedImap
* cached
= static_cast<KMFolderCachedImap
*>( f
->storage() );
780 cached
->account()->processNewMailSingleFolder( f
);
786 /* Used by the resource to query whether folders are writable. */
787 bool KMailICalIfaceImpl::isWritableFolder( const QString
& type
,
788 const QString
& resource
)
790 KMFolder
* f
= folderFromType( type
, resource
);
792 // Definitely not writable
795 return !f
->isReadOnly();
798 /* Used by the resource to query the storage format of the folder. */
799 StorageFormat
KMailICalIfaceImpl::storageFormat( const QString
& resource
)
801 StorageFormat format
;
802 KMFolder
* f
= findResourceFolder( resource
);
804 format
= storageFormat( f
);
806 format
= globalStorageFormat();
811 // This finds the message with serial number "sernum", sets the
812 // xml attachments to hold the contents of "xml", and updates all
814 // The mail can have additional attachments, and these are not
815 // touched! They belong to other clients - like Outlook
816 // So we delete all the attachments listed in the
817 // "deletedAttachments" arg, and then update/add all the attachments
818 // given by the urllist attachments.
820 // If the mail does not already exist, id will not be a valid serial
821 // number, and the mail is just added instead. In this case
822 // the deletedAttachments can be forgotten.
824 quint32
KMailICalIfaceImpl::update( const QString
& resource
,
826 const QString
& subject
,
827 const QString
& plainTextBody
,
828 const KMail::CustomHeader::List
& customHeaders
,
829 const QStringList
& attachmentURLs
,
830 const QStringList
& attachmentMimetypes
,
831 const QStringList
& attachmentNames
,
832 const QStringList
& deletedAttachments
)
836 if( !mUseResourceIMAP
)
839 Q_ASSERT( !resource
.isEmpty() );
841 kDebug() << resource
<< "," << sernum
;
842 kDebug() << attachmentURLs
;
843 kDebug() << attachmentMimetypes
;
844 kDebug() << attachmentNames
;
845 kDebug() << "deleted attachments:" << deletedAttachments
;
848 KMFolder
* f
= findResourceFolder( resource
);
850 kError() << "update(" << resource
<<") : Not an IMAP resource folder";
854 f
->open( "ifaceupdate" );
858 msg
= findMessageBySerNum( sernum
, f
);
859 if ( !msg
) return 0;
860 // Message found - make a copy and update it:
861 KMMessage
* newMsg
= new KMMessage( *msg
);
862 newMsg
->setSubject( subject
);
863 foreach ( const CustomHeader
& header
, customHeaders
)
864 newMsg
->setHeaderField( header
.name
, header
.value
);
865 newMsg
->setParent( 0 ); // workaround strange line in KMMsgBase::assign. newMsg is not in any folder yet.
866 // Note that plainTextBody isn't used in this branch. We assume it's still valid from when the mail was created.
868 // Delete some attachments according to list
869 for( QStringList::ConstIterator it
= deletedAttachments
.begin();
870 it
!= deletedAttachments
.end();
872 if( !deleteAttachment( *newMsg
, *it
) ){
873 // Note: It is _not_ an error if an attachment was already deleted.
877 const KMail::FolderContentsType t
= f
->storage()->contentsType();
878 const QByteArray type
= msg
->typeStr();
879 const QByteArray subtype
= msg
->subtypeStr();
880 const bool messageWasIcalVcardFormat
= ( type
.toLower() == "text" &&
881 ( subtype
.toLower() == "calendar" || subtype
.toLower() == "x-vcard" ) );
883 if ( storageFormat( f
) == StorageIcalVcard
) {
884 //kDebug() << " StorageFormatIcalVcard";
885 if ( !messageWasIcalVcardFormat
) {
886 setIcalVcardContentTypeHeader( newMsg
, t
, f
);
888 newMsg
->setBodyEncoded( plainTextBody
.toUtf8() );
889 } else if ( storageFormat( f
) == StorageXML
) {
890 if ( messageWasIcalVcardFormat
) {
891 // this was originally an ical event, but the folder changed to xml,
893 setXMLContentTypeHeader( newMsg
, plainTextBody
);
895 //kDebug() << " StorageFormatXML";
896 // Add all attachments by reading them from their temp. files
897 QStringList::ConstIterator iturl
= attachmentURLs
.begin();
898 QStringList::ConstIterator itmime
= attachmentMimetypes
.begin();
899 QStringList::ConstIterator itname
= attachmentNames
.begin();
901 iturl
!= attachmentURLs
.end()
902 && itmime
!= attachmentMimetypes
.end()
903 && itname
!= attachmentNames
.end();
904 ++iturl
, ++itname
, ++itmime
){
905 bool byname
= !(*itmime
).startsWith( "application/x-vnd.kolab." );
906 if( !updateAttachment( *newMsg
, *iturl
, *itname
, *itmime
, byname
) ){
907 kDebug() << "Attachment error, can not update attachment" << *iturl
;
913 //debugBodyParts( "in update, before cleanup", *newMsg );
915 // This is necessary for the headers to be readable later on
916 newMsg
->cleanupHeader();
918 //debugBodyParts( "in update, after cleanup", *newMsg );
921 if ( f
->addMsg( newMsg
) == 0 ) {
923 rc
= newMsg
->getMsgSerNum();
924 kDebug() << "forget about" << sernum
<<", it's" << rc
<<" now";
926 addFolderChange( f
, ContentsChanged
);
929 // Message not found - store it newly
930 rc
= addIncidenceKolab( *f
, subject
, plainTextBody
, customHeaders
,
933 attachmentMimetypes
);
936 f
->close( "ifaceupdate" );
940 QString
KMailICalIfaceImpl::getAttachment( const QString
& resource
,
942 const QString
& filename
)
944 // This finds the attachment with the filename, saves it to a
945 // temp file and returns a URL to it. It's up to the resource
946 // to delete the tmp file later.
947 if( !mUseResourceIMAP
)
950 kDebug() << resource
<< ", " << sernum
<< ", " << filename
;
953 KMFolder
* f
= findResourceFolder( resource
);
955 kError() << resource
<< ": Not an IMAP resource folder";
958 if ( storageFormat( f
) != StorageXML
) {
959 kError() << resource
<< ": Folder has wrong storage format" << storageFormat( f
);
966 bool quiet
= mResourceQuiet
;
967 mResourceQuiet
= true;
969 KMMessage
* msg
= findMessageBySerNum( sernum
, f
);
971 // Message found - look for the attachment:
973 DwBodyPart
* part
= findBodyPart( *msg
, filename
);
975 // Save the contents of the attachment.
977 msg
->bodyPart( part
, &aPart
);
978 QByteArray
rawData( aPart
.bodyDecodedBinary() );
981 file
.setAutoRemove(false);
983 file
.write( rawData
.data(), rawData
.size() );
985 url
.setPath( file
.fileName() );
991 kDebug() << "Attachment" << filename
<< " not found.";
994 kDebug() << "Message not found.";
997 mResourceQuiet
= quiet
;
1001 QString
KMailICalIfaceImpl::attachmentMimetype( const QString
& resource
,
1003 const QString
& filename
)
1005 if( !mUseResourceIMAP
)
1007 KMFolder
* f
= findResourceFolder( resource
);
1008 if( !f
|| storageFormat( f
) != StorageXML
) {
1009 kError() << "attachmentMimetype(" << resource
<< ") : Wrong folder";
1013 KMMessage
* msg
= findMessageBySerNum( sernum
, f
);
1015 // Message found - look for the attachment:
1016 DwBodyPart
* part
= findBodyPart( *msg
, filename
);
1018 KMMessagePart kmPart
;
1019 msg
->bodyPart( part
, &kmPart
);
1020 return QString( kmPart
.typeStr() ) + '/' + QString( kmPart
.subtypeStr() );
1022 kDebug() << "Attachment " << filename
<< " not found.";
1025 kDebug() << "Message not found.";
1031 QStringList
KMailICalIfaceImpl::listAttachments(const QString
& resource
, quint32 sernum
)
1034 if( !mUseResourceIMAP
)
1038 KMFolder
* f
= findResourceFolder( resource
);
1040 kError() <<"listAttachments(" << resource
<<") : Not an IMAP resource folder";
1043 if ( storageFormat( f
) != StorageXML
) {
1044 kError() <<"listAttachment(" << resource
<<") : Folder has wrong storage format" << storageFormat( f
);
1048 KMMessage
* msg
= findMessageBySerNum( sernum
, f
);
1050 for ( DwBodyPart
* part
= msg
->getFirstDwBodyPart(); part
; part
= part
->Next() ) {
1051 if ( part
->hasHeaders() ) {
1053 DwMediaType
& contentType
= part
->Headers().ContentType();
1054 if ( QString( contentType
.SubtypeStr().c_str() ).startsWith( "x-vnd.kolab." )
1055 || QString( contentType
.SubtypeStr().c_str() ).contains( "tnef" ) )
1057 if ( !part
->Headers().ContentDisposition().Filename().empty() )
1058 name
= part
->Headers().ContentDisposition().Filename().c_str();
1059 else if ( !contentType
.Name().empty() )
1060 name
= contentType
.Name().c_str();
1061 if ( !name
.isEmpty() )
1066 kDebug() << "Message not found.";
1073 // ============================================================================
1075 /* KMail part of the interface. These slots are connected to the resource
1076 * folders and inform us of folders or incidences in them changing, being
1077 * added or going away. */
1079 void KMailICalIfaceImpl::slotFolderRemoved( KMFolder
* folder
)
1081 // pretend the folder just changed back to the mail type, which
1082 // does the right thing, namely remove resource
1083 folderContentsTypeChanged( folder
, KMail::ContentsTypeMail
);
1084 KConfigGroup
configGroup( kmkernel
->config(), "GroupwareFolderInfo" );
1085 configGroup
.deleteEntry( folder
->idString() + "-storageFormat" );
1086 configGroup
.deleteEntry( folder
->idString() + "-changes" );
1089 // KMail added a file to one of the groupware folders
1090 void KMailICalIfaceImpl::slotIncidenceAdded( KMFolder
* folder
,
1093 if( mResourceQuiet
|| !mUseResourceIMAP
)
1097 QString type
= folderContentsType( folder
->storage()->contentsType() );
1098 if( type
.isEmpty() ) {
1099 kError() <<"Not an IMAP resource folder";
1102 // Get the index of the mail
1104 KMFolder
* aFolder
= 0;
1105 KMMsgDict::instance()->getLocation( sernum
, &aFolder
, &i
);
1106 assert( folder
== aFolder
);
1108 bool unget
= !folder
->isMessage( i
);
1110 QString
uid( "UID" );
1111 KMMessage
*msg
= folder
->getMsg( i
);
1113 if( msg
->isComplete() ) {
1116 StorageFormat format
= storageFormat( folder
);
1118 case StorageIcalVcard
:
1119 // Read the iCal or vCard
1120 ok
= vPartFoundAndDecoded( msg
, s
);
1122 vPartMicroParser( s
, uid
);
1125 // Read the XML from the attachment with the given mimetype
1126 if ( kolabXMLFoundAndDecoded( *msg
,
1127 folderKolabMimeType( folder
->storage()->contentsType() ), s
) ) {
1128 uid
= msg
->subject();
1135 folder
->unGetMsg( i
);
1138 const quint32 sernum
= msg
->getMsgSerNum();
1139 mUIDToSerNum
.insert( uid
, sernum
);
1141 // tell the resource if we didn't trigger this ourselves
1142 if ( mInTransit
.contains( uid
) ) {
1143 mInTransit
.remove( uid
);
1145 emit
incidenceAdded( type
, folder
->location(), sernum
, format
, s
);
1148 // go get the rest of it, then try again
1149 // TODO: Till, port me
1150 if ( unget
) mTheUnGetMes
.insert( msg
->getMsgSerNum(), true );
1151 FolderJob
*job
= msg
->parent()->createJob( msg
);
1152 connect( job
, SIGNAL( messageRetrieved( KMMessage
* ) ),
1153 this, SLOT( slotMessageRetrieved( KMMessage
* ) ) );
1157 if( unget
) folder
->unGetMsg(i
);
1160 // KMail deleted a file
1161 void KMailICalIfaceImpl::slotIncidenceDeleted( KMFolder
* folder
,
1164 if( mResourceQuiet
|| !mUseResourceIMAP
)
1167 QString type
= folderContentsType( folder
->storage()->contentsType() );
1168 //kDebug() << folder << type << sernum;
1169 if( !type
.isEmpty() ) {
1170 // Get the index of the mail
1172 KMFolder
* aFolder
= 0;
1173 KMMsgDict::instance()->getLocation( sernum
, &aFolder
, &i
);
1174 assert( folder
== aFolder
);
1176 // Read the iCal or vCard
1177 bool unget
= !folder
->isMessage( i
);
1180 KMMessage
* msg
= folder
->getMsg( i
);
1181 QString
uid( "UID" );
1182 switch( storageFormat( folder
) ) {
1183 case StorageIcalVcard
:
1184 if( vPartFoundAndDecoded( msg
, s
) ) {
1185 vPartMicroParser( s
, uid
);
1190 if ( kolabXMLFoundAndDecoded( *msg
, folderKolabMimeType( folder
->storage()->contentsType() ), s
) ) {
1191 uid
= msg
->subject();
1197 kDebug() << "Emitting D-Bus signal incidenceDeleted("
1198 << type
<< "," << folder
->location() << "," << uid
<< ")";
1200 emit
incidenceDeleted( type
, folder
->location(), uid
);
1202 if( unget
) folder
->unGetMsg(i
);
1204 kError() <<"Not a groupware folder";
1207 // KMail orders a refresh
1208 void KMailICalIfaceImpl::slotRefresh( const QString
& type
)
1210 if( mUseResourceIMAP
) {
1211 kDebug() << "Emitting D-Bus signal signalRefresh(" << type
<< " )";
1212 emit
signalRefresh( type
, QString() /* PENDING(bo) folder->location() */ );
1216 // This is among other things called when an expunge of a folder happens
1217 void KMailICalIfaceImpl::slotRefreshFolder( KMFolder
* folder
)
1219 // TODO: The resources would of course be better off, if only this
1220 // folder would need refreshing. Currently it just orders a reload of
1221 // the type of the folder
1222 if( mUseResourceIMAP
&& folder
) {
1223 if( folder
== mCalendar
|| folder
== mContacts
1224 || folder
== mNotes
|| folder
== mTasks
1225 || folder
== mJournals
|| mExtraFolders
.contains( folder
->location() ) ) {
1226 // Refresh the folder of this type
1227 KMail::FolderContentsType ct
= folder
->storage()->contentsType();
1228 slotRefresh( s_folderContentsType
[ct
].contentsTypeStr
);
1233 /****************************
1234 * The folder and message stuff code
1237 KMFolder
* KMailICalIfaceImpl::folderFromType( const QString
& type
,
1238 const QString
& folder
)
1240 if( mUseResourceIMAP
) {
1242 if ( !folder
.isEmpty() ) {
1243 f
= extraFolder( type
, folder
);
1248 if( type
== "Calendar" ) f
= mCalendar
;
1249 else if( type
== "Contact" ) f
= mContacts
;
1250 else if( type
== "Note" ) f
= mNotes
;
1251 else if( type
== "Task" || type
== "Todo" ) f
= mTasks
;
1252 else if( type
== "Journal" ) f
= mJournals
;
1254 if ( f
&& ( folder
.isEmpty() || folder
== f
->location() ) )
1257 kError() <<"No folder (" << type
<<"," << folder
<<" )";
1264 // Returns true if folder is a resource folder. If the resource isn't enabled
1265 // this always returns false
1266 bool KMailICalIfaceImpl::isResourceFolder( KMFolder
* folder
) const
1268 return mUseResourceIMAP
&& folder
&&
1269 ( isStandardResourceFolder( folder
) || mExtraFolders
.contains( folder
->location() ) );
1272 bool KMailICalIfaceImpl::isStandardResourceFolder( KMFolder
* folder
) const
1274 return ( folder
== mCalendar
|| folder
== mTasks
|| folder
== mJournals
||
1275 folder
== mNotes
|| folder
== mContacts
);
1278 bool KMailICalIfaceImpl::hideResourceFolder( KMFolder
* folder
) const
1280 return mHideFolders
&& isResourceFolder( folder
);
1283 bool KMailICalIfaceImpl::hideResourceAccountRoot( KMFolder
* folder
) const
1285 KMFolderCachedImap
*dimapFolder
= dynamic_cast<KMFolderCachedImap
*>( folder
->storage() );
1286 bool hide
= dimapFolder
&& mHideFolders
1287 && (int)dimapFolder
->account()->id() == GlobalSettings::self()->theIMAPResourceAccount()
1288 && GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount();
1293 KPIM::FolderTreeWidgetItem::FolderType
KMailICalIfaceImpl::folderType( KMFolder
* folder
) const
1295 if( mUseResourceIMAP
&& folder
) {
1296 if( folder
== mCalendar
|| folder
== mContacts
1297 || folder
== mNotes
|| folder
== mTasks
1298 || folder
== mJournals
|| mExtraFolders
.contains( folder
->location() ) ) {
1299 KMail::FolderContentsType ct
= folder
->storage()->contentsType();
1300 return s_folderContentsType
[ct
].treeItemType
;
1304 return KPIM::FolderTreeWidgetItem::Other
;
1307 // Global tables of foldernames is different languages
1308 // For now: 0->English, 1->German, 2->French, 3->Dutch
1309 static QMap
<KPIM::FolderTreeWidgetItem::FolderType
,QString
> folderNames
[4];
1310 QString
KMailICalIfaceImpl::folderName( KPIM::FolderTreeWidgetItem::FolderType type
, int language
) const
1312 // With the XML storage, folders are always (internally) named in English
1313 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
)
1316 static bool folderNamesSet
= false;
1317 if( !folderNamesSet
) {
1318 folderNamesSet
= true;
1319 /* NOTE: If you add something here, you also need to update
1320 GroupwarePage in configuredialog.cpp */
1323 folderNames
[0][KPIM::FolderTreeWidgetItem::Calendar
] = QString::fromLatin1("Calendar");
1324 folderNames
[0][KPIM::FolderTreeWidgetItem::Tasks
] = QString::fromLatin1("Tasks");
1325 folderNames
[0][KPIM::FolderTreeWidgetItem::Journals
] = QString::fromLatin1("Journal");
1326 folderNames
[0][KPIM::FolderTreeWidgetItem::Contacts
] = QString::fromLatin1("Contacts");
1327 folderNames
[0][KPIM::FolderTreeWidgetItem::Notes
] = QString::fromLatin1("Notes");
1330 folderNames
[1][KPIM::FolderTreeWidgetItem::Calendar
] = QString::fromLatin1("Kalender");
1331 folderNames
[1][KPIM::FolderTreeWidgetItem::Tasks
] = QString::fromLatin1("Aufgaben");
1332 folderNames
[1][KPIM::FolderTreeWidgetItem::Journals
] = QString::fromLatin1("Journal");
1333 folderNames
[1][KPIM::FolderTreeWidgetItem::Contacts
] = QString::fromLatin1("Kontakte");
1334 folderNames
[1][KPIM::FolderTreeWidgetItem::Notes
] = QString::fromLatin1("Notizen");
1337 folderNames
[2][KPIM::FolderTreeWidgetItem::Calendar
] = QString::fromLatin1("Calendrier");
1338 // Tasks = Tâches (â == 0xE2 in latin1)
1339 folderNames
[2][KPIM::FolderTreeWidgetItem::Tasks
] = QString::fromLatin1("T\342ches");
1340 folderNames
[2][KPIM::FolderTreeWidgetItem::Journals
] = QString::fromLatin1("Journal");
1341 folderNames
[2][KPIM::FolderTreeWidgetItem::Contacts
] = QString::fromLatin1("Contacts");
1342 folderNames
[2][KPIM::FolderTreeWidgetItem::Notes
] = QString::fromLatin1("Notes");
1345 folderNames
[3][KPIM::FolderTreeWidgetItem::Calendar
] = QString::fromLatin1("Agenda");
1346 folderNames
[3][KPIM::FolderTreeWidgetItem::Tasks
] = QString::fromLatin1("Taken");
1347 folderNames
[3][KPIM::FolderTreeWidgetItem::Journals
] = QString::fromLatin1("Logboek");
1348 folderNames
[3][KPIM::FolderTreeWidgetItem::Contacts
] = QString::fromLatin1("Contactpersonen");
1349 folderNames
[3][KPIM::FolderTreeWidgetItem::Notes
] = QString::fromLatin1("Notities");
1352 if( language
< 0 || language
> 3 ) {
1353 return folderNames
[mFolderLanguage
][type
];
1356 return folderNames
[language
][type
];
1361 // Find message matching a given UID
1362 KMMessage
*KMailICalIfaceImpl::findMessageByUID( const QString
& uid
, KMFolder
* folder
)
1364 if( !folder
|| !mUIDToSerNum
.contains( uid
) ) return 0;
1367 KMMsgDict::instance()->getLocation( mUIDToSerNum
[uid
], &aFolder
, &i
);
1368 Q_ASSERT( aFolder
== folder
);
1369 return folder
->getMsg( i
);
1372 // Find message matching a given serial number
1373 KMMessage
*KMailICalIfaceImpl::findMessageBySerNum( quint32 serNum
, KMFolder
* folder
)
1375 if( !folder
|| serNum
== 0 ) return 0;
1377 KMMessage
*message
= 0;
1378 KMFolder
* aFolder
= 0;
1380 KMMsgDict::instance()->getLocation( serNum
, &aFolder
, &index
);
1381 if( aFolder
&& aFolder
!= folder
) {
1382 kWarning() <<"findMessageBySerNum(" << serNum
<<" ) found it in folder" << aFolder
->location() <<", expected" << folder
->location();
1385 message
= aFolder
->getMsg( index
);
1387 kWarning() <<"findMessageBySerNum(" << serNum
<<" ) invalid serial number";
1392 void KMailICalIfaceImpl::deleteMsg( KMMessage
*msg
)
1395 // Commands are now delayed; can't use that anymore, we need immediate deletion
1396 //( new KMDeleteMsgCommand( msg->parent(), msg ) )->start();
1397 KMFolder
*srcFolder
= msg
->parent();
1398 int idx
= srcFolder
->find(msg
);
1400 if ( !msg
->transferInProgress() ) {
1401 // kill existing jobs since we are about to delete the message
1402 srcFolder
->ignoreJobsForMessage( msg
);
1403 srcFolder
->removeMsg(idx
);
1406 kDebug(5006) << "Message cannot be deleted now because it is currently in use " << msg
;
1407 msg
->deleteWhenUnused();
1409 addFolderChange( srcFolder
, ContentsChanged
);
1412 void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder
* folder
,
1413 KMail::FolderContentsType contentsType
)
1415 if ( !mUseResourceIMAP
)
1417 // kDebug() << "folderContentsTypeChanged(" << folder->name()
1418 // << ", " << contentsType << ")\n";
1420 // The builtins can't change type
1421 if ( isStandardResourceFolder( folder
) )
1424 // Check if already know that 'extra folder'
1425 const QString location
= folder
->location();
1426 ExtraFolder
* ef
= mExtraFolders
.value( location
, 0 );
1427 if ( ef
&& ef
->folder
) {
1428 // Notify that the old folder resource is no longer available
1429 emit
subresourceDeleted( folderContentsType( folder
->storage()->contentsType() ), location
);
1431 if ( contentsType
== KMail::ContentsTypeMail
) {
1432 // Delete the old entry, stop listening and stop here
1433 delete mExtraFolders
.take( location
);
1434 folder
->disconnect( this );
1437 // So the type changed to another groupware type, ok.
1439 if ( ef
&& !ef
->folder
) // deleted folder, clean up
1440 delete mExtraFolders
.take( location
);
1441 if ( contentsType
== KMail::ContentsTypeMail
)
1444 //kDebug() << "registering" << location <<" as extra folder";
1445 // Make a new entry for the list
1446 ef
= new ExtraFolder( folder
);
1447 mExtraFolders
.insert( location
, ef
);
1449 FolderInfo info
= readFolderInfo( folder
);
1450 mFolderInfoMap
.insert( folder
, info
);
1452 // Adjust the folder names of all foo.default folders.
1453 // German users will get Kalender as the name of all default Calendar folders,
1454 // including their own, so that the default calendar folder of their Japanese
1455 // coworker appears as /user/hirohito/Kalender, although Hirohito sees his folder
1456 // in Japanese. On the server the folders are always in English.
1457 if ( folder
->folderType() == KMFolderTypeCachedImap
) {
1458 QString annotation
= static_cast<KMFolderCachedImap
*>( folder
->storage() )->annotationFolderType();
1459 kDebug() << "folderContentsTypeChanged:" << folder
->name() <<" has annotation" << annotation
;
1460 if ( annotation
== QString( s_folderContentsType
[contentsType
].annotation
) + ".default" )
1461 folder
->setLabel( localizedDefaultFolderName( contentsType
) );
1464 connectFolder( folder
);
1466 // Tell about the new resource
1467 emit
subresourceAdded( folderContentsType( contentsType
), location
,
1468 subresourceLabelForPresentation( folder
),
1469 !folder
->isReadOnly(),
1470 folderIsAlarmRelevant( folder
) );
1473 KMFolder
* KMailICalIfaceImpl::extraFolder( const QString
& type
,
1474 const QString
& folder
)
1476 // If an extra folder exists that matches the type and folder location,
1478 int t
= folderContentsType( type
);
1479 if ( t
< 1 || t
> 5 )
1482 ExtraFolder
* ef
= mExtraFolders
.value( folder
, 0 );
1483 if ( ef
&& ef
->folder
&& ef
->folder
->storage()->contentsType() == t
)
1489 StorageFormat
KMailICalIfaceImpl::storageFormat( KMFolder
* folder
) const
1491 FolderInfoMap::ConstIterator it
= mFolderInfoMap
.find( folder
);
1492 if ( it
!= mFolderInfoMap
.end() )
1493 return (*it
).mStorageFormat
;
1494 return globalStorageFormat();
1497 void KMailICalIfaceImpl::setStorageFormat( KMFolder
* folder
, StorageFormat format
)
1499 FolderInfoMap::Iterator it
= mFolderInfoMap
.find( folder
);
1500 if ( it
!= mFolderInfoMap
.end() ) {
1501 (*it
).mStorageFormat
= format
;
1503 FolderInfo
info( format
, NoChange
);
1504 mFolderInfoMap
.insert( folder
, info
);
1506 KConfigGroup
configGroup( kmkernel
->config(), "GroupwareFolderInfo" );
1507 configGroup
.writeEntry( folder
->idString() + "-storageFormat",
1508 format
== StorageXML
? "xml" : "icalvcard" );
1511 void KMailICalIfaceImpl::addFolderChange( KMFolder
* folder
, FolderChanges changes
)
1513 FolderInfoMap::Iterator it
= mFolderInfoMap
.find( folder
);
1514 if ( it
!= mFolderInfoMap
.end() ) {
1515 (*it
).mChanges
= static_cast<FolderChanges
>( (*it
).mChanges
| changes
);
1516 } else { // Otherwise, well, it's a folder we don't care about.
1517 kDebug() << "addFolderChange: nothing known about folder" << folder
->location();
1519 KConfigGroup
configGroup( kmkernel
->config(), "GroupwareFolderInfo" );
1520 configGroup
.writeEntry( folder
->idString() + "-changes", (int)(*it
).mChanges
);
1523 KMailICalIfaceImpl::FolderInfo
KMailICalIfaceImpl::readFolderInfo( const KMFolder
* const folder
) const
1525 KConfigGroup
configGroup( kmkernel
->config(), "GroupwareFolderInfo" );
1526 QString str
= configGroup
.readEntry( folder
->idString() + "-storageFormat", QString( "unset" ) );
1528 if ( str
== "unset" ) {
1529 info
.mStorageFormat
= globalStorageFormat();
1530 configGroup
.writeEntry( folder
->idString() + "-storageFormat",
1531 info
.mStorageFormat
== StorageXML
? "xml" : "icalvcard" );
1533 info
.mStorageFormat
= ( str
== "xml" ) ? StorageXML
: StorageIcalVcard
;
1535 info
.mChanges
= (FolderChanges
) configGroup
.readEntry( folder
->idString() + "-changes", 0 );
1540 void KMailICalIfaceImpl::folderSynced( KMFolder
* folder
, const KUrl
& folderURL
)
1542 FolderInfoMap::Iterator it
= mFolderInfoMap
.find( folder
);
1543 if ( it
!= mFolderInfoMap
.end() && (*it
).mChanges
) {
1544 handleFolderSynced( folder
, folderURL
, (*it
).mChanges
);
1545 (*it
).mChanges
= NoChange
;
1549 void KMailICalIfaceImpl::handleFolderSynced( KMFolder
* folder
,
1550 const KUrl
& folderURL
,
1553 // This is done here instead of in the resource, because
1554 // there could be 0, 1, or N kolab resources at this point.
1555 // We can hack the N case, but not the 0 case.
1556 // So the idea of a D-Bus signal for this wouldn't work.
1557 if ( ( _changes
& KMail::ContentsChanged
) ||
1558 ( _changes
& KMail::ACLChanged
) ) {
1559 if ( storageFormat( folder
) == StorageXML
&& folder
->storage()->contentsType() == KMail::ContentsTypeCalendar
)
1560 triggerKolabFreeBusy( folderURL
);
1564 void KMailICalIfaceImpl::folderDeletedOnServer( const KUrl
& folderURL
)
1566 triggerKolabFreeBusy( folderURL
);
1569 void KMailICalIfaceImpl::triggerKolabFreeBusy( const KUrl
& folderURL
)
1571 /* Steffen said: you must issue an authenticated HTTP GET request to
1572 https://kolabserver/freebusy/trigger/user@domain/Folder/NestedFolder.pfb
1573 (replace .pfb with .xpfb for extended fb lists). */
1574 KUrl
httpURL( folderURL
);
1575 // Keep username ("user@domain"), pass, and host from the imap url
1576 httpURL
.setProtocol( "https" );
1577 httpURL
.setPort( 0 ); // remove imap port
1579 // IMAP path is either /INBOX/<path> or /user/someone/<path>
1580 QString path
= folderURL
.path( KUrl::RemoveTrailingSlash
);
1581 Q_ASSERT( path
.startsWith( '/' ) );
1582 int secondSlash
= path
.indexOf( '/', 1 );
1583 if ( secondSlash
== -1 ) {
1584 kWarning() << "path is too short:" << path
;
1587 if ( path
.startsWith( "/INBOX/", Qt::CaseInsensitive
) ) {
1588 // If INBOX, replace it with the username (which is user@domain)
1589 path
= path
.mid( secondSlash
);
1590 path
.prepend( folderURL
.user() );
1592 // If user, just remove it. So we keep the IMAP-returned username.
1593 // This assumes it's a known user on the same domain.
1594 path
= path
.mid( secondSlash
);
1597 httpURL
.setPath( "/freebusy/trigger/" + path
+ ".pfb" );
1598 httpURL
.setQuery( QString() );
1599 // Ensure that we encode everything with UTF8
1600 httpURL
= KUrl( httpURL
.url( KUrl::LeaveTrailingSlash
) );
1601 kDebug() << "Triggering PFB update for" << folderURL
<<" : getting" << httpURL
;
1602 // "Fire and forget". No need for error handling, nor for explicit deletion.
1603 // Maybe we should try to prevent launching it if it's already running (for this URL) though.
1604 /*KIO::Job* job =*/ KIO::get( httpURL
, KIO::NoReload
, KIO::HideProgressInfo
);
1607 void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder
* folder
)
1609 if ( isResourceFolder( folder
) ) {
1610 const QString location
= folder
->location();
1611 const QString contentsTypeStr
= folderContentsType( folder
->storage()->contentsType() );
1613 emit
subresourceDeleted( contentsTypeStr
, location
);
1614 emit
subresourceAdded( contentsTypeStr
, location
,
1615 subresourceLabelForPresentation( folder
),
1616 !folder
->isReadOnly(),
1617 folderIsAlarmRelevant( folder
) );
1621 // Must only be connected to a signal from KMFolder!
1622 void KMailICalIfaceImpl::slotFolderRenamed()
1624 const KMFolder
* folder
= static_cast<const KMFolder
*>( sender() );
1625 slotFolderPropertiesChanged( const_cast<KMFolder
*>( folder
) );
1628 void KMailICalIfaceImpl::slotFolderLocationChanged( const QString
&oldLocation
,
1629 const QString
&newLocation
)
1631 KMFolder
*folder
= findResourceFolder( oldLocation
);
1632 ExtraFolder
* ef
= mExtraFolders
.value( oldLocation
, 0 );
1634 // reuse the ExtraFolder entry, but adjust the key
1635 ExtraFolder
*changedFolder
= mExtraFolders
.take( oldLocation
);
1636 mExtraFolders
.insert( newLocation
, changedFolder
);
1640 emit
subresourceDeleted( folderContentsType( folder
->storage()->contentsType() ), oldLocation
);
1643 KMFolder
* KMailICalIfaceImpl::findResourceFolder( const QString
& resource
)
1645 // Try the standard folders
1646 if( mCalendar
&& mCalendar
->location() == resource
)
1648 if ( mContacts
&& mContacts
->location() == resource
)
1650 if ( mNotes
&& mNotes
->location() == resource
)
1652 if ( mTasks
&& mTasks
->location() == resource
)
1654 if ( mJournals
&& mJournals
->location() == resource
)
1657 // No luck. Try the extrafolders
1658 ExtraFolder
* ef
= mExtraFolders
.value( resource
, 0 );
1666 /****************************
1670 void KMailICalIfaceImpl::readConfig()
1672 bool enabled
= GlobalSettings::self()->theIMAPResourceEnabled() &&
1673 ( GlobalSettings::self()->theIMAPResourceAccount() != 0 );
1675 bool justEnabled
= false;
1677 if( mUseResourceIMAP
== true ) {
1679 mUseResourceIMAP
= false;
1685 justEnabled
= enabled
!= mUseResourceIMAP
;
1687 mUseResourceIMAP
= enabled
;
1689 // Read remaining options
1690 const bool hideFolders
= GlobalSettings::self()->hideGroupwareFolders();
1691 QString parentName
= GlobalSettings::self()->theIMAPResourceFolderParent();
1693 // Find the folder parent
1694 KMFolderDir
* folderParentDir
;
1695 KMFolderType folderType
;
1696 KMFolder
* folderParent
= kmkernel
->findFolderById( parentName
);
1697 if( folderParent
== 0 ) {
1698 // Parent folder not found. It was probably deleted. The user will have to
1699 // configure things again.
1700 kDebug() << "Groupware folder" << parentName
<<" not found. Groupware functionality disabled";
1701 // Or maybe the inbox simply wasn't created on the first startup
1702 KMAccount
* account
= kmkernel
->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
1704 // just in case we were connected already
1705 disconnect( account
, SIGNAL( finishedCheck( bool, CheckStatus
) ),
1706 this, SLOT( slotCheckDone() ) );
1707 connect( account
, SIGNAL( finishedCheck( bool, CheckStatus
) ),
1708 this, SLOT( slotCheckDone() ) );
1710 kDebug() << "null IMAP account!";
1712 mUseResourceIMAP
= false;
1713 // We can't really call cleanup(), if those folders were completely deleted.
1721 folderParentDir
= folderParent
->createChildFolder();
1722 folderType
= folderParent
->folderType();
1725 KMAcctCachedImap::GroupwareType groupwareType
= dynamic_cast<KMFolderCachedImap
*>( folderParent
->storage() )->account()->groupwareType();
1727 if ( groupwareType
== KMAcctCachedImap::GroupwareKolab
) {
1728 // Make sure the folder parent has the subdirs
1729 // Globally there are 3 cases: nothing found, some stuff found by type/name heuristics, or everything found OK
1730 bool noneFound
= true;
1731 bool mustFix
= false; // true when at least one was found by heuristics
1732 QVector
<StandardFolderSearchResult
> results( KMail::ContentsTypeLast
+ 1 );
1733 for ( int i
= 0; i
< KMail::ContentsTypeLast
+1; ++i
) {
1734 if ( i
!= KMail::ContentsTypeMail
) {
1735 results
[i
] = findStandardResourceFolder( folderParentDir
, static_cast<KMail::FolderContentsType
>(i
) );
1736 if ( results
[i
].found
== StandardFolderSearchResult::FoundAndStandard
)
1738 else if ( results
[i
].found
== StandardFolderSearchResult::FoundByType
||
1739 results
[i
].found
== StandardFolderSearchResult::FoundByName
) {
1747 // Check if something changed
1748 if( mUseResourceIMAP
&& !noneFound
&& !mustFix
&& mFolderParentDir
== folderParentDir
1749 && mFolderType
== folderType
) {
1751 if ( hideFolders
!= mHideFolders
) {
1752 // Well, the folder hiding has changed
1753 mHideFolders
= hideFolders
;
1759 if( noneFound
|| mustFix
) {
1761 QString parentFolderName
= folderParent
->name();
1763 // No subfolder was found, so ask if we can make them
1764 msg
= i18n("KMail will now create the required groupware folders"
1765 " as subfolders of %1; if you do not want this, cancel"
1766 " and the IMAP resource will be disabled", parentFolderName
);
1768 // Some subfolders were found, be more precise
1769 QString operations
= "<ul>";
1770 for ( int i
= 0; i
< KMail::ContentsTypeLast
+1; ++i
) {
1771 if ( i
!= KMail::ContentsTypeMail
) {
1772 QString typeName
= localizedDefaultFolderName( static_cast<KMail::FolderContentsType
>( i
) );
1773 if ( results
[i
].found
== StandardFolderSearchResult::NotFound
)
1774 operations
+= "<li>" + i18n( "%1: no folder found, will create it", typeName
) + "</li>";
1775 else if ( results
[i
].found
== StandardFolderSearchResult::FoundByType
|| results
[i
].found
== StandardFolderSearchResult::FoundByName
)
1776 operations
+= "<li>" + i18n( "%1: found folder %2, will set it as main groupware folder",
1777 typeName
, results
[i
].folder
->label() ) + "</li>";
1780 operations
+= "</ul>";
1782 msg
= i18n( "<qt>KMail found the following groupware folders in %1 and "
1783 "needs to perform the following operations: %2<nl/>"
1784 "If you do not want this, cancel"
1785 " and the IMAP resource will be disabled</qt>"
1786 , parentFolderName
, operations
);
1790 if( KMessageBox::questionYesNo( 0, msg
,
1791 i18n("Standard Groupware Folders"), KStandardGuiItem::cont(), KStandardGuiItem::cancel() ) == KMessageBox::No
) {
1793 GlobalSettings::self()->setTheIMAPResourceEnabled( false );
1794 mUseResourceIMAP
= false;
1795 mFolderParentDir
= 0;
1802 // Make the new settings work
1803 mUseResourceIMAP
= true;
1804 mFolderLanguage
= GlobalSettings::self()->theIMAPResourceFolderLanguage();
1805 if( mFolderLanguage
> 3 ) mFolderLanguage
= 0;
1806 mFolderParentDir
= folderParentDir
;
1807 mFolderParent
= folderParent
;
1808 mFolderType
= folderType
;
1809 mHideFolders
= hideFolders
;
1811 // Close the previous folders
1814 // Set the new folders
1815 mCalendar
= initFolder( KMail::ContentsTypeCalendar
);
1816 mTasks
= initFolder( KMail::ContentsTypeTask
);
1817 mJournals
= initFolder( KMail::ContentsTypeJournal
);
1818 mContacts
= initFolder( KMail::ContentsTypeContact
);
1819 mNotes
= initFolder( KMail::ContentsTypeNote
);
1821 if ( !mCalendar
|| !mTasks
|| !mJournals
|| !mContacts
|| !mNotes
)
1824 // Store final annotation (with .default) so that we won't ask again on next startup
1825 if ( mCalendar
->folderType() == KMFolderTypeCachedImap
)
1826 static_cast<KMFolderCachedImap
*>( mCalendar
->storage() )->updateAnnotationFolderType();
1827 if ( mTasks
->folderType() == KMFolderTypeCachedImap
)
1828 static_cast<KMFolderCachedImap
*>( mTasks
->storage() )->updateAnnotationFolderType();
1829 if ( mJournals
->folderType() == KMFolderTypeCachedImap
)
1830 static_cast<KMFolderCachedImap
*>( mJournals
->storage() )->updateAnnotationFolderType();
1831 if ( mContacts
->folderType() == KMFolderTypeCachedImap
)
1832 static_cast<KMFolderCachedImap
*>( mContacts
->storage() )->updateAnnotationFolderType();
1833 if ( mNotes
->folderType() == KMFolderTypeCachedImap
)
1834 static_cast<KMFolderCachedImap
*>( mNotes
->storage() )->updateAnnotationFolderType();
1836 // BEGIN TILL TODO The below only uses the dimap folder manager, which
1837 // will fail for all other folder types. Adjust.
1839 kDebug() << "mCalendar=" << mCalendar
<< mCalendar
->location();
1840 kDebug() << "mContacts=" << mContacts
<< mContacts
->location();
1841 kDebug() << "mNotes=" << mNotes
<< mNotes
->location();
1843 // Find all extra folders
1844 QStringList folderNames
;
1845 QList
<QPointer
<KMFolder
> > folderList
;
1846 kmkernel
->dimapFolderMgr()->createFolderList(&folderNames
, &folderList
);
1847 for(QList
<QPointer
<KMFolder
> >::iterator it
= folderList
.begin();
1848 it
!= folderList
.end(); ++it
)
1850 KMFolderCachedImap
* storage
= dynamic_cast<KMFolderCachedImap
*>( (*it
)->storage() );
1851 if ( storage
&& storage
->contentsType() != 0 ) {
1852 storage
->updateAnnotationFolderType();
1853 folderContentsTypeChanged( *it
, storage
->contentsType() );
1857 // If we just created them, they might have been registered as extra folders temporarily.
1859 if ( mExtraFolders
.contains( mCalendar
->location() ) )
1860 delete mExtraFolders
.take( mCalendar
->location() );
1861 if ( mExtraFolders
.contains( mTasks
->location() ) )
1862 delete mExtraFolders
.take( mTasks
->location() );
1863 if ( mExtraFolders
.contains( mJournals
->location() ) )
1864 delete mExtraFolders
.take( mJournals
->location() );
1865 if ( mExtraFolders
.contains( mContacts
->location() ) )
1866 delete mExtraFolders
.take( mContacts
->location() );
1867 if ( mExtraFolders
.contains( mNotes
->location() ) )
1868 delete mExtraFolders
.take( mNotes
->location() );
1872 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar
),
1873 mCalendar
->location(), mCalendar
->label(), true, true );
1874 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeTask
),
1875 mTasks
->location(), mTasks
->label(), true, true );
1876 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeJournal
),
1877 mJournals
->location(), mJournals
->label(), true, false );
1878 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeContact
),
1879 mContacts
->location(), mContacts
->label(), true, false );
1880 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeNote
),
1881 mNotes
->location(), mNotes
->label(), true, false );
1883 } else if ( groupwareType
== KMAcctCachedImap::GroupwareScalix
) {
1884 // Make the new settings work
1885 mUseResourceIMAP
= true;
1886 mFolderParentDir
= folderParentDir
;
1887 mFolderParent
= folderParent
;
1888 mFolderType
= folderType
;
1889 mHideFolders
= false;
1891 // Close the previous folders
1894 // Set the new folders
1895 mCalendar
= initScalixFolder( KMail::ContentsTypeCalendar
);
1896 mTasks
= initScalixFolder( KMail::ContentsTypeTask
);
1898 mContacts
= initScalixFolder( KMail::ContentsTypeContact
);
1899 mNotes
= initScalixFolder( KMail::ContentsTypeNote
);
1901 // Store final annotation (with .default) so that we won't ask again on next startup
1902 if ( mCalendar
->folderType() == KMFolderTypeCachedImap
)
1903 static_cast<KMFolderCachedImap
*>( mCalendar
->storage() )->updateAnnotationFolderType();
1904 if ( mTasks
->folderType() == KMFolderTypeCachedImap
)
1905 static_cast<KMFolderCachedImap
*>( mTasks
->storage() )->updateAnnotationFolderType();
1906 if ( mContacts
->folderType() == KMFolderTypeCachedImap
)
1907 static_cast<KMFolderCachedImap
*>( mContacts
->storage() )->updateAnnotationFolderType();
1908 if ( mNotes
->folderType() == KMFolderTypeCachedImap
)
1909 static_cast<KMFolderCachedImap
*>( mNotes
->storage() )->updateAnnotationFolderType();
1911 // BEGIN TILL TODO The below only uses the dimap folder manager, which
1912 // will fail for all other folder types. Adjust.
1914 kDebug() << "mCalendar=" << mCalendar
<< " " << mCalendar
->location();
1915 kDebug() << "mContacts=" << mContacts
<< " " << mContacts
->location();
1916 kDebug() << "mNotes=" << mNotes
<< " " << mNotes
->location();
1918 // Find all extra folders
1919 QStringList folderNames
;
1920 QList
<QPointer
<KMFolder
> > folderList
;
1921 kmkernel
->dimapFolderMgr()->createFolderList(&folderNames
, &folderList
);
1922 QList
<QPointer
<KMFolder
> >::iterator it
;
1923 for(it
= folderList
.begin(); it
!= folderList
.end(); ++it
)
1925 FolderStorage
*storage
= (*it
)->storage();
1927 if ( (*it
)->folderType() == KMFolderTypeCachedImap
) {
1928 KMFolderCachedImap
*imapFolder
= static_cast<KMFolderCachedImap
*>( storage
);
1930 const QString attributes
= imapFolder
->folderAttributes();
1931 if ( attributes
.contains( "X-FolderClass" ) ) {
1932 if ( !attributes
.contains( "X-SpecialFolder" ) || (*it
)->location().contains( "@" ) ) {
1933 const Scalix::FolderAttributeParser
parser( attributes
);
1934 if ( !parser
.folderClass().isEmpty() ) {
1935 FolderContentsType type
= Scalix::Utils::scalixIdToContentsType( parser
.folderClass() );
1936 imapFolder
->setContentsType( type
);
1937 folderContentsTypeChanged( *it
, type
);
1944 // If we just created them, they might have been registered as extra folders temporarily.
1946 if ( mExtraFolders
.contains( mCalendar
->location() ) )
1947 delete mExtraFolders
.take( mCalendar
->location() );
1948 if ( mExtraFolders
.contains( mTasks
->location() ) )
1949 delete mExtraFolders
.take( mTasks
->location() );
1950 if ( mExtraFolders
.contains( mContacts
->location() ) )
1951 delete mExtraFolders
.take( mContacts
->location() );
1952 if ( mExtraFolders
.contains( mNotes
->location() ) )
1953 delete mExtraFolders
.take( mNotes
->location() );
1957 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar
),
1958 mCalendar
->location(), mCalendar
->label(), true, true );
1959 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeTask
),
1960 mTasks
->location(), mTasks
->label(), true, true );
1961 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeContact
),
1962 mContacts
->location(), mContacts
->label(), true, false );
1963 emit
subresourceAdded( folderContentsType( KMail::ContentsTypeNote
),
1964 mNotes
->location(), mNotes
->label(), true, false );
1969 if ( justEnabled
) {
1970 for ( int i
= KMail::ContentsTypeCalendar
; i
<= KMail::ContentsTypeLast
; ++i
) {
1971 foreach ( const KMail::SubResource
&res
,
1972 subresourcesKolab( folderContentsType( (FolderContentsType
)i
) ) ) {
1973 emit
subresourceAdded( folderContentsType( (FolderContentsType
)i
),
1974 res
.location
, res
.label
,
1975 res
.writable
, res
.alarmRelevant
);
1981 void KMailICalIfaceImpl::slotCheckDone()
1983 QString parentName
= GlobalSettings::self()->theIMAPResourceFolderParent();
1984 KMFolder
* folderParent
= kmkernel
->findFolderById( parentName
);
1985 //kDebug() <<" folderParent=" << folderParent;
1986 if ( folderParent
) // cool it exists now
1988 KMAccount
* account
= kmkernel
->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
1990 disconnect( account
, SIGNAL( finishedCheck( bool, CheckStatus
) ),
1991 this, SLOT( slotCheckDone() ) );
1996 KMFolder
* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType
)
1998 // Figure out what type of folder this is supposed to be
1999 KMFolderType type
= mFolderType
;
2000 if( type
== KMFolderTypeUnknown
) type
= KMFolderTypeMaildir
;
2002 KPIM::FolderTreeWidgetItem::FolderType itemType
= s_folderContentsType
[contentsType
].treeItemType
;
2003 //kDebug() << folderName( itemType );
2006 StandardFolderSearchResult result
= findStandardResourceFolder( mFolderParentDir
, contentsType
);
2008 // deal with multiple default groupware folders
2009 if ( result
.folders
.count() > 1 && result
.found
== StandardFolderSearchResult::FoundAndStandard
) {
2011 for ( QList
<KMFolder
*>::ConstIterator it
= result
.folders
.begin(); it
!= result
.folders
.end(); ++it
)
2012 labels
<< (*it
)->prettyUrl();
2013 const QString selected
= KInputDialog::getItem( i18n("Default folder"),
2014 i18nc( "%1 is one of the messages with context 'type of folder content'",
2015 "There are multiple %1 default folders, please choose one:",
2016 localizedDefaultFolderName( contentsType
) ), labels
);
2017 if ( !selected
.isEmpty() )
2018 result
.folder
= result
.folders
[ labels
.indexOf( selected
) ];
2021 KMFolder
* folder
= result
.folder
;
2024 // The folder isn't there yet - create it
2026 mFolderParentDir
->createFolder( localizedDefaultFolderName( contentsType
), false, type
);
2027 if( mFolderType
== KMFolderTypeImap
) {
2028 KMFolderImap
* parentFolder
= static_cast<KMFolderImap
*>( mFolderParent
->storage() );
2029 parentFolder
->createFolder( localizedDefaultFolderName( contentsType
) );
2030 static_cast<KMFolderImap
*>( folder
->storage() )->setAccount( parentFolder
->account() );
2032 // Groupware folder created, use the global setting for storage format
2033 setStorageFormat( folder
, globalStorageFormat() );
2035 FolderInfo info
= readFolderInfo( folder
);
2036 mFolderInfoMap
.insert( folder
, info
);
2037 //kDebug() <<"Found existing folder type" << itemType <<" :" << folder->location();
2040 if( !folder
->canAccess() ) {
2041 KMessageBox::sorry(0, i18n("You do not have read/write permission to your %1 folder.",
2042 folderName( itemType
) ) );
2045 folder
->storage()->setContentsType( contentsType
);
2046 folder
->setSystemFolder( true );
2047 folder
->storage()->writeConfig();
2048 if ( 0 != folder
->open( "ifacefolder" ) ) {
2049 kWarning() << "folder->open( \"ifacefolder\" ) ) FAILED";
2050 // TODO: remove data structures?
2053 connectFolder( folder
);
2057 KMFolder
* KMailICalIfaceImpl::initScalixFolder( KMail::FolderContentsType contentsType
)
2059 // Figure out what type of folder this is supposed to be
2060 KMFolderType type
= mFolderType
;
2061 if( type
== KMFolderTypeUnknown
) type
= KMFolderTypeMaildir
;
2063 KMFolder
* folder
= 0;
2065 // Find all extra folders
2066 QStringList folderNames
;
2067 QList
<QPointer
<KMFolder
> > folderList
;
2068 Q_ASSERT( kmkernel
);
2069 Q_ASSERT( kmkernel
->dimapFolderMgr() );
2070 kmkernel
->dimapFolderMgr()->createFolderList(&folderNames
, &folderList
);
2071 QList
<QPointer
<KMFolder
> >::iterator it
= folderList
.begin();
2072 for(; it
!= folderList
.end(); ++it
)
2074 FolderStorage
*storage
= (*it
)->storage();
2076 if ( (*it
)->folderType() == KMFolderTypeCachedImap
) {
2077 KMFolderCachedImap
*imapFolder
= static_cast<KMFolderCachedImap
*>( storage
);
2079 const QString attributes
= imapFolder
->folderAttributes();
2080 if ( attributes
.contains( "X-SpecialFolder" ) ) {
2081 const Scalix::FolderAttributeParser
parser( attributes
);
2082 if ( contentsType
== Scalix::Utils::scalixIdToContentsType( parser
.folderClass() ) ) {
2093 FolderInfo info
= readFolderInfo( folder
);
2094 mFolderInfoMap
.insert( folder
, info
);
2095 //kDebug() << "Found existing folder type " << itemType << " : " << folder->location();
2098 if( !folder
->canAccess() ) {
2099 KMessageBox::sorry(0, i18n("You do not have read/write permission to your folder.") );
2102 folder
->storage()->setContentsType( contentsType
);
2103 folder
->setSystemFolder( true );
2104 folder
->storage()->writeConfig();
2105 folder
->open("ifacefolder");
2106 connectFolder( folder
);
2110 void KMailICalIfaceImpl::connectFolder( KMFolder
* folder
)
2112 // avoid multiple connections
2113 disconnect( folder
, SIGNAL( msgAdded( KMFolder
*, quint32
) ),
2114 this, SLOT( slotIncidenceAdded( KMFolder
*, quint32
) ) );
2115 disconnect( folder
, SIGNAL( msgRemoved( KMFolder
*, quint32
) ),
2116 this, SLOT( slotIncidenceDeleted( KMFolder
*, quint32
) ) );
2117 disconnect( folder
, SIGNAL( expunged( KMFolder
* ) ),
2118 this, SLOT( slotRefreshFolder( KMFolder
* ) ) );
2119 disconnect( folder
->storage(), SIGNAL( readOnlyChanged( KMFolder
* ) ),
2120 this, SLOT( slotFolderPropertiesChanged( KMFolder
* ) ) );
2121 disconnect( folder
, SIGNAL( nameChanged() ),
2122 this, SLOT( slotFolderRenamed() ) );
2123 disconnect( folder
->storage(), SIGNAL( locationChanged( const QString
&, const QString
&) ),
2124 this, SLOT( slotFolderLocationChanged( const QString
&, const QString
&) ) );
2126 // Setup the signals to listen for changes
2127 connect( folder
, SIGNAL( msgAdded( KMFolder
*, quint32
) ),
2128 this, SLOT( slotIncidenceAdded( KMFolder
*, quint32
) ) );
2129 connect( folder
, SIGNAL( msgRemoved( KMFolder
*, quint32
) ),
2130 this, SLOT( slotIncidenceDeleted( KMFolder
*, quint32
) ) );
2131 connect( folder
, SIGNAL( expunged( KMFolder
* ) ),
2132 this, SLOT( slotRefreshFolder( KMFolder
* ) ) );
2133 connect( folder
->storage(), SIGNAL( readOnlyChanged( KMFolder
* ) ),
2134 this, SLOT( slotFolderPropertiesChanged( KMFolder
* ) ) );
2135 connect( folder
, SIGNAL( nameChanged() ),
2136 this, SLOT( slotFolderRenamed() ) );
2137 connect( folder
->storage(), SIGNAL( locationChanged( const QString
&, const QString
&) ),
2138 this, SLOT( slotFolderLocationChanged( const QString
&, const QString
&) ) );
2142 static void cleanupFolder( KMFolder
* folder
, KMailICalIfaceImpl
* _this
)
2145 folder
->setSystemFolder( false );
2146 folder
->disconnect( _this
);
2147 folder
->close( "ifacefolder" );
2151 void KMailICalIfaceImpl::cleanup()
2153 cleanupFolder( mContacts
, this );
2154 cleanupFolder( mCalendar
, this );
2155 cleanupFolder( mNotes
, this );
2156 cleanupFolder( mTasks
, this );
2157 cleanupFolder( mJournals
, this );
2159 mContacts
= mCalendar
= mNotes
= mTasks
= mJournals
= 0;
2162 QString
KMailICalIfaceImpl::folderPixmap( KPIM::FolderTreeWidgetItem::FolderType type
) const
2164 if( !mUseResourceIMAP
)
2167 if( type
== KPIM::FolderTreeWidgetItem::Contacts
)
2168 return QString::fromLatin1( "text-directory" );
2169 else if( type
== KPIM::FolderTreeWidgetItem::Calendar
)
2170 return QString::fromLatin1( "text-calendar" );
2171 else if( type
== KPIM::FolderTreeWidgetItem::Notes
)
2172 return QString::fromLatin1( "view-pim-notes" );
2173 else if( type
== KPIM::FolderTreeWidgetItem::Tasks
)
2174 return QString::fromLatin1( "view-pim-tasks" );
2175 else if( type
== KPIM::FolderTreeWidgetItem::Journals
)
2176 return QString::fromLatin1( "view-pim-journal" );
2181 static void reloadFolderTree()
2183 // Make the folder tree show the icons or not
2184 kmkernel
->folderMgr()->contentsChanged();
2187 // This is a very light-weight and fast 'parser' to retrieve
2188 // a data entry from a vCal taking continuation lines
2190 static void vPartMicroParser( const QString
& str
, QString
& s
)
2193 uint len
= str
.length();
2195 for( uint i
=0; i
<len
; ++i
){
2196 if( str
[i
] == '\r' || str
[i
] == '\n' ){
2197 if( str
[i
] == '\r' )
2199 if( i
+1 < len
&& str
[i
+1] == ' ' ){
2200 // found a continuation line, skip it's leading blanc
2203 // found a logical line end, process the line
2204 if( line
.startsWith( s
) ) {
2205 s
= line
.mid( s
.length() + 1 );
2215 // Not found. Clear it
2219 // Returns the first child folder having the given annotation
2220 static QList
<KMFolder
*> findFolderByAnnotation( KMFolderDir
* folderParentDir
, const QString
& annotation
)
2222 QList
<KMFolder
*> rv
;
2223 QList
<KMFolderNode
*>::const_iterator it
;
2224 for ( it
= folderParentDir
->begin(); it
!= folderParentDir
->end(); ++it
) {
2225 if ( !(*it
)->isDir() ) {
2226 KMFolder
* folder
= static_cast<KMFolder
*>( *it
);
2227 if ( folder
->folderType() == KMFolderTypeCachedImap
) {
2228 QString folderAnnotation
= static_cast<KMFolderCachedImap
*>( folder
->storage() )->annotationFolderType();
2229 //kDebug() <<"findStandardResourceFolder:" << folder->name() <<" has annotation" << folderAnnotation;
2230 if ( folderAnnotation
== annotation
)
2231 rv
.append( folder
);
2238 KMailICalIfaceImpl::StandardFolderSearchResult
KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir
* folderParentDir
, KMail::FolderContentsType contentsType
)
2240 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
)
2242 // Look for a folder with an annotation like "event.default"
2243 QList
<KMFolder
*> folders
= findFolderByAnnotation( folderParentDir
, QString( s_folderContentsType
[contentsType
].annotation
) + ".default" );
2244 if ( !folders
.isEmpty() )
2245 return StandardFolderSearchResult( folders
, StandardFolderSearchResult::FoundAndStandard
);
2247 // Fallback: look for a folder with an annotation like "event"
2248 folders
= findFolderByAnnotation( folderParentDir
, QString( s_folderContentsType
[contentsType
].annotation
) );
2249 if ( !folders
.isEmpty() )
2250 return StandardFolderSearchResult( folders
, StandardFolderSearchResult::FoundByType
);
2252 // Fallback: look for the folder by name (we'll need to change its type)
2253 KMFolderNode
* node
= folderParentDir
->hasNamedFolder( localizedDefaultFolderName( contentsType
) );
2254 if ( node
&& !node
->isDir() )
2255 return StandardFolderSearchResult( static_cast<KMFolder
*>( node
), StandardFolderSearchResult::FoundByName
);
2257 kDebug() <<"findStandardResourceFolder: found no resource folder for" << s_folderContentsType
[contentsType
].annotation
;
2258 return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound
);
2260 else // icalvcard: look up standard resource folders by name
2262 KPIM::FolderTreeWidgetItem::FolderType itemType
= s_folderContentsType
[contentsType
].treeItemType
;
2263 unsigned int folderLanguage
= GlobalSettings::self()->theIMAPResourceFolderLanguage();
2264 if( folderLanguage
> 3 ) folderLanguage
= 0;
2265 KMFolderNode
* node
= folderParentDir
->hasNamedFolder( folderName( itemType
, folderLanguage
) );
2266 if ( !node
|| node
->isDir() )
2267 return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound
);
2268 return StandardFolderSearchResult( static_cast<KMFolder
*>( node
), StandardFolderSearchResult::FoundAndStandard
);
2272 /* We treat all folders as relevant wrt alarms for which we have Administer
2273 * rights or for which the "Incidences relevant for everyone" annotation has
2274 * been set. It can be reasonably assumed that those are "ours". All local folders
2275 * must be ours anyhow. */
2276 bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder
*folder
)
2278 bool administerRights
= true;
2279 bool relevantForOwner
= true;
2280 bool relevantForEveryone
= false;
2281 if ( folder
->folderType() == KMFolderTypeImap
) {
2282 const KMFolderImap
*imapFolder
= static_cast<const KMFolderImap
*>( folder
->storage() );
2284 imapFolder
->userRights() <= 0 || imapFolder
->userRights() & KMail::ACLJobs::Administer
;
2286 if ( folder
->folderType() == KMFolderTypeCachedImap
) {
2287 const KMFolderCachedImap
*dimapFolder
= static_cast<const KMFolderCachedImap
*>( folder
->storage() );
2289 dimapFolder
->userRights() <= 0 || dimapFolder
->userRights() & KMail::ACLJobs::Administer
;
2290 relevantForOwner
= dimapFolder
->incidencesFor () == KMFolderCachedImap::IncForAdmins
;
2291 relevantForEveryone
= ( dimapFolder
->incidencesFor() == KMFolderCachedImap::IncForReaders
);
2295 kDebug() <<"Folder:" << folder
->label() <<" has administer rights:" << administerRights
;
2296 kDebug() <<"and is relevant for owner:" << relevantForOwner
;
2297 kDebug() <<"and relevant for everyone:" << relevantForEveryone
;
2299 return ( administerRights
&& relevantForOwner
) || relevantForEveryone
;
2302 void KMailICalIfaceImpl::setResourceQuiet(bool q
)
2307 bool KMailICalIfaceImpl::isResourceQuiet() const
2309 return mResourceQuiet
;
2313 bool KMailICalIfaceImpl::addSubresource( const QString
& resource
,
2314 const QString
& parent
,
2315 const QString
& contentsType
)
2317 kDebug() <<"Adding subresource to parent:" << parent
<<" with name:" << resource
;
2318 kDebug() <<"contents type:" << contentsType
;
2319 KMFolder
*folder
= findResourceFolder( parent
);
2320 KMFolderDir
*parentFolderDir
= !parent
.isEmpty() && folder
? folder
->createChildFolder(): mFolderParentDir
;
2321 if ( !parentFolderDir
|| parentFolderDir
->hasNamedFolder( resource
) ) return false;
2323 KMFolderType type
= mFolderType
;
2324 if( type
== KMFolderTypeUnknown
) type
= KMFolderTypeMaildir
;
2326 KMFolder
* newFolder
= parentFolderDir
->createFolder( resource
, false, type
);
2327 if ( !newFolder
) return false;
2328 if( mFolderType
== KMFolderTypeImap
)
2329 static_cast<KMFolderImap
*>( folder
->storage() )->createFolder( resource
);
2331 StorageFormat defaultFormat
= GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
? StorageXML
: StorageIcalVcard
;
2332 setStorageFormat( newFolder
, folder
? storageFormat( folder
) : defaultFormat
);
2333 newFolder
->storage()->setContentsType( folderContentsType( contentsType
) );
2334 newFolder
->storage()->writeConfig();
2335 newFolder
->open( "KMailICalIfaceImpl::addSubresource" );
2336 connectFolder( newFolder
);
2342 bool KMailICalIfaceImpl::removeSubresource( const QString
& location
)
2346 KMFolder
*folder
= findResourceFolder( location
);
2348 // We don't allow the default folders to be deleted, so check for
2349 // those first. It would be nicer to produce a more meaningful error,
2350 // or prevent deletion of the builtin folders from the gui already.
2351 if ( !folder
|| isStandardResourceFolder( folder
) )
2354 // the folder will be removed, which implies closed, so make sure
2355 // nothing is using it anymore first
2356 emit
subresourceDeleted( folderContentsType( folder
->storage()->contentsType() ), location
);
2358 if ( mExtraFolders
.contains( location
) )
2359 delete mExtraFolders
.take( location
);
2360 folder
->disconnect( this );
2362 if ( folder
->folderType() == KMFolderTypeImap
)
2363 kmkernel
->imapFolderMgr()->remove( folder
);
2364 else if ( folder
->folderType() == KMFolderTypeCachedImap
) {
2365 // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2)
2366 KMFolderCachedImap
* storage
= static_cast<KMFolderCachedImap
*>( folder
->storage() );
2367 KMAcctCachedImap
* acct
= storage
->account();
2369 acct
->addDeletedFolder( folder
);
2370 kmkernel
->dimapFolderMgr()->remove( folder
);
2375 void KMailICalIfaceImpl::syncFolder(KMFolder
* folder
) const
2377 if ( kmkernel
->isOffline() || !GlobalSettings::immediatlySyncDIMAPOnGroupwareChanges() )
2379 KMFolderCachedImap
*dimapFolder
= dynamic_cast<KMFolderCachedImap
*>( folder
->storage() );
2382 // check if the folder exists already, otherwise sync its parent as well to create it
2383 if ( dimapFolder
->imapPath().isEmpty() ) {
2384 if ( folder
->parent() && folder
->parent()->owner() )
2385 syncFolder( folder
->parent()->owner() );
2389 dimapFolder
->account()->processNewMailSingleFolder( folder
);
2392 #include "kmailicalifaceimpl.moc"