summaryrefslogtreecommitdiffstats
path: root/kresources/kolab
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch)
tree67208f7c145782a7e90b123b982ca78d88cc2c87 /kresources/kolab
downloadtdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz
tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kresources/kolab')
-rw-r--r--kresources/kolab/Makefile.am8
-rw-r--r--kresources/kolab/kabc/Makefile.am27
-rw-r--r--kresources/kolab/kabc/contact.cpp1314
-rw-r--r--kresources/kolab/kabc/contact.h287
-rw-r--r--kresources/kolab/kabc/kolab.desktop53
-rw-r--r--kresources/kolab/kabc/resourcekolab.cpp670
-rw-r--r--kresources/kolab/kabc/resourcekolab.h175
-rw-r--r--kresources/kolab/kabc/resourcekolab_plugin.cpp52
-rw-r--r--kresources/kolab/kcal/Makefile.am27
-rw-r--r--kresources/kolab/kcal/event.cpp220
-rw-r--r--kresources/kolab/kcal/event.h102
-rw-r--r--kresources/kolab/kcal/incidence.cpp845
-rw-r--r--kresources/kolab/kcal/incidence.h174
-rw-r--r--kresources/kolab/kcal/journal.cpp184
-rw-r--r--kresources/kolab/kcal/journal.h103
-rw-r--r--kresources/kolab/kcal/kolab.desktop53
-rw-r--r--kresources/kolab/kcal/resourcekolab.cpp1153
-rw-r--r--kresources/kolab/kcal/resourcekolab.h237
-rw-r--r--kresources/kolab/kcal/resourcekolab_plugin.cpp49
-rw-r--r--kresources/kolab/kcal/task.cpp336
-rw-r--r--kresources/kolab/kcal/task.h129
-rw-r--r--kresources/kolab/knotes/Makefile.am29
-rw-r--r--kresources/kolab/knotes/kolabresource.desktop53
-rw-r--r--kresources/kolab/knotes/note.cpp210
-rw-r--r--kresources/kolab/knotes/note.h111
-rw-r--r--kresources/kolab/knotes/resourcekolab.cpp437
-rw-r--r--kresources/kolab/knotes/resourcekolab.h129
-rw-r--r--kresources/kolab/knotes/resourcekolab_plugin.cpp50
-rw-r--r--kresources/kolab/kolab-resource.upd12
-rw-r--r--kresources/kolab/shared/Makefile.am17
-rw-r--r--kresources/kolab/shared/kmailconnection.cpp317
-rw-r--r--kresources/kolab/shared/kmailconnection.h128
-rw-r--r--kresources/kolab/shared/kolabbase.cpp474
-rw-r--r--kresources/kolab/shared/kolabbase.h177
-rw-r--r--kresources/kolab/shared/resourcekolabbase.cpp255
-rw-r--r--kresources/kolab/shared/resourcekolabbase.h195
-rw-r--r--kresources/kolab/shared/subresource.cpp133
-rw-r--r--kresources/kolab/shared/subresource.h117
-rw-r--r--kresources/kolab/uninstall.desktop2
-rw-r--r--kresources/kolab/upgrade-resourcetype.pl24
40 files changed, 9068 insertions, 0 deletions
diff --git a/kresources/kolab/Makefile.am b/kresources/kolab/Makefile.am
new file mode 100644
index 00000000..0f144e05
--- /dev/null
+++ b/kresources/kolab/Makefile.am
@@ -0,0 +1,8 @@
+SUBDIRS = shared kabc knotes kcal
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = kolab-resource.upd
+update_SCRIPTS = upgrade-resourcetype.pl
+
+messages: rc.cpp
+ $(XGETTEXT) */*.cpp -o $(podir)/kres_kolab.pot
diff --git a/kresources/kolab/kabc/Makefile.am b/kresources/kolab/kabc/Makefile.am
new file mode 100644
index 00000000..2a906a45
--- /dev/null
+++ b/kresources/kolab/kabc/Makefile.am
@@ -0,0 +1,27 @@
+METASOURCES = AUTO
+
+INCLUDES = -I$(top_srcdir)/kresources/kolab/shared -I$(top_srcdir) $(all_includes)
+
+# The kolab wizard links to this library too
+lib_LTLIBRARIES = libkabckolab.la
+
+libkabckolab_la_SOURCES = resourcekolab.cpp contact.cpp
+libkabckolab_la_LDFLAGS = $(all_libraries) -no-undefined
+libkabckolab_la_LIBADD = \
+ $(top_builddir)/kresources/kolab/shared/libresourcekolabshared.la \
+ -lkresources -lkabc
+
+kde_module_LTLIBRARIES = kabc_kolab.la
+
+noinst_HEADERS = resourcekolab.h contact.h
+
+kabc_kolab_la_SOURCES = resourcekolab_plugin.cpp
+kabc_kolab_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined
+kabc_kolab_la_LIBADD = libkabckolab.la
+
+servicedir = $(kde_servicesdir)/kresources/kabc
+service_DATA = kolab.desktop
+
+install-data-local: $(srcdir)/../uninstall.desktop
+ $(mkinstalldirs) $(DESTDIR)$(servicedir)
+ $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop
diff --git a/kresources/kolab/kabc/contact.cpp b/kresources/kolab/kabc/contact.cpp
new file mode 100644
index 00000000..c5e819b3
--- /dev/null
+++ b/kresources/kolab/kabc/contact.cpp
@@ -0,0 +1,1314 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2004 Klarälvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "contact.h"
+#include "resourcekolab.h"
+
+#include <kabc/addressee.h>
+#include <kabc/stdaddressbook.h>
+#include <libkdepim/distributionlist.h>
+#include <kio/netaccess.h>
+#include <kdebug.h>
+#include <qfile.h>
+#include <float.h>
+
+using namespace Kolab;
+
+static const char* s_pictureAttachmentName = "kolab-picture.png";
+static const char* s_logoAttachmentName = "kolab-logo.png";
+static const char* s_soundAttachmentName = "sound";
+static const char* s_unhandledTagAppName = "KOLABUNHANDLED"; // no hyphens in appnames!
+
+// saving (addressee->xml)
+Contact::Contact( const KABC::Addressee* addr )
+ : mHasGeo( false )
+{
+ setFields( addr );
+}
+
+// loading (xml->addressee)
+Contact::Contact( const QString& xml, KABC::ResourceKolab* resource, const QString& subResource, Q_UINT32 sernum )
+ : mHasGeo( false )
+{
+ load( xml );
+ if ( !mPictureAttachmentName.isEmpty() )
+ mPicture = loadPictureFromKMail( mPictureAttachmentName, resource, subResource, sernum );
+ if ( !mLogoAttachmentName.isEmpty() )
+ mLogo = loadPictureFromKMail( mLogoAttachmentName, resource, subResource, sernum );
+ if ( !mSoundAttachmentName.isEmpty() )
+ mSound = loadDataFromKMail( mSound, resource, subResource, sernum );
+}
+
+Contact::~Contact()
+{
+}
+
+void Contact::setGivenName( const QString& name )
+{
+ mGivenName = name;
+}
+
+QString Contact::givenName() const
+{
+ return mGivenName;
+}
+
+void Contact::setMiddleNames( const QString& names )
+{
+ mMiddleNames = names;
+}
+
+QString Contact::middleNames() const
+{
+ return mMiddleNames;
+}
+
+void Contact::setLastName( const QString& name )
+{
+ mLastName = name;
+}
+
+QString Contact::lastName() const
+{
+ return mLastName;
+}
+
+void Contact::setFullName( const QString& name )
+{
+ mFullName = name;
+}
+
+QString Contact::fullName() const
+{
+ return mFullName;
+}
+
+void Contact::setInitials( const QString& initials )
+{
+ mInitials = initials;
+}
+
+QString Contact::initials() const
+{
+ return mInitials;
+}
+
+void Contact::setPrefix( const QString& prefix )
+{
+ mPrefix = prefix;
+}
+
+QString Contact::prefix() const
+{
+ return mPrefix;
+}
+
+void Contact::setSuffix( const QString& suffix )
+{
+ mSuffix = suffix;
+}
+
+QString Contact::suffix() const
+{
+ return mSuffix;
+}
+
+void Contact::setRole( const QString& role )
+{
+ mRole = role;
+}
+
+QString Contact::role() const
+{
+ return mRole;
+}
+
+void Contact::setFreeBusyUrl( const QString& fbUrl )
+{
+ mFreeBusyUrl = fbUrl;
+}
+
+QString Contact::freeBusyUrl() const
+{
+ return mFreeBusyUrl;
+}
+
+void Contact::setOrganization( const QString& organization )
+{
+ mOrganization = organization;
+}
+
+QString Contact::organization() const
+{
+ return mOrganization;
+}
+
+void Contact::setWebPage( const QString& url )
+{
+ mWebPage = url;
+}
+
+QString Contact::webPage() const
+{
+ return mWebPage;
+}
+
+void Contact::setIMAddress( const QString& imAddress )
+{
+ mIMAddress = imAddress;
+}
+
+QString Contact::imAddress() const
+{
+ return mIMAddress;
+}
+
+void Contact::setDepartment( const QString& department )
+{
+ mDepartment = department;
+}
+
+QString Contact::department() const
+{
+ return mDepartment;
+}
+
+void Contact::setOfficeLocation( const QString& location )
+{
+ mOfficeLocation = location;
+}
+
+QString Contact::officeLocation() const
+{
+ return mOfficeLocation;
+}
+
+void Contact::setProfession( const QString& profession )
+{
+ mProfession = profession;
+}
+
+QString Contact::profession() const
+{
+ return mProfession;
+}
+
+// void Contact::setJobTitle( const QString& title )
+// {
+// mJobTitle = title;
+// }
+
+// QString Contact::jobTitle() const
+// {
+// return mJobTitle;
+// }
+
+void Contact::setManagerName( const QString& name )
+{
+ mManagerName = name;
+}
+
+QString Contact::managerName() const
+{
+ return mManagerName;
+}
+
+void Contact::setAssistant( const QString& name )
+{
+ mAssistant = name;
+}
+
+QString Contact::assistant() const
+{
+ return mAssistant;
+}
+
+void Contact::setNickName( const QString& name )
+{
+ mNickName = name;
+}
+
+QString Contact::nickName() const
+{
+ return mNickName;
+}
+
+void Contact::setSpouseName( const QString& name )
+{
+ mSpouseName = name;
+}
+
+QString Contact::spouseName() const
+{
+ return mSpouseName;
+}
+
+void Contact::setBirthday( const QDate& date )
+{
+ mBirthday = date;
+}
+
+QDate Contact::birthday() const
+{
+ return mBirthday;
+}
+
+void Contact::setAnniversary( const QDate& date )
+{
+ mAnniversary = date;
+}
+
+QDate Contact::anniversary() const
+{
+ return mAnniversary;
+}
+
+void Contact::setChildren( const QString& children )
+{
+ mChildren = children;
+}
+
+QString Contact::children() const
+{
+ return mChildren;
+}
+
+void Contact::setGender( const QString& gender )
+{
+ mGender = gender;
+}
+
+QString Contact::gender() const
+{
+ return mGender;
+}
+
+void Contact::setLanguage( const QString& language )
+{
+ mLanguage = language;
+}
+
+QString Contact::language() const
+{
+ return mLanguage;
+}
+
+void Contact::addPhoneNumber( const PhoneNumber& number )
+{
+ mPhoneNumbers.append( number );
+}
+
+QValueList<Contact::PhoneNumber>& Contact::phoneNumbers()
+{
+ return mPhoneNumbers;
+}
+
+const QValueList<Contact::PhoneNumber>& Contact::phoneNumbers() const
+{
+ return mPhoneNumbers;
+}
+
+void Contact::addEmail( const Email& email )
+{
+ mEmails.append( email );
+}
+
+QValueList<Contact::Email>& Contact::emails()
+{
+ return mEmails;
+}
+
+const QValueList<Contact::Email>& Contact::emails() const
+{
+ return mEmails;
+}
+
+void Contact::addAddress( const Contact::Address& address )
+{
+ mAddresses.append( address );
+}
+
+QValueList<Contact::Address>& Contact::addresses()
+{
+ return mAddresses;
+}
+
+const QValueList<Contact::Address>& Contact::addresses() const
+{
+ return mAddresses;
+}
+
+void Contact::setPreferredAddress( const QString& address )
+{
+ mPreferredAddress = address;
+}
+
+QString Contact::preferredAddress() const
+{
+ return mPreferredAddress;
+}
+
+bool Contact::loadNameAttribute( QDomElement& element )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "given-name" )
+ setGivenName( e.text() );
+ else if ( tagName == "middle-names" )
+ setMiddleNames( e.text() );
+ else if ( tagName == "last-name" )
+ setLastName( e.text() );
+ else if ( tagName == "full-name" )
+ setFullName( e.text() );
+ else if ( tagName == "initials" )
+ setInitials( e.text() );
+ else if ( tagName == "prefix" )
+ setPrefix( e.text() );
+ else if ( tagName == "suffix" )
+ setSuffix( e.text() );
+ else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ return true;
+}
+
+void Contact::saveNameAttribute( QDomElement& element ) const
+{
+ QDomElement e = element.ownerDocument().createElement( "name" );
+ element.appendChild( e );
+
+ writeString( e, "given-name", givenName() );
+ writeString( e, "middle-names", middleNames() );
+ writeString( e, "last-name", lastName() );
+ writeString( e, "full-name", fullName() );
+ writeString( e, "initials", initials() );
+ writeString( e, "prefix", prefix() );
+ writeString( e, "suffix", suffix() );
+}
+
+bool Contact::loadPhoneAttribute( QDomElement& element )
+{
+ PhoneNumber number;
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "type" )
+ number.type = e.text();
+ else if ( tagName == "number" )
+ number.number = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ addPhoneNumber( number );
+ return true;
+}
+
+void Contact::savePhoneAttributes( QDomElement& element ) const
+{
+ QValueList<PhoneNumber>::ConstIterator it = mPhoneNumbers.begin();
+ for ( ; it != mPhoneNumbers.end(); ++it ) {
+ QDomElement e = element.ownerDocument().createElement( "phone" );
+ element.appendChild( e );
+ const PhoneNumber& p = *it;
+ writeString( e, "type", p.type );
+ writeString( e, "number", p.number );
+ }
+}
+
+void Contact::saveEmailAttributes( QDomElement& element ) const
+{
+ QValueList<Email>::ConstIterator it = mEmails.begin();
+ for ( ; it != mEmails.end(); ++it )
+ saveEmailAttribute( element, *it );
+}
+
+void Contact::loadCustomAttributes( QDomElement& element )
+{
+ Custom custom;
+ custom.app = element.attribute( "app" );
+ custom.name = element.attribute( "name" );
+ custom.value = element.attribute( "value" );
+ mCustomList.append( custom );
+}
+
+void Contact::saveCustomAttributes( QDomElement& element ) const
+{
+ QValueList<Custom>::ConstIterator it = mCustomList.begin();
+ for ( ; it != mCustomList.end(); ++it ) {
+ Q_ASSERT( !(*it).name.isEmpty() );
+ if ( (*it).app == s_unhandledTagAppName ) {
+ writeString( element, (*it).name, (*it).value );
+ } else {
+ // Let's use attributes so that other tag-preserving-code doesn't need sub-elements
+ QDomElement e = element.ownerDocument().createElement( "x-custom" );
+ element.appendChild( e );
+ e.setAttribute( "app", (*it).app );
+ e.setAttribute( "name", (*it).name );
+ e.setAttribute( "value", (*it).value );
+ }
+ }
+}
+
+bool Contact::loadAddressAttribute( QDomElement& element )
+{
+ Address address;
+
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "type" )
+ address.type = e.text();
+ else if ( tagName == "x-kde-type" )
+ address.kdeAddressType = e.text().toInt();
+ else if ( tagName == "street" )
+ address.street = e.text();
+ else if ( tagName == "pobox" )
+ address.pobox = e.text();
+ else if ( tagName == "locality" )
+ address.locality = e.text();
+ else if ( tagName == "region" )
+ address.region = e.text();
+ else if ( tagName == "postal-code" )
+ address.postalCode = e.text();
+ else if ( tagName == "country" )
+ address.country = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ addAddress( address );
+ return true;
+}
+
+void Contact::saveAddressAttributes( QDomElement& element ) const
+{
+ QValueList<Address>::ConstIterator it = mAddresses.begin();
+ for ( ; it != mAddresses.end(); ++it ) {
+ QDomElement e = element.ownerDocument().createElement( "address" );
+ element.appendChild( e );
+ const Address& a = *it;
+ writeString( e, "type", a.type );
+ writeString( e, "x-kde-type", QString::number( a.kdeAddressType ) );
+ if ( !a.street.isEmpty() )
+ writeString( e, "street", a.street );
+ if ( !a.pobox.isEmpty() )
+ writeString( e, "pobox", a.pobox );
+ if ( !a.locality.isEmpty() )
+ writeString( e, "locality", a.locality );
+ if ( !a.region.isEmpty() )
+ writeString( e, "region", a.region );
+ if ( !a.postalCode.isEmpty() )
+ writeString( e, "postal-code", a.postalCode );
+ if ( !a.country.isEmpty() )
+ writeString( e, "country", a.country );
+ }
+}
+
+
+void Kolab::Contact::loadDistrListMember( const QDomElement& element )
+{
+ Member member;
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+ if ( tagName == "display-name" )
+ member.displayName = e.text();
+ else if ( tagName == "smtp-address" )
+ member.email = e.text();
+ }
+ }
+ mDistrListMembers.append( member );
+}
+
+void Contact::saveDistrListMembers( QDomElement& element ) const
+{
+ QValueList<Member>::ConstIterator it = mDistrListMembers.begin();
+ for( ; it != mDistrListMembers.end(); ++it ) {
+ QDomElement e = element.ownerDocument().createElement( "member" );
+ element.appendChild( e );
+ const Member& m = *it;
+ writeString( e, "display-name", m.displayName );
+ writeString( e, "smtp-address", m.email );
+ }
+}
+
+bool Contact::loadAttribute( QDomElement& element )
+{
+ const QString tagName = element.tagName();
+ switch ( tagName[0].latin1() ) {
+ case 'a':
+ if ( tagName == "address" )
+ return loadAddressAttribute( element );
+ if ( tagName == "assistant" ) {
+ setAssistant( element.text() );
+ return true;
+ }
+ if ( tagName == "anniversary" ) {
+ if ( !element.text().isEmpty() )
+ setAnniversary( stringToDate( element.text() ) );
+ return true;
+ }
+ break;
+ case 'b':
+ if ( tagName == "birthday" ) {
+ if ( !element.text().isEmpty() )
+ setBirthday( stringToDate( element.text() ) );
+ return true;
+ }
+ break;
+ case 'c':
+ if ( tagName == "children" ) {
+ setChildren( element.text() );
+ return true;
+ }
+ break;
+ case 'd':
+ if ( tagName == "department" ) {
+ setDepartment( element.text() );
+ return true;
+ }
+ if ( mIsDistributionList && tagName == "display-name" ) {
+ setFullName( element.text() );
+ return true;
+ }
+ break;
+ case 'e':
+ if ( tagName == "email" ) {
+ Email email;
+ if ( loadEmailAttribute( element, email ) ) {
+ addEmail( email );
+ return true;
+ } else
+ return false;
+ }
+ break;
+ case 'f':
+ if ( tagName == "free-busy-url" ) {
+ setFreeBusyUrl( element.text() );
+ return true;
+ }
+ break;
+ case 'g':
+ if ( tagName == "gender" ) {
+ setGender( element.text() );
+ return true;
+ }
+ break;
+ case 'i':
+ if ( tagName == "im-address" ) {
+ setIMAddress( element.text() );
+ return true;
+ }
+ break;
+ case 'j':
+ if ( tagName == "job-title" ) {
+ // see saveAttributes: <job-title> is mapped to the Role field
+ setRole( element.text() );
+ return true;
+ }
+ break;
+ case 'l':
+ if ( tagName == "language" ) {
+ setLanguage( element.text() );
+ return true;
+ }
+ if ( tagName == "latitude" ) {
+ setLatitude( element.text().toFloat() );
+ mHasGeo = true;
+ return true;
+ }
+ if ( tagName == "longitude" ) {
+ setLongitude( element.text().toFloat() );
+ mHasGeo = true;
+ }
+ break;
+ case 'm':
+ if ( tagName == "manager-name" ) {
+ setManagerName( element.text() );
+ return true;
+ }
+ if ( mIsDistributionList && tagName == "member" ) {
+ loadDistrListMember( element );
+ return true;
+ }
+ break;
+ case 'n':
+ if ( tagName == "name" )
+ return loadNameAttribute( element );
+ if ( tagName == "nick-name" ) {
+ setNickName( element.text() );
+ return true;
+ }
+ break;
+ case 'o':
+ if ( tagName == "organization" ) {
+ setOrganization( element.text() );
+ return true;
+ }
+ if ( tagName == "office-location" ) {
+ setOfficeLocation( element.text() );
+ return true;
+ }
+ break;
+ case 'p':
+ if ( tagName == "profession" ) {
+ setProfession( element.text() );
+ return true;
+ }
+ if ( tagName == "picture" ) {
+ mPictureAttachmentName = element.text();
+ return true;
+ }
+ if ( tagName == "phone" ) {
+ return loadPhoneAttribute( element );
+ return true;
+ }
+ if ( tagName == "preferred-address" ) {
+ setPreferredAddress( element.text() );
+ return true;
+ }
+ break;
+ case 'r':
+ if ( tagName == "role" ) {
+ setRole( element.text() );
+ return true;
+ }
+ break;
+ case 's':
+ if ( tagName == "spouse-name" ) {
+ setSpouseName( element.text() );
+ return true;
+ }
+ break;
+ case 'x':
+ if ( tagName == "x-logo" ) {
+ mLogoAttachmentName = element.text();
+ return true;
+ }
+ if ( tagName == "x-sound" ) {
+ mSoundAttachmentName = element.text();
+ return true;
+ }
+ if ( tagName == "x-custom" ) {
+ loadCustomAttributes( element );
+ return true;
+ }
+ break;
+ case 'w':
+ if ( tagName == "web-page" ) {
+ setWebPage( element.text() );
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return KolabBase::loadAttribute( element );
+}
+
+bool Contact::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ if ( mIsDistributionList ) {
+ writeString( element, "display-name", fullName() );
+ saveDistrListMembers( element );
+ } else {
+ saveNameAttribute( element );
+ writeString( element, "free-busy-url", freeBusyUrl() );
+ writeString( element, "organization", organization() );
+ writeString( element, "web-page", webPage() );
+ writeString( element, "im-address", imAddress() );
+ writeString( element, "department", department() );
+ writeString( element, "office-location", officeLocation() );
+ writeString( element, "profession", profession() );
+ // <role> is gone; jobTitle() is not shown in the addresseeeditor,
+ // so let's bind <job-title> to role()
+ //writeString( element, "role", role() );
+ //writeString( element, "job-title", jobTitle() );
+ writeString( element, "job-title", role() );
+ writeString( element, "manager-name", managerName() );
+ writeString( element, "assistant", assistant() );
+ writeString( element, "nick-name", nickName() );
+ writeString( element, "spouse-name", spouseName() );
+ writeString( element, "birthday", dateToString( birthday() ) );
+ writeString( element, "anniversary", dateToString( anniversary() ) );
+ if ( !picture().isNull() )
+ writeString( element, "picture", mPictureAttachmentName );
+ if ( !logo().isNull() )
+ writeString( element, "x-logo", mLogoAttachmentName );
+ if ( !sound().isNull() )
+ writeString( element, "x-sound", mSoundAttachmentName );
+ writeString( element, "children", children() );
+ writeString( element, "gender", gender() );
+ writeString( element, "language", language() );
+ savePhoneAttributes( element );
+ saveEmailAttributes( element );
+ saveAddressAttributes( element );
+ writeString( element, "preferred-address", preferredAddress() );
+ if ( mHasGeo ) {
+ writeString( element, "latitude", QString::number( latitude(), 'g', DBL_DIG ) );
+ writeString( element, "longitude", QString::number( longitude(), 'g', DBL_DIG ) );
+ }
+ }
+ saveCustomAttributes( element );
+
+ return true;
+}
+
+bool Contact::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ mIsDistributionList = top.tagName() == "distribution-list";
+ if ( top.tagName() != "contact" && !mIsDistributionList ) {
+ qWarning( "XML error: Top tag was %s instead of the expected contact or distribution-list",
+ top.tagName().ascii() );
+ return false;
+ }
+
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ if ( !loadAttribute( e ) ) {
+ // Unhandled tag - save for later storage
+ //kdDebug() << "Saving unhandled tag " << e.tagName() << endl;
+ Custom c;
+ c.app = s_unhandledTagAppName;
+ c.name = e.tagName();
+ c.value = e.text();
+ mCustomList.append( c );
+ }
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ return true;
+}
+
+QString Contact::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement(
+ mIsDistributionList ? "distribution-list" : "contact" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+static QString addressTypeToString( int /*KABC::Address::Type*/ type )
+{
+ if ( type & KABC::Address::Home )
+ return "home";
+ if ( type & KABC::Address::Work )
+ return "business";
+ return "other";
+}
+
+static int addressTypeFromString( const QString& type )
+{
+ if ( type == "home" )
+ return KABC::Address::Home;
+ if ( type == "business" )
+ return KABC::Address::Work;
+ // well, this shows "other" in the editor, which is what we want...
+ return KABC::Address::Dom | KABC::Address::Intl | KABC::Address::Postal | KABC::Address::Parcel;
+}
+
+static QStringList phoneTypeToString( int /*KABC::PhoneNumber::Types*/ type )
+{
+ // KABC has a bitfield, i.e. the same phone number can be used for work and home
+ // and fax and cellphone etc. etc.
+ // So when saving we need to create as many tags as bits that were set.
+ QStringList types;
+ if ( type & KABC::PhoneNumber::Fax ) {
+ if ( type & KABC::PhoneNumber::Home )
+ types << "homefax";
+ else // assume work -- if ( type & KABC::PhoneNumber::Work )
+ types << "businessfax";
+ type = type & ~KABC::PhoneNumber::Home;
+ type = type & ~KABC::PhoneNumber::Work;
+ }
+
+ // To support both "home1" and "home2", map Home+Pref to home1
+ if ( ( type & KABC::PhoneNumber::Home ) && ( type & KABC::PhoneNumber::Pref ) )
+ {
+ types << "home1";
+ type = type & ~KABC::PhoneNumber::Home;
+ type = type & ~KABC::PhoneNumber::Pref;
+ }
+ // To support both "business1" and "business2", map Work+Pref to business1
+ if ( ( type & KABC::PhoneNumber::Work ) && ( type & KABC::PhoneNumber::Pref ) )
+ {
+ types << "business1";
+ type = type & ~KABC::PhoneNumber::Work;
+ type = type & ~KABC::PhoneNumber::Pref;
+ }
+
+
+ if ( type & KABC::PhoneNumber::Home )
+ types << "home2";
+ if ( type & KABC::PhoneNumber::Msg ) // Msg==messaging
+ types << "company";
+ if ( type & KABC::PhoneNumber::Work )
+ types << "business2";
+ if ( type & KABC::PhoneNumber::Pref )
+ types << "primary";
+ if ( type & KABC::PhoneNumber::Voice )
+ types << "callback"; // ##
+ if ( type & KABC::PhoneNumber::Cell )
+ types << "mobile";
+ if ( type & KABC::PhoneNumber::Video )
+ types << "radio"; // ##
+ if ( type & KABC::PhoneNumber::Bbs )
+ types << "ttytdd";
+ if ( type & KABC::PhoneNumber::Modem )
+ types << "telex"; // #
+ if ( type & KABC::PhoneNumber::Car )
+ types << "car";
+ if ( type & KABC::PhoneNumber::Isdn )
+ types << "isdn";
+ if ( type & KABC::PhoneNumber::Pcs )
+ types << "assistant"; // ## Assistant is e.g. secretary
+ if ( type & KABC::PhoneNumber::Pager )
+ types << "pager";
+ return types;
+}
+
+static int /*KABC::PhoneNumber::Types*/ phoneTypeFromString( const QString& type )
+{
+ if ( type == "homefax" )
+ return KABC::PhoneNumber::Home | KABC::PhoneNumber::Fax;
+ if ( type == "businessfax" )
+ return KABC::PhoneNumber::Work | KABC::PhoneNumber::Fax;
+ if ( type == "business1" )
+ return KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref;
+ if ( type == "business2" )
+ return KABC::PhoneNumber::Work;
+ if ( type == "home1" )
+ return KABC::PhoneNumber::Home | KABC::PhoneNumber::Pref;
+ if ( type == "home2" )
+ return KABC::PhoneNumber::Home;
+ if ( type == "company" )
+ return KABC::PhoneNumber::Msg;
+ if ( type == "primary" )
+ return KABC::PhoneNumber::Pref;
+ if ( type == "callback" )
+ return KABC::PhoneNumber::Voice;
+ if ( type == "mobile" )
+ return KABC::PhoneNumber::Cell;
+ if ( type == "radio" )
+ return KABC::PhoneNumber::Video;
+ if ( type == "ttytdd" )
+ return KABC::PhoneNumber::Bbs;
+ if ( type == "telex" )
+ return KABC::PhoneNumber::Modem;
+ if ( type == "car" )
+ return KABC::PhoneNumber::Car;
+ if ( type == "isdn" )
+ return KABC::PhoneNumber::Isdn;
+ if ( type == "assistant" )
+ return KABC::PhoneNumber::Pcs;
+ if ( type == "pager" )
+ return KABC::PhoneNumber::Pager;
+ return KABC::PhoneNumber::Home; // whatever
+}
+
+static const char* s_knownCustomFields[] = {
+ "X-IMAddress",
+ "X-Department",
+ "X-Office",
+ "X-Profession",
+ "X-ManagersName",
+ "X-AssistantsName",
+ "X-SpousesName",
+ "X-Anniversary",
+ "DistributionList",
+ 0
+};
+
+// The saving is addressee -> Contact -> xml, this is the first part
+void Contact::setFields( const KABC::Addressee* addressee )
+{
+ KolabBase::setFields( addressee );
+
+ mIsDistributionList = KPIM::DistributionList::isDistributionList( *addressee );
+ if ( mIsDistributionList ) {
+ // Hopefully all resources are available during saving, so we can look up
+ // in the addressbook to get name+email from the UID.
+ KPIM::DistributionList distrList( *addressee );
+ const KPIM::DistributionList::Entry::List entries = distrList.entries( KABC::StdAddressBook::self() );
+ KPIM::DistributionList::Entry::List::ConstIterator it = entries.begin();
+ for ( ; it != entries.end() ; ++it ) {
+ Member m;
+ m.displayName = (*it).addressee.formattedName();
+ m.email = (*it).email;
+ if ( m.email.isEmpty() )
+ m.email = (*it).addressee.preferredEmail();
+ mDistrListMembers.append( m );
+ }
+ }
+
+ setGivenName( addressee->givenName() );
+ setMiddleNames( addressee->additionalName() );
+ setLastName( addressee->familyName() );
+ setFullName( addressee->formattedName() );
+ setPrefix( addressee->prefix() );
+ setSuffix( addressee->suffix() );
+ setOrganization( addressee->organization() );
+ setWebPage( addressee->url().url() );
+ setIMAddress( addressee->custom( "KADDRESSBOOK", "X-IMAddress" ) );
+ setDepartment( addressee->custom( "KADDRESSBOOK", "X-Department" ) );
+ setOfficeLocation( addressee->custom( "KADDRESSBOOK", "X-Office" ) );
+ setProfession( addressee->custom( "KADDRESSBOOK", "X-Profession" ) );
+ setRole( addressee->role() );
+ //setJobTitle( addressee->title() );
+ setManagerName( addressee->custom( "KADDRESSBOOK", "X-ManagersName" ) );
+ setAssistant( addressee->custom( "KADDRESSBOOK", "X-AssistantsName" ) );
+ setNickName( addressee->nickName() );
+ setSpouseName( addressee->custom( "KADDRESSBOOK", "X-SpousesName" ) );
+ if ( !addressee->birthday().isNull() )
+ setBirthday( addressee->birthday().date() );
+ const QString& anniversary = addressee->custom( "KADDRESSBOOK", "X-Anniversary" );
+ if ( !anniversary.isEmpty() )
+ setAnniversary( stringToDate( anniversary ) );
+
+ const QStringList emails = addressee->emails();
+ // Conversion problem here:
+ // KABC::Addressee has only one full name and N addresses, but the XML format
+ // has N times (fullname+address). So we just copy the fullname over and ignore it on loading.
+ for ( QStringList::ConstIterator it = emails.begin(); it != emails.end(); ++it ) {
+ Email email;
+ email.displayName = fullName();
+ email.smtpAddress = *it;
+ addEmail( email );
+ }
+
+ // Now the real-world addresses
+ QString preferredAddress = "home";
+ const KABC::Address::List addresses = addressee->addresses();
+ for ( KABC::Address::List::ConstIterator it = addresses.begin() ; it != addresses.end(); ++it ) {
+ Address address;
+ address.kdeAddressType = (*it).type();
+ address.type = addressTypeToString( address.kdeAddressType );
+ address.street = (*it).street();
+ address.pobox = (*it).postOfficeBox();
+ address.locality = (*it).locality();
+ address.region = (*it).region();
+ address.postalCode = (*it).postalCode();
+ address.country = (*it).country();
+ // ## TODO not in the XML format: extended address info.
+ // ## KDE-specific tags? Or hiding those fields? Or adding a warning?
+ addAddress( address );
+ if ( address.kdeAddressType & KABC::Address::Pref ) {
+ preferredAddress = address.type; // home, business or other
+ }
+ }
+ setPreferredAddress( preferredAddress );
+
+ const KABC::PhoneNumber::List phones = addressee->phoneNumbers();
+ for ( KABC::PhoneNumber::List::ConstIterator it = phones.begin(); it != phones.end(); ++it ) {
+ // Create a tag per phone type set in the bitfield
+ QStringList types = phoneTypeToString( (*it).type() );
+ for( QStringList::Iterator typit = types.begin(); typit != types.end(); ++typit ) {
+ PhoneNumber phoneNumber;
+ phoneNumber.type = *typit;
+ phoneNumber.number = (*it).number();
+ addPhoneNumber( phoneNumber );
+ }
+ }
+
+ setPicture( loadPictureFromAddressee( addressee->photo() ) );
+ mPictureAttachmentName = addressee->custom( "KOLAB", "PictureAttachmentName" );
+ if ( mPictureAttachmentName.isEmpty() )
+ mPictureAttachmentName = s_pictureAttachmentName;
+
+ setLogo( loadPictureFromAddressee( addressee->logo() ) );
+ mLogoAttachmentName = addressee->custom( "KOLAB", "LogoAttachmentName" );
+ if ( mLogoAttachmentName.isEmpty() )
+ mLogoAttachmentName = s_logoAttachmentName;
+
+ setSound( loadSoundFromAddressee( addressee->sound() ) );
+ mSoundAttachmentName = addressee->custom( "KOLAB", "SoundAttachmentName" );
+ if ( mSoundAttachmentName.isEmpty() )
+ mSoundAttachmentName = s_soundAttachmentName;
+
+ if ( addressee->geo().isValid() ) {
+ setLatitude( addressee->geo().latitude() );
+ setLongitude( addressee->geo().longitude() );
+ mHasGeo = true;
+ }
+
+ // Other KADDRESSBOOK custom fields than those already handled
+ // (includes e.g. crypto settings, and extra im addresses)
+ QStringList knownCustoms;
+ for ( const char** p = s_knownCustomFields; *p; ++p )
+ knownCustoms << QString::fromLatin1( *p );
+ QStringList customs = addressee->customs();
+ for( QStringList::Iterator it = customs.begin(); it != customs.end(); ++it ) {
+ // KABC::Addressee doesn't offer a real way to iterate over customs, other than splitting strings ourselves
+ // The format is "app-name:value".
+ int pos = (*it).find( '-' );
+ if ( pos == -1 ) continue;
+ QString app = (*it).left( pos );
+ if ( app == "KOLAB" ) continue;
+ QString name = (*it).mid( pos + 1 );
+ pos = name.find( ':' );
+ if ( pos == -1 ) continue;
+ QString value = name.mid( pos + 1 );
+ name = name.left( pos );
+ if ( !knownCustoms.contains( name ) ) {
+ //kdDebug() << k_funcinfo << "app=" << app << " name=" << name << " value=" << value << endl;
+ Custom c;
+ if ( app != "KADDRESSBOOK" ) // that's the default
+ c.app = app;
+ c.name = name;
+ c.value = value;
+ mCustomList.append( c );
+ }
+ }
+
+ // Those fields, although defined in Addressee, are not used in KDE
+ // (e.g. not visible in kaddressbook/addresseeeditorwidget.cpp)
+ // So it doesn't matter much if we don't have them in the XML.
+ // mailer, timezone, productId, sortString, agent, rfc2426 name()
+
+ // Things KAddressBook can't handle, so they are saved as unhandled tags:
+ // initials, children, gender, language
+
+ // TODO: Free/Busy URL. This is done rather awkward in KAddressBook -
+ // it stores it in a local file through a korganizer file :-(
+}
+
+// The loading is: xml -> Contact -> addressee, this is the second part
+void Contact::saveTo( KABC::Addressee* addressee )
+{
+ // TODO: This needs the same set of TODOs as the setFields method
+ KolabBase::saveTo( addressee );
+
+ if ( mIsDistributionList ) {
+ KPIM::DistributionList distrList( *addressee );
+ distrList.setName( fullName() );
+ QValueList<Member>::ConstIterator mit = mDistrListMembers.begin();
+ for ( ; mit != mDistrListMembers.end(); ++mit ) {
+ QString displayName = (*mit).displayName;
+ // fixup the display name DistributionList::assumes neither ',' nor ';' is present
+ displayName.replace( ',', ' ' );
+ displayName.replace( ';', ' ' );
+ distrList.insertEntry( displayName, (*mit).email );
+ }
+ addressee->insertCustom( "KADDRESSBOOK", "DistributionList", distrList.custom( "KADDRESSBOOK", "DistributionList" ) );
+ Q_ASSERT( KPIM::DistributionList::isDistributionList( *addressee ) );
+ }
+
+ addressee->setGivenName( givenName() );
+ addressee->setAdditionalName( middleNames() );
+ addressee->setFamilyName( lastName() );
+ addressee->setFormattedName( fullName() );
+ if ( mIsDistributionList )
+ addressee->setName( fullName() );
+ addressee->setPrefix( prefix() );
+ addressee->setSuffix( suffix() );
+ addressee->setOrganization( organization() );
+ addressee->setUrl( webPage() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-IMAddress", imAddress() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-Department", department() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-Office", officeLocation() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-Profession", profession() );
+ addressee->setRole( role() );
+ //addressee->setTitle( jobTitle() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-ManagersName", managerName() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-AssistantsName", assistant() );
+ addressee->setNickName( nickName() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-SpousesName", spouseName() );
+ if ( birthday().isValid() )
+ addressee->setBirthday( QDateTime( birthday() ) );
+
+ if ( anniversary().isValid() )
+ addressee->insertCustom( "KADDRESSBOOK", "X-Anniversary",
+ dateToString( anniversary() ) );
+ else
+ addressee->removeCustom( "KADDRESSBOOK", "X-Anniversary" );
+
+ // We need to store both the original attachment name and the picture data into the addressee.
+ // This is important, otherwise we would save the image under another attachment name w/o deleting the original one!
+ if ( !mPicture.isNull() )
+ addressee->setPhoto( KABC::Picture( mPicture ) );
+ // Note that we must save the filename in all cases, so that removing the picture
+ // actually deletes the attachment.
+ addressee->insertCustom( "KOLAB", "PictureAttachmentName", mPictureAttachmentName );
+ if ( !mLogo.isNull() )
+ addressee->setLogo( KABC::Picture( mLogo ) );
+ addressee->insertCustom( "KOLAB", "LogoAttachmentName", mLogoAttachmentName );
+ if ( !mSound.isNull() )
+ addressee->setSound( KABC::Sound( mSound ) );
+ addressee->insertCustom( "KOLAB", "SoundAttachmentName", mSoundAttachmentName );
+
+ if ( mHasGeo )
+ addressee->setGeo( KABC::Geo( mLatitude, mLongitude ) );
+
+ QStringList emailAddresses;
+ for ( QValueList<Email>::ConstIterator it = mEmails.begin(); it != mEmails.end(); ++it ) {
+ // we can't do anything with (*it).displayName
+ emailAddresses.append( (*it).smtpAddress );
+ }
+ addressee->setEmails( emailAddresses );
+
+ for ( QValueList<Address>::ConstIterator it = mAddresses.begin(); it != mAddresses.end(); ++it ) {
+ KABC::Address address;
+ int type = (*it).kdeAddressType;
+ if ( type == -1 ) { // no kde-specific type available
+ type = addressTypeFromString( (*it).type );
+ if ( (*it).type == mPreferredAddress )
+ type |= KABC::Address::Pref;
+ }
+ address.setType( type );
+ address.setStreet( (*it).street );
+ address.setPostOfficeBox( (*it).pobox );
+ address.setLocality( (*it).locality );
+ address.setRegion( (*it).region );
+ address.setPostalCode( (*it).postalCode );
+ address.setCountry( (*it).country );
+ addressee->insertAddress( address );
+ }
+
+ for ( QValueList<PhoneNumber>::ConstIterator it = mPhoneNumbers.begin(); it != mPhoneNumbers.end(); ++it ) {
+ KABC::PhoneNumber number;
+ number.setType( phoneTypeFromString( (*it).type ) );
+ number.setNumber( (*it).number );
+ addressee->insertPhoneNumber( number );
+ }
+
+ for( QValueList<Custom>::ConstIterator it = mCustomList.begin(); it != mCustomList.end(); ++it ) {
+ QString app = (*it).app.isEmpty() ? QString::fromLatin1( "KADDRESSBOOK" ) : (*it).app;
+ addressee->insertCustom( app, (*it).name, (*it).value );
+ }
+ //kdDebug(5006) << addressee->customs() << endl;
+}
+
+QImage Contact::loadPictureFromKMail( const QString& attachmentName, KABC::ResourceKolab* resource, const QString& subResource, Q_UINT32 sernum )
+{
+ QImage img;
+ KURL url;
+ if ( resource->kmailGetAttachment( url, subResource, sernum, attachmentName ) && !url.isEmpty() ) {
+ const QString path = url.path();
+ img.load( path );
+ QFile::remove(path);
+ }
+ return img;
+}
+
+QImage Contact::loadPictureFromAddressee( const KABC::Picture& picture )
+{
+ QImage img;
+ if ( !picture.isIntern() && !picture.url().isEmpty() ) {
+ QString tmpFile;
+ if ( KIO::NetAccess::download( picture.url(), tmpFile, 0 /*no widget known*/ ) ) {
+ img.load( tmpFile );
+ KIO::NetAccess::removeTempFile( tmpFile );
+ }
+ } else
+ img = picture.data();
+ return img;
+}
+
+QByteArray Kolab::Contact::loadDataFromKMail( const QString& attachmentName, KABC::ResourceKolab* resource, const QString& subResource, Q_UINT32 sernum )
+{
+ QByteArray data;
+ KURL url;
+ if ( resource->kmailGetAttachment( url, subResource, sernum, attachmentName ) && !url.isEmpty() ) {
+ QFile f( url.path() );
+ if ( f.open( IO_ReadOnly ) ) {
+ data = f.readAll();
+ f.close();
+ }
+ f.remove();
+ }
+ return data;
+}
+
+QByteArray Kolab::Contact::loadSoundFromAddressee( const KABC::Sound& sound )
+{
+ QByteArray data;
+ if ( !sound.isIntern() && !sound.url().isEmpty() ) {
+ QString tmpFile;
+ if ( KIO::NetAccess::download( sound.url(), tmpFile, 0 /*no widget known*/ ) ) {
+ QFile f( tmpFile );
+ if ( f.open( IO_ReadOnly ) ) {
+ data = f.readAll();
+ f.close();
+ }
+ KIO::NetAccess::removeTempFile( tmpFile );
+ }
+ } else
+ data = sound.data();
+ return data;
+}
+
+QString Kolab::Contact::productID() const
+{
+ // TODO: When KAB has the version number in a header file, don't hardcode (Bo)
+ // Or we could use Addressee::productID? (David)
+ return "KAddressBook 3.3, Kolab resource";
+}
diff --git a/kresources/kolab/kabc/contact.h b/kresources/kolab/kabc/contact.h
new file mode 100644
index 00000000..0c06dfb8
--- /dev/null
+++ b/kresources/kolab/kabc/contact.h
@@ -0,0 +1,287 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KOLABCONTACT_H
+#define KOLABCONTACT_H
+
+#include <kolabbase.h>
+#include <qimage.h>
+
+namespace KABC {
+ class Addressee;
+ class ResourceKolab;
+ class Picture;
+ class Sound;
+}
+
+namespace Kolab {
+
+class Contact : public KolabBase {
+public:
+ struct PhoneNumber {
+ public:
+ QString type;
+ QString number;
+ };
+
+ struct Address {
+ public:
+ Address() : kdeAddressType( -1 )
+ {
+ }
+ int kdeAddressType; // KABC::Address::Type
+ QString type; // kolab-compliant address type: home, work or other
+ QString street;
+ QString pobox;
+ QString locality;
+ QString region;
+ QString postalCode;
+ QString country;
+ };
+
+ explicit Contact( const KABC::Addressee* address );
+ Contact( const QString& xml, KABC::ResourceKolab* resource, const QString& subResource, Q_UINT32 sernum );
+ ~Contact();
+
+ void saveTo( KABC::Addressee* address );
+
+ QString type() const { return "Contact"; }
+
+ void setGivenName( const QString& name );
+ QString givenName() const;
+
+ void setMiddleNames( const QString& names );
+ QString middleNames() const;
+
+ void setLastName( const QString& name );
+ QString lastName() const;
+
+ void setFullName( const QString& name );
+ QString fullName() const;
+
+ void setInitials( const QString& initials );
+ QString initials() const;
+
+ void setPrefix( const QString& prefix );
+ QString prefix() const;
+
+ void setSuffix( const QString& suffix );
+ QString suffix() const;
+
+ void setRole( const QString& role );
+ QString role() const;
+
+ void setFreeBusyUrl( const QString& fbUrl );
+ QString freeBusyUrl() const;
+
+ void setOrganization( const QString& organization );
+ QString organization() const;
+
+ void setWebPage( const QString& url );
+ QString webPage() const;
+
+ void setIMAddress( const QString& imAddress );
+ QString imAddress() const;
+
+ void setDepartment( const QString& department );
+ QString department() const;
+
+ void setOfficeLocation( const QString& location );
+ QString officeLocation() const;
+
+ void setProfession( const QString& profession );
+ QString profession() const;
+
+ // not shown in the kaddressbook GUI
+ //void setJobTitle( const QString& title );
+ //QString jobTitle() const;
+
+ void setManagerName( const QString& name );
+ QString managerName() const;
+
+ void setAssistant( const QString& name );
+ QString assistant() const;
+
+ void setNickName( const QString& name );
+ QString nickName() const;
+
+ void setSpouseName( const QString& name );
+ QString spouseName() const;
+
+ void setBirthday( const QDate& date );
+ QDate birthday() const;
+
+ void setAnniversary( const QDate& date );
+ QDate anniversary() const;
+
+ void setPicture( const QImage& image) { mPicture = image; }
+ QString pictureAttachmentName() const { return mPictureAttachmentName; }
+ QImage picture() const { return mPicture; }
+
+ void setLogo( const QImage& image ) { mLogo = image; }
+ QString logoAttachmentName() const { return mLogoAttachmentName; }
+ QImage logo() const { return mLogo; }
+
+ void setSound( const QByteArray& sound ) { mSound = sound; }
+ QString soundAttachmentName() const { return mSoundAttachmentName; }
+ QByteArray sound() const { return mSound; }
+
+ void setChildren( const QString& children );
+ QString children() const;
+
+ void setGender( const QString& gender );
+ QString gender() const;
+
+ void setLanguage( const QString& language );
+ QString language() const;
+
+ void addPhoneNumber( const PhoneNumber& number );
+ QValueList<PhoneNumber>& phoneNumbers();
+ const QValueList<PhoneNumber>& phoneNumbers() const;
+
+ void addEmail( const Email& email );
+ QValueList<Email>& emails();
+ const QValueList<Email>& emails() const;
+
+ void addAddress( const Address& address );
+ QValueList<Address>& addresses();
+ const QValueList<Address>& addresses() const;
+
+ // which address is preferred: home or business or other
+ void setPreferredAddress( const QString& address );
+ QString preferredAddress() const;
+
+ float latitude() const { return mLatitude; }
+ void setLatitude( float latitude ) { mLatitude = latitude; }
+
+ float longitude() const { return mLongitude; }
+ void setLongitude( float longitude ) { mLongitude = longitude; }
+
+ // Load the attributes of this class
+ bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ bool saveAttributes( QDomElement& ) const;
+
+ // Load this note by reading the XML file
+ bool loadXML( const QDomDocument& xml );
+
+ // Serialize this note to an XML string
+ QString saveXML() const;
+
+ // Return true if this contact is a distr list
+ bool isDistributionList() const { return mIsDistributionList; }
+
+protected:
+ void setFields( const KABC::Addressee* );
+
+private:
+ bool loadNameAttribute( QDomElement& element );
+ void saveNameAttribute( QDomElement& element ) const;
+
+ bool loadPhoneAttribute( QDomElement& element );
+ void savePhoneAttributes( QDomElement& element ) const;
+
+ void saveEmailAttributes( QDomElement& element ) const;
+
+ bool loadAddressAttribute( QDomElement& element );
+ void saveAddressAttributes( QDomElement& element ) const;
+
+ void loadCustomAttributes( QDomElement& element );
+ void saveCustomAttributes( QDomElement& element ) const;
+
+ void loadDistrListMember( const QDomElement& element );
+ void saveDistrListMembers( QDomElement& element ) const;
+
+ QImage loadPictureFromKMail( const QString& attachmentName, KABC::ResourceKolab* resource, const QString& subResource, Q_UINT32 sernum );
+ QImage loadPictureFromAddressee( const KABC::Picture& picture );
+
+ QByteArray loadDataFromKMail( const QString& attachmentName, KABC::ResourceKolab* resource, const QString& subResource, Q_UINT32 sernum );
+ QByteArray loadSoundFromAddressee( const KABC::Sound& sound );
+
+ QString productID() const;
+
+ QString mGivenName;
+ QString mMiddleNames;
+ QString mLastName;
+ QString mFullName;
+ QString mInitials;
+ QString mPrefix;
+ QString mSuffix;
+ QString mRole;
+ QString mFreeBusyUrl;
+ QString mOrganization;
+ QString mWebPage;
+ QString mIMAddress;
+ QString mDepartment;
+ QString mOfficeLocation;
+ QString mProfession;
+ //QString mJobTitle;
+ QString mManagerName;
+ QString mAssistant;
+ QString mNickName;
+ QString mSpouseName;
+ QDate mBirthday;
+ QDate mAnniversary;
+ QImage mPicture;
+ QImage mLogo;
+ QByteArray mSound;
+ QString mPictureAttachmentName;
+ QString mLogoAttachmentName;
+ QString mSoundAttachmentName;
+ QString mChildren;
+ QString mGender;
+ QString mLanguage;
+ QValueList<PhoneNumber> mPhoneNumbers;
+ QValueList<Email> mEmails;
+ QValueList<Address> mAddresses;
+ QString mPreferredAddress;
+ float mLatitude;
+ float mLongitude;
+ bool mHasGeo;
+ bool mIsDistributionList;
+ struct Custom {
+ QString app;
+ QString name;
+ QString value;
+ };
+ QValueList<Custom> mCustomList;
+ struct Member {
+ QString displayName;
+ QString email;
+ };
+ QValueList<Member> mDistrListMembers;
+};
+
+}
+
+#endif // KOLABCONTACT_H
diff --git a/kresources/kolab/kabc/kolab.desktop b/kresources/kolab/kabc/kolab.desktop
new file mode 100644
index 00000000..d75090a7
--- /dev/null
+++ b/kresources/kolab/kabc/kolab.desktop
@@ -0,0 +1,53 @@
+[Desktop Entry]
+Name=Addressbook on IMAP Server via KMail
+Name[af]=Adresboek op IMAP bediener via KMail
+Name[bg]=Адресник на сървър IMAP през KMail
+Name[br]=Karned chomlec'hioù war ur servijer IMAP gant KMail
+Name[ca]=Llibreta d'adreces sobre servidor IMAP mitjançant KMail
+Name[cs]=Kniha adres na IMAP serveru přes KMail
+Name[da]=Adressebog på IMAP-server via KMail
+Name[de]=Adressbuch auf einem IMAP-Server via KMail
+Name[el]=Ημερολόγιο σε εξυπηρετητή IMAP μέσω του KMail
+Name[es]=Libreta de direcciones en servidor IMAP por medio de KMail
+Name[et]=Aadressiraamat IMAP-serveris (KMaili vahendusel)
+Name[eu]=Helbide-liburua IMAP zerbitzarian KMail-en bidez
+Name[fa]=کتاب نشانی روی کارساز IMAP از طریق KMail
+Name[fi]=Osoitekirja IMAP-palvelimella KMailin avulla
+Name[fr]=Carnet d'adresse sur serveur IMAP (via KMail)
+Name[fy]=Adresboek op IMAP-tsjinner fia KMail
+Name[ga]=Leabhar Seoltaí ar Fhreastalaí IMAP via KMail
+Name[gl]=Caderno de enderezos nun servidor IMAP mediante KMail
+Name[hu]=IMAP-kiszolgálón tárolt címjegyzék a KMailen keresztül
+Name[is]=Vistfangaskrá á IMAP þjóni gegnum KMail
+Name[it]=Rubrica indirizzi su server IMAP via KMail
+Name[ja]=KMail 経由 IMAP サーバのアドレス帳
+Name[ka]=წიგნაკი IMAP-ის სერვერზე KMail-ის საშუალებით
+Name[kk]=KMail арқылы IMAP серверіндегі адрестік кітапша
+Name[km]=សៀវភៅ​អាសយដ្ឋាន​លើ​ម៉ាស៊ីន​បម្រើ IMAP តាម​រយៈ KMail
+Name[lt]=Adresų knygelė IMAP serveryje per KMail
+Name[mk]=Адресар на IMAP-сервер преку КПошта
+Name[ms]=Buku alamat pada pelayan IMAP melalui KMail
+Name[nb]=Adressebok på IMAP-tjener via KMail
+Name[nds]=Adressbook op IMAP-Server över KMail
+Name[ne]=केडीई मेल मार्फत IMAP सर्भरमा ठेगाना पुस्तिका
+Name[nl]=Adresboek op IMAP-server via KMail
+Name[nn]=Adressebok på IMAP-tenar via KMail
+Name[pl]=Książka adresowa na serwerze IMAP za pośrednictwem KMail
+Name[pt]=Livro de Endereços em Servidor IMAP via KMail
+Name[pt_BR]=Livro de Endereços em servidor IMAP via KMail
+Name[ru]=Адресная книга на сервере IMAP через KMail
+Name[sk]=Adresár na IMAP-serveri pomocou KMail
+Name[sl]=Adresar na strežniku IMAP preko KMaila
+Name[sr]=Адресар на IMAP серверу преко KMail-а
+Name[sr@Latn]=Adresar na IMAP serveru preko KMail-a
+Name[sv]=Adressbok på IMAP-server via Kmail
+Name[ta]=IMAP சேவகன் மூலம் கேஅஞ்சல் முகவரிப்புத்தகம்
+Name[tr]=KMail Aracılığı ile IMAP Sunucusunda Adres Defteri
+Name[uk]=Адресна книга на сервері IMAP через KMail
+Name[zh_CN]=通过 KMail 访问 IMAP 服务器上的地址簿
+Name[zh_TW]=透過 KMail 取得 IMAP 伺服器上的通訊錄
+X-KDE-Library=kabc_kolab
+Type=Service
+ServiceTypes=KResources/Plugin
+X-KDE-ResourceFamily=contact
+X-KDE-ResourceType=imap
diff --git a/kresources/kolab/kabc/resourcekolab.cpp b/kresources/kolab/kabc/resourcekolab.cpp
new file mode 100644
index 00000000..464c9c61
--- /dev/null
+++ b/kresources/kolab/kabc/resourcekolab.cpp
@@ -0,0 +1,670 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "resourcekolab.h"
+#include "contact.h"
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <ktempfile.h>
+#include <kio/observer.h>
+#include <kio/uiserver_stub.h>
+#include <kabc/vcardconverter.h>
+#include <kmainwindow.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <qstring.h>
+#include <qfile.h>
+#include <qapplication.h>
+
+#include <assert.h>
+
+using namespace Kolab;
+
+class KolabFactory : public KRES::PluginFactoryBase
+{
+ public:
+ KRES::Resource *resource( const KConfig *config )
+ {
+ return new KABC::ResourceKolab( config );
+ }
+
+ KRES::ConfigWidget *configWidget( QWidget* )
+ {
+ return 0;
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY(kabc_kolab,KolabFactory)
+
+static const char* s_kmailContentsType = "Contact";
+static const char* s_attachmentMimeTypeContact = "application/x-vnd.kolab.contact";
+static const char* s_attachmentMimeTypeDistList = "application/x-vnd.kolab.contact.distlist";
+static const char* s_inlineMimeType = "text/x-vcard";
+
+KABC::ResourceKolab::ResourceKolab( const KConfig *config )
+ : KPIM::ResourceABC( config ),
+ Kolab::ResourceKolabBase( "ResourceKolab-KABC" ),
+ mCachedSubresource( QString::null ), mLocked( false )
+{
+ setType( "imap" );
+}
+
+KABC::ResourceKolab::~ResourceKolab()
+{
+ // The resource is deleted on exit (StdAddressBook's KStaticDeleter),
+ // and it wasn't closed before that, so close here to save the config.
+ if ( isOpen() ) {
+ close();
+ }
+}
+
+void KABC::ResourceKolab::loadSubResourceConfig( KConfig& config,
+ const QString& name,
+ const QString& label,
+ bool writable )
+{
+ KConfigGroup group( &config, name );
+ bool active = group.readBoolEntry( "Active", true );
+ int completionWeight = group.readNumEntry( "CompletionWeight", 80 );
+ mSubResources.insert( name, Kolab::SubResource( active, writable, label,
+ completionWeight ) );
+}
+
+bool KABC::ResourceKolab::doOpen()
+{
+ KConfig config( configFile() );
+
+ // Read the calendar entries
+ QValueList<KMailICalIface::SubResource> subResources;
+ if ( !kmailSubresources( subResources, s_kmailContentsType ) )
+ return false;
+ mSubResources.clear();
+ QValueList<KMailICalIface::SubResource>::ConstIterator it;
+ for ( it = subResources.begin(); it != subResources.end(); ++it ) {
+ loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable );
+ }
+
+ return true;
+}
+
+void KABC::ResourceKolab::doClose()
+{
+ KConfig config( configFile() );
+
+ Kolab::ResourceMap::ConstIterator it;
+ for ( it = mSubResources.begin(); it != mSubResources.end(); ++it ) {
+ config.setGroup( it.key() );
+ config.writeEntry( "Active", it.data().active() );
+ config.writeEntry( "CompletionWeight", it.data().completionWeight() );
+ }
+}
+
+KABC::Ticket * KABC::ResourceKolab::requestSaveTicket()
+{
+ if ( !addressBook() ) {
+ kdError() << "no addressbook" << endl;
+ return 0;
+ }
+ mLocked = true;
+
+ return createTicket( this );
+}
+
+void KABC::ResourceKolab::releaseSaveTicket( Ticket* ticket )
+{
+ mLocked = false;
+ mCachedSubresource = QString::null;
+ delete ticket;
+}
+
+QString KABC::ResourceKolab::loadContact( const QString& contactData,
+ const QString& subResource,
+ Q_UINT32 sernum,
+ KMailICalIface::StorageFormat format )
+{
+ KABC::Addressee addr;
+ if ( format == KMailICalIface::StorageXML ) {
+ Contact contact( contactData, this, subResource, sernum ); // load
+ contact.saveTo( &addr );
+ } else {
+ KABC::VCardConverter converter;
+ addr = converter.parseVCard( contactData );
+ }
+
+ addr.setResource( this );
+ addr.setChanged( false );
+ KABC::Resource::insertAddressee( addr ); // same as mAddrMap.insert( addr.uid(), addr );
+ mUidMap[ addr.uid() ] = StorageReference( subResource, sernum );
+ kdDebug(5650) << "Loaded contact uid=" << addr.uid() << " sernum=" << sernum << " fullName=" << addr.name() << endl;
+ return addr.uid();
+}
+
+static const struct { const char* mimetype; KMailICalIface::StorageFormat format; } s_formats[] =
+{
+ { s_attachmentMimeTypeContact, KMailICalIface::StorageXML },
+ { s_attachmentMimeTypeDistList, KMailICalIface::StorageXML },
+ { s_inlineMimeType, KMailICalIface::StorageIcalVcard }
+};
+
+bool KABC::ResourceKolab::loadSubResource( const QString& subResource )
+{
+ int count = 0;
+ if ( !kmailIncidencesCount( count, QString::null, subResource ) ) {
+ kdError() << "Communication problem in KABC::ResourceKolab::loadSubResource()\n";
+ return false;
+ }
+ if ( !count )
+ return true;
+
+ // Read that many contacts at a time.
+ // If this number is too small we lose time in kmail.
+ // If it's too big the progressbar is jumpy.
+ const int nbMessages = 200;
+
+ (void)Observer::self(); // ensure kio_uiserver is running
+ UIServer_stub uiserver( "kio_uiserver", "UIServer" );
+ int progressId = 0;
+ if ( count > 200 ) {
+ progressId = uiserver.newJob( kapp->dcopClient()->appId(), true );
+ uiserver.totalFiles( progressId, count );
+ uiserver.infoMessage( progressId, i18n( "Loading contacts..." ) );
+ uiserver.transferring( progressId, "Contacts" );
+ }
+
+ for ( int startIndex = 0; startIndex < count; startIndex += nbMessages ) {
+
+ // TODO it would be faster to pass the s_formats array to kmail and let it load
+ // all events - to avoid loading each mail 3 times. But then we need to extend the returned
+ // QMap to also tell us the StorageFormat of each found contact...
+ for ( int indexFormat = 0; indexFormat < 3; ++indexFormat ) {
+ const char* mimetype = s_formats[indexFormat].mimetype;
+ KMailICalIface::StorageFormat format = s_formats[indexFormat].format;
+ QMap<Q_UINT32, QString> lst;
+ if ( !kmailIncidences( lst, mimetype, subResource, startIndex, nbMessages ) ) {
+ kdError() << "Communication problem in KABC::ResourceKolab::loadSubResource()\n";
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return false;
+ }
+
+ for( QMap<Q_UINT32, QString>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
+ loadContact( it.data(), subResource, it.key(), format );
+ }
+
+ }
+ if ( progressId ) {
+ uiserver.processedFiles( progressId, startIndex );
+ uiserver.percent( progressId, 100 * startIndex / count );
+ }
+
+// if ( progress.wasCanceled() ) {
+// uiserver.jobFinished( progressId );
+// return false;
+// }
+
+ }
+
+ kdDebug(5650) << "Contacts kolab resource: got " << count << " contacts in " << subResource << endl;
+
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return true;
+}
+
+bool KABC::ResourceKolab::load()
+{
+ mUidMap.clear();
+ mAddrMap.clear();
+
+ bool rc = true;
+ Kolab::ResourceMap::ConstIterator itR;
+ for ( itR = mSubResources.begin(); itR != mSubResources.end(); ++itR ) {
+ if ( !itR.data().active() )
+ // This resource is disabled
+ continue;
+
+ rc &= loadSubResource( itR.key() );
+ }
+
+ return rc;
+}
+
+bool KABC::ResourceKolab::save( Ticket* )
+{
+ bool rc = true;
+
+ for( ConstIterator it = begin(); it != end(); ++it )
+ if( (*it).changed() ) {
+ rc &= kmailUpdateAddressee( *it );
+ }
+
+ if ( !rc )
+ kdDebug(5650) << k_funcinfo << " failed." << endl;
+ return rc;
+}
+
+namespace Kolab {
+struct AttachmentList {
+ QStringList attachmentURLs;
+ QStringList attachmentNames;
+ QStringList attachmentMimeTypes;
+ QStringList deletedAttachments;
+ QValueList<KTempFile *> tempFiles;
+
+ void addAttachment( const QString& url, const QString& name, const QString& mimetype ) {
+ attachmentURLs.append( url );
+ attachmentNames.append( name );
+ attachmentMimeTypes.append( mimetype );
+ }
+
+ void updatePictureAttachment( const QImage& image, const QString& name );
+ void updateAttachment( const QByteArray& data, const QString& name, const char* mimetype );
+};
+} // namespace
+
+void AttachmentList::updatePictureAttachment( const QImage& image, const QString& name )
+{
+ assert( !name.isEmpty() );
+ if ( !image.isNull() ) {
+ KTempFile* tempFile = new KTempFile;
+ image.save( tempFile->file(), "PNG" );
+ tempFile->close();
+ KURL url;
+ url.setPath( tempFile->name() );
+ kdDebug(5650) << "picture saved to " << url.path() << endl;
+ addAttachment( url.url(), name, "image/png" );
+ } else {
+ deletedAttachments.append( name );
+ }
+}
+
+void AttachmentList::updateAttachment( const QByteArray& data, const QString& name, const char* mimetype )
+{
+ assert( !name.isEmpty() );
+ if ( !data.isNull() ) {
+ KTempFile* tempFile = new KTempFile;
+ tempFile->file()->writeBlock( data );
+ tempFile->close();
+ KURL url;
+ url.setPath( tempFile->name() );
+ kdDebug(5650) << "data saved to " << url.path() << endl;
+ addAttachment( url.url(), name, mimetype );
+ } else {
+ deletedAttachments.append( name );
+ }
+}
+
+bool KABC::ResourceKolab::kmailUpdateAddressee( const Addressee& addr )
+{
+ const QString uid = addr.uid();
+ QString subResource;
+ Q_UINT32 sernum;
+ if ( mUidMap.find( uid ) != mUidMap.end() ) {
+ subResource = mUidMap[ uid ].resource();
+ if ( !subresourceWritable( subResource ) ) {
+ kdWarning() << "Wow! Something tried to update a non-writable addressee! Fix this caller: " << kdBacktrace() << endl;
+ return false;
+ }
+ sernum = mUidMap[ uid ].serialNumber();
+ } else {
+ if ( !mCachedSubresource.isNull() ) {
+ subResource = mCachedSubresource;
+ } else {
+ subResource = findWritableResource( mSubResources );
+ // We were locked, remember the subresource we are working with until
+ // we are unlocked
+ if ( mLocked )
+ mCachedSubresource = subResource;
+ }
+ if ( subResource.isEmpty() )
+ return false;
+ sernum = 0;
+ }
+ QString data;
+ QString mimetype;
+ AttachmentList att;
+ bool isXMLStorageFormat = kmailStorageFormat( subResource ) == KMailICalIface::StorageXML;
+ QString subject = uid; // as per kolab2 spec
+ if ( isXMLStorageFormat ) {
+ Contact contact( &addr );
+ // The addressee is converted to: 1) the xml 2) the optional picture 3) the optional logo 4) the optional sound
+ data = contact.saveXML();
+ att.updatePictureAttachment( contact.picture(), contact.pictureAttachmentName() );
+ att.updatePictureAttachment( contact.logo(), contact.logoAttachmentName() );
+ // no way to know the mimetype. The addressee editor allows to attach _any_ kind of file,
+ // and the sound system sorts it out.
+ att.updateAttachment( contact.sound(), contact.soundAttachmentName(), "audio/unknown" );
+ mimetype = contact.isDistributionList() ?
+ s_attachmentMimeTypeDistList : s_attachmentMimeTypeContact;
+ } else {
+ mimetype = s_inlineMimeType;
+ KABC::VCardConverter converter;
+ data = converter.createVCard( addr );
+ subject.prepend( "vCard " ); // as per kolab1 spec
+ }
+ bool rc = kmailUpdate( subResource, sernum, data, mimetype, subject,
+ CustomHeaderMap(),
+ att.attachmentURLs, att.attachmentMimeTypes, att.attachmentNames,
+ att.deletedAttachments );
+ if ( !rc )
+ kdDebug(5650) << "kmailUpdate returned false!" << endl;
+ if ( rc ) {
+ kdDebug(5650) << "kmailUpdate returned, now sernum=" << sernum << " for uid=" << uid << endl;
+ mUidMap[ uid ] = StorageReference( subResource, sernum );
+ // This is ugly, but it's faster than doing
+ // mAddrMap.find(addr.uid()), which would give the same :-(
+ // Reason for this: The Changed attribute of Addressee should
+ // be mutable
+ const_cast<Addressee&>(addr).setChanged( false );
+ }
+
+ for( QValueList<KTempFile *>::Iterator it = att.tempFiles.begin(); it != att.tempFiles.end(); ++it ) {
+ (*it)->setAutoDelete( true );
+ delete (*it);
+ }
+ return rc;
+}
+
+void KABC::ResourceKolab::insertAddressee( const Addressee& addr )
+{
+ const QString uid = addr.uid();
+ //kdDebug(5650) << k_funcinfo << uid << endl;
+ bool ok = false;
+ if ( mUidMap.contains( uid ) ) {
+ mUidsPendingUpdate.append( uid );
+ } else {
+ mUidsPendingAdding.append( uid );
+ }
+
+ ok = kmailUpdateAddressee( addr );
+
+ if ( ok )
+ Resource::insertAddressee( addr );
+}
+
+void KABC::ResourceKolab::removeAddressee( const Addressee& addr )
+{
+ const QString uid = addr.uid();
+ if ( mUidMap.find( uid ) == mUidMap.end() ) return;
+ //kdDebug(5650) << k_funcinfo << uid << endl;
+ const QString resource = mUidMap[ uid ].resource();
+ if ( !subresourceWritable( resource ) ) {
+ kdWarning() << "Wow! Something tried to delete a non-writable addressee! Fix this caller: " << kdBacktrace() << endl;
+ return;
+ }
+ /* The user told us to delete, tell KMail */
+ kmailDeleteIncidence( resource,
+ mUidMap[ uid ].serialNumber() );
+ mUidsPendingDeletion.append( uid );
+ mUidMap.remove( uid );
+
+ Resource::removeAddressee( addr );
+}
+
+/*
+ * These are the DCOP slots that KMail call to notify when something
+ * changed.
+ */
+bool KABC::ResourceKolab::fromKMailAddIncidence( const QString& type,
+ const QString& subResource,
+ Q_UINT32 sernum,
+ int format,
+ const QString& contactXML )
+{
+ // Check if this is a contact
+ if( type != s_kmailContentsType || !subresourceActive( subResource ) )
+ return false;
+
+ // Load contact to find the UID
+ const QString uid = loadContact( contactXML, subResource, sernum,
+ ( KMailICalIface::StorageFormat )format );
+
+ //kdDebug(5650) << k_funcinfo << uid << endl;
+
+ // Emit "addressbook changed" if this comes from kmail and not from the GUI
+ if ( !mUidsPendingAdding.contains( uid )
+ && !mUidsPendingUpdate.contains( uid ) ) {
+ addressBook()->emitAddressBookChanged();
+ } else {
+ mUidsPendingAdding.remove( uid );
+ mUidsPendingUpdate.remove( uid );
+ }
+
+ return true;
+}
+
+void KABC::ResourceKolab::fromKMailDelIncidence( const QString& type,
+ const QString& subResource,
+ const QString& uid )
+{
+ // Check if this is a contact
+ if( type != s_kmailContentsType || !subresourceActive( subResource ) )
+ return;
+
+ //kdDebug(5650) << k_funcinfo << uid << endl;
+
+ // Can't be in both, by contract
+ if ( mUidsPendingDeletion.contains( uid ) ) {
+ mUidsPendingDeletion.remove( uid );
+ } else if ( mUidsPendingUpdate.contains( uid ) ) {
+ // It's good to know if was deleted, but we are waiting on a new one to
+ // replace it, so let's just sit tight.
+ } else {
+ // We didn't trigger this, so KMail did, remove the reference to the uid
+ mAddrMap.remove( uid );
+ mUidMap.remove( uid );
+ addressBook()->emitAddressBookChanged();
+ }
+}
+
+void KABC::ResourceKolab::fromKMailRefresh( const QString& type,
+ const QString& /*subResource*/ )
+{
+ // Check if this is a contact
+ if( type != s_kmailContentsType ) return;
+
+ //kdDebug(5650) << k_funcinfo << endl;
+
+ load(); // ### should call loadSubResource(subResource) probably
+ addressBook()->emitAddressBookChanged();
+}
+
+void KABC::ResourceKolab::fromKMailAddSubresource( const QString& type,
+ const QString& subResource,
+ const QString& label,
+ bool writable,
+ bool )
+{
+ if( type != s_kmailContentsType ) return;
+
+ if ( mSubResources.contains( subResource ) )
+ // Already registered
+ return;
+
+ KConfig config( configFile() );
+ config.setGroup( "Contact" );
+ loadSubResourceConfig( config, subResource, label, writable );
+ loadSubResource( subResource );
+ addressBook()->emitAddressBookChanged();
+ emit signalSubresourceAdded( this, type, subResource );
+}
+
+void KABC::ResourceKolab::fromKMailDelSubresource( const QString& type,
+ const QString& subResource )
+{
+ if( type != s_kmailContentsType ) return;
+
+ if ( !mSubResources.contains( subResource ) )
+ // Not registered
+ return;
+
+ // Ok, it's our job, and we have it here
+ mSubResources.erase( subResource );
+
+ KConfig config( configFile() );
+ config.deleteGroup( subResource );
+ config.sync();
+
+ // Make a list of all uids to remove
+ Kolab::UidMap::ConstIterator mapIt;
+ QStringList uids;
+ for ( mapIt = mUidMap.begin(); mapIt != mUidMap.end(); ++mapIt )
+ if ( mapIt.data().resource() == subResource )
+ // We have a match
+ uids << mapIt.key();
+
+ // Finally delete all the incidences
+ if ( !uids.isEmpty() ) {
+ QStringList::ConstIterator it;
+ for ( it = uids.begin(); it != uids.end(); ++it ) {
+ mAddrMap.remove( *it );
+ mUidMap.remove( *it );
+ }
+
+ addressBook()->emitAddressBookChanged();
+ }
+
+ emit signalSubresourceRemoved( this, type, subResource );
+}
+
+
+
+void KABC::ResourceKolab::fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& /* type */,
+ const QString& folder )
+{
+ // FIXME
+ KMailICalIface::StorageFormat format = KMailICalIface::StorageXML;
+ for( QMap<Q_UINT32, QString>::ConstIterator it = map.begin(); it != map.end(); ++it ) {
+ loadContact( it.data(), folder, it.key(), format );
+ }
+ if ( !addressBook() ){
+ kdDebug(5650) << "asyncLoadResult() : addressBook() returning NULL pointer.\n";
+ }else
+ addressBook()->emitAddressBookChanged();
+}
+
+QStringList KABC::ResourceKolab::subresources() const
+{
+ return mSubResources.keys();
+}
+
+bool KABC::ResourceKolab::subresourceActive( const QString& subresource ) const
+{
+ if ( mSubResources.contains( subresource ) ) {
+ return mSubResources[ subresource ].active();
+ }
+
+ // Safe default bet:
+ kdDebug(5650) << "subresourceActive( " << subresource << " ): Safe bet\n";
+
+ return true;
+}
+
+bool KABC::ResourceKolab::subresourceWritable( const QString& subresource ) const
+{
+ if ( mSubResources.contains( subresource ) ) {
+ return mSubResources[ subresource ].writable();
+ }
+ return false; //better a safe default
+}
+
+int KABC::ResourceKolab::subresourceCompletionWeight( const QString& subresource ) const
+{
+ if ( mSubResources.contains( subresource ) ) {
+ return mSubResources[ subresource ].completionWeight();
+ }
+
+ kdDebug(5650) << "subresourceCompletionWeight( " << subresource << " ): not found, using default\n";
+
+ return 80;
+}
+
+QString KABC::ResourceKolab::subresourceLabel( const QString& subresource ) const
+{
+ if ( mSubResources.contains( subresource ) ) {
+ return mSubResources[ subresource ].label();
+ }
+
+ kdDebug(5650) << "subresourceLabel( " << subresource << " ): not found!\n";
+ return QString::null;
+}
+
+void KABC::ResourceKolab::setSubresourceCompletionWeight( const QString& subresource, int completionWeight )
+{
+ if ( mSubResources.contains( subresource ) ) {
+ mSubResources[ subresource ].setCompletionWeight( completionWeight );
+ } else {
+ kdDebug(5650) << "setSubresourceCompletionWeight: subresource " << subresource << " not found" << endl;
+ }
+}
+
+QMap<QString, QString> KABC::ResourceKolab::uidToResourceMap() const
+{
+ // TODO: Couldn't this be made simpler?
+ QMap<QString, QString> map;
+ Kolab::UidMap::ConstIterator mapIt;
+ for ( mapIt = mUidMap.begin(); mapIt != mUidMap.end(); ++mapIt )
+ map[ mapIt.key() ] = mapIt.data().resource();
+ return map;
+}
+
+void KABC::ResourceKolab::setSubresourceActive( const QString &subresource, bool active )
+{
+ if ( mSubResources.contains( subresource ) ) {
+ mSubResources[ subresource ].setActive( active );
+ load();
+ } else {
+ kdDebug(5650) << "setSubresourceCompletionWeight: subresource " << subresource << " not found" << endl;
+ }
+}
+
+
+/*virtual*/
+bool KABC::ResourceKolab::addSubresource( const QString& label, const QString& parent )
+{
+ return kmailAddSubresource( label, parent, s_kmailContentsType );
+}
+
+/*virtual*/
+bool KABC::ResourceKolab::removeSubresource( const QString& id )
+{
+ return kmailRemoveSubresource( id );
+}
+
+#include "resourcekolab.moc"
diff --git a/kresources/kolab/kabc/resourcekolab.h b/kresources/kolab/kabc/resourcekolab.h
new file mode 100644
index 00000000..e0192268
--- /dev/null
+++ b/kresources/kolab/kabc/resourcekolab.h
@@ -0,0 +1,175 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KABC_RESOURCEKOLAB_H
+#define KABC_RESOURCEKOLAB_H
+
+#include <libkdepim/resourceabc.h>
+#include <dcopobject.h>
+#include "../shared/resourcekolabbase.h"
+#include "../shared/subresource.h"
+#include <kmail/kmailicalIface.h>
+#include <kdepimmacros.h>
+
+namespace KABC {
+
+ class FormatPlugin;
+
+/**
+ * This class implements a KAddressBook resource that keeps its
+ * addresses in an Kolab folder in KMail (or other conforming email
+ * clients).
+ */
+class KDE_EXPORT ResourceKolab : public KPIM::ResourceABC,
+ public Kolab::ResourceKolabBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor
+ */
+ ResourceKolab( const KConfig* );
+
+ /**
+ * Destructor.
+ */
+ virtual ~ResourceKolab();
+
+ /**
+ * Open the contacts list
+ */
+ virtual bool doOpen();
+
+ /**
+ * Request a ticket, you have to pass through save() to
+ * allow locking.
+ */
+ virtual Ticket *requestSaveTicket();
+
+ /**
+ Releases the ticket previousely requested with requestSaveTicket().
+ The resource has to remove its locks in this function.
+ */
+ virtual void releaseSaveTicket( Ticket* );
+
+ /**
+ * Load all addressees to the addressbook
+ */
+ virtual bool load();
+
+ /**
+ * Save all addressees to the addressbook.
+ *
+ * @param ticket The ticket you get by requestSaveTicket()
+ */
+ virtual bool save( Ticket *ticket );
+
+ /**
+ Insert an addressee into the resource.
+ */
+ virtual void insertAddressee( const Addressee& );
+
+ /**
+ * Removes a addressee from resource. This method is mainly
+ * used by record-based resources like LDAP or SQL.
+ */
+ virtual void removeAddressee( const Addressee& addr );
+
+ // Listen to KMail changes in the amount of sub resources
+ void fromKMailAddSubresource( const QString& type, const QString& id,
+ const QString& label, bool writable,
+ bool alarmRelevant );
+ void fromKMailDelSubresource( const QString& type, const QString& id );
+
+ bool fromKMailAddIncidence( const QString& type, const QString& resource,
+ Q_UINT32 sernum, int format, const QString& contact );
+ void fromKMailDelIncidence( const QString& type, const QString& resource,
+ const QString& contact );
+ void fromKMailRefresh( const QString& type, const QString& resource );
+
+ void fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder );
+
+ /// Return the list of subresources.
+ QStringList subresources() const;
+
+ /// Is this subresource active?
+ bool subresourceActive( const QString& ) const;
+ /// Is this subresource writable?
+ virtual bool subresourceWritable( const QString& ) const;
+
+ virtual void setSubresourceActive( const QString &, bool );
+
+ virtual bool addSubresource( const QString&, const QString& );
+
+ virtual bool removeSubresource( const QString& );
+
+ virtual bool canHaveSubresources() const { return true; }
+
+ /// Completion weight for a given subresource
+ virtual int subresourceCompletionWeight( const QString& ) const;
+
+ /// Label for a given subresource
+ virtual QString subresourceLabel( const QString& ) const;
+
+ /// Set completion weight for a given subresource
+ virtual void setSubresourceCompletionWeight( const QString&, int );
+
+ /// Give the uidmap. Used for ordered searching
+ QMap<QString, QString> uidToResourceMap() const;
+
+protected:
+ bool kmailUpdateAddressee( const Addressee& );
+
+ void doClose();
+
+ void loadSubResourceConfig( KConfig& config, const QString& name,
+ const QString& label, bool writable );
+ bool loadSubResource( const QString& subResource );
+ QString loadContact( const QString& contactData, const QString& subResource,
+ Q_UINT32 sernum, const KMailICalIface::StorageFormat format );
+
+ QString configFile() const {
+ return Kolab::ResourceKolabBase::configFile( "kabc" );
+ }
+
+ // The list of subresources
+ Kolab::ResourceMap mSubResources;
+ QString mCachedSubresource;
+ bool mLocked;
+};
+
+}
+
+#endif // KABC_RESOURCEKOLAB_H
diff --git a/kresources/kolab/kabc/resourcekolab_plugin.cpp b/kresources/kolab/kabc/resourcekolab_plugin.cpp
new file mode 100644
index 00000000..be87772b
--- /dev/null
+++ b/kresources/kolab/kabc/resourcekolab_plugin.cpp
@@ -0,0 +1,52 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarlvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "resourcekolab.h"
+
+using namespace Kolab;
+
+class KolabFactory : public KRES::PluginFactoryBase
+{
+ public:
+ KRES::Resource *resource( const KConfig *config )
+ {
+ return new KABC::ResourceKolab( config );
+ }
+
+ KRES::ConfigWidget *configWidget( QWidget* )
+ {
+ return 0;
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY(kabc_kolab,KolabFactory)
+
diff --git a/kresources/kolab/kcal/Makefile.am b/kresources/kolab/kcal/Makefile.am
new file mode 100644
index 00000000..bc0af8dd
--- /dev/null
+++ b/kresources/kolab/kcal/Makefile.am
@@ -0,0 +1,27 @@
+METASOURCES = AUTO
+
+INCLUDES = -I$(top_srcdir)/kresources/kolab/shared -I$(top_srcdir) \
+ -I$(top_builddir)/libkdepim $(all_includes)
+
+# The kolab wizard links to this library too
+lib_LTLIBRARIES = libkcalkolab.la
+
+libkcalkolab_la_SOURCES = incidence.cpp event.cpp task.cpp journal.cpp resourcekolab.cpp
+libkcalkolab_la_LDFLAGS = $(all_libraries) -no-undefined
+libkcalkolab_la_LIBADD = $(top_builddir)/libkcal/libkcal.la \
+ $(top_builddir)/kresources/kolab/shared/libresourcekolabshared.la \
+ -lkresources
+
+kde_module_LTLIBRARIES = kcal_kolab.la
+
+kcal_kolab_la_SOURCES = resourcekolab_plugin.cpp
+kcal_kolab_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined
+kcal_kolab_la_LIBADD = libkcalkolab.la
+
+servicedir = $(kde_servicesdir)/kresources/kcal
+service_DATA = kolab.desktop
+
+install-data-local: $(srcdir)/../uninstall.desktop
+ $(mkinstalldirs) $(DESTDIR)$(servicedir)
+ $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop
+
diff --git a/kresources/kolab/kcal/event.cpp b/kresources/kolab/kcal/event.cpp
new file mode 100644
index 00000000..a53ca416
--- /dev/null
+++ b/kresources/kolab/kcal/event.cpp
@@ -0,0 +1,220 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "event.h"
+
+#include <libkcal/event.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCal::Event* Event::xmlToEvent( const QString& xml, const QString& tz, KCal::ResourceKolab* res,
+ const QString& subResource, Q_UINT32 sernum )
+{
+ Event event( res, subResource, sernum, tz );
+ event.load( xml );
+ KCal::Event* kcalEvent = new KCal::Event();
+ event.saveTo( kcalEvent );
+ return kcalEvent;
+}
+
+QString Event::eventToXML( KCal::Event* kcalEvent, const QString& tz )
+{
+ Event event( 0, QString::null, 0, tz, kcalEvent );
+ return event.saveXML();
+}
+
+Event::Event( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz, KCal::Event* event )
+ : Incidence( res, subResource, sernum, tz ),
+ mShowTimeAs( KCal::Event::Opaque ), mHasEndDate( false )
+{
+ if ( event )
+ setFields( event );
+}
+
+Event::~Event()
+{
+}
+
+void Event::setTransparency( KCal::Event::Transparency transparency )
+{
+ mShowTimeAs = transparency;
+}
+
+KCal::Event::Transparency Event::transparency() const
+{
+ return mShowTimeAs;
+}
+
+void Event::setEndDate( const QDateTime& date )
+{
+ mEndDate = date;
+ mHasEndDate = true;
+ if ( mFloatingStatus == AllDay )
+ kdDebug() << "ERROR: Time on end date but no time on the event\n";
+ mFloatingStatus = HasTime;
+}
+
+void Event::setEndDate( const QDate& date )
+{
+ mEndDate = date;
+ mHasEndDate = true;
+ if ( mFloatingStatus == HasTime )
+ kdDebug() << "ERROR: No time on end date but time on the event\n";
+ mFloatingStatus = AllDay;
+}
+
+void Event::setEndDate( const QString& endDate )
+{
+ if ( endDate.length() > 10 )
+ // This is a date + time
+ setEndDate( stringToDateTime( endDate ) );
+ else
+ // This is only a date
+ setEndDate( stringToDate( endDate ) );
+}
+
+QDateTime Event::endDate() const
+{
+ return mEndDate;
+}
+
+bool Event::loadAttribute( QDomElement& element )
+{
+ // This method doesn't handle the color-label tag yet
+ QString tagName = element.tagName();
+
+ if ( tagName == "show-time-as" ) {
+ // TODO: Support tentative and outofoffice
+ if ( element.text() == "free" )
+ setTransparency( KCal::Event::Transparent );
+ else
+ setTransparency( KCal::Event::Opaque );
+ } else if ( tagName == "end-date" )
+ setEndDate( element.text() );
+ else
+ return Incidence::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Event::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ Incidence::saveAttributes( element );
+
+ // TODO: Support tentative and outofoffice
+ if ( transparency() == KCal::Event::Transparent )
+ writeString( element, "show-time-as", "free" );
+ else
+ writeString( element, "show-time-as", "busy" );
+ if ( mHasEndDate ) {
+ if ( mFloatingStatus == HasTime )
+ writeString( element, "end-date", dateTimeToString( endDate() ) );
+ else
+ writeString( element, "end-date", dateToString( endDate().date() ) );
+ }
+
+ return true;
+}
+
+
+bool Event::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "event" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected event",
+ top.tagName().ascii() );
+ return false;
+ }
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ loadAttribute( e );
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ loadAttachments();
+ return true;
+}
+
+QString Event::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "event" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Event::setFields( const KCal::Event* event )
+{
+ Incidence::setFields( event );
+
+ if ( event->hasEndDate() ) {
+ if ( event->doesFloat() ) {
+ // This is a floating event. Don't timezone move this one
+ mFloatingStatus = AllDay;
+ setEndDate( event->dtEnd().date() );
+ } else {
+ mFloatingStatus = HasTime;
+ setEndDate( localToUTC( event->dtEnd() ) );
+ }
+ } else
+ mHasEndDate = false;
+ setTransparency( event->transparency() );
+}
+
+void Event::saveTo( KCal::Event* event )
+{
+ Incidence::saveTo( event );
+
+ event->setHasEndDate( mHasEndDate );
+ if ( mHasEndDate ) {
+ if ( mFloatingStatus == AllDay )
+ // This is a floating event. Don't timezone move this one
+ event->setDtEnd( endDate() );
+ else
+ event->setDtEnd( utcToLocal( endDate() ) );
+ }
+ event->setTransparency( transparency() );
+}
diff --git a/kresources/kolab/kcal/event.h b/kresources/kolab/kcal/event.h
new file mode 100644
index 00000000..ad33db52
--- /dev/null
+++ b/kresources/kolab/kcal/event.h
@@ -0,0 +1,102 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KOLAB_EVENT_H
+#define KOLAB_EVENT_H
+
+#include "incidence.h"
+
+#include <libkcal/event.h>
+
+class QDomElement;
+
+
+namespace Kolab {
+
+/**
+ * This class represents an event, and knows how to load/save it
+ * from/to XML, and from/to a KCal::Event.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Event : public Incidence {
+public:
+ /// Use this to parse an xml string to a event entry
+ /// The caller is responsible for deleting the returned event
+ static KCal::Event* xmlToEvent( const QString& xml, const QString& tz, KCal::ResourceKolab* res = 0,
+ const QString& subResource = QString::null, Q_UINT32 sernum = 0 );
+
+ /// Use this to get an xml string describing this event entry
+ static QString eventToXML( KCal::Event*, const QString& tz );
+
+ /// Create a event object and
+ explicit Event( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz, KCal::Event* event = 0 );
+ virtual ~Event();
+
+ void saveTo( KCal::Event* event );
+
+ virtual QString type() const { return "Event"; }
+
+ virtual void setTransparency( KCal::Event::Transparency transparency );
+ virtual KCal::Event::Transparency transparency() const;
+
+ virtual void setEndDate( const QDateTime& date );
+ virtual void setEndDate( const QDate& date );
+ virtual void setEndDate( const QString& date );
+ virtual QDateTime endDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this event by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this event to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical incidence
+ void setFields( const KCal::Event* );
+
+ KCal::Event::Transparency mShowTimeAs;
+ QDateTime mEndDate;
+ bool mHasEndDate;
+};
+
+}
+
+#endif // KOLAB_EVENT_H
diff --git a/kresources/kolab/kcal/incidence.cpp b/kresources/kolab/kcal/incidence.cpp
new file mode 100644
index 00000000..8c74e3bd
--- /dev/null
+++ b/kresources/kolab/kcal/incidence.cpp
@@ -0,0 +1,845 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "incidence.h"
+#include "resourcekolab.h"
+
+#include <qfile.h>
+#include <qvaluelist.h>
+
+#include <libkcal/journal.h>
+#include <korganizer/version.h>
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+using namespace Kolab;
+
+
+Incidence::Incidence( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz )
+ : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false ),
+ mRevision( 0 ),
+ mResource( res ),
+ mSubResource( subResource ),
+ mSernum( sernum )
+{
+}
+
+Incidence::~Incidence()
+{
+}
+
+void Incidence::setSummary( const QString& summary )
+{
+ mSummary = summary;
+}
+
+QString Incidence::summary() const
+{
+ return mSummary;
+}
+
+void Incidence::setLocation( const QString& location )
+{
+ mLocation = location;
+}
+
+QString Incidence::location() const
+{
+ return mLocation;
+}
+
+void Incidence::setOrganizer( const Email& organizer )
+{
+ mOrganizer = organizer;
+}
+
+KolabBase::Email Incidence::organizer() const
+{
+ return mOrganizer;
+}
+
+void Incidence::setStartDate( const QDateTime& startDate )
+{
+ mStartDate = startDate;
+ if ( mFloatingStatus == AllDay )
+ kdDebug() << "ERROR: Time on start date but no time on the event\n";
+ mFloatingStatus = HasTime;
+}
+
+void Incidence::setStartDate( const QDate& startDate )
+{
+ mStartDate = startDate;
+ if ( mFloatingStatus == HasTime )
+ kdDebug() << "ERROR: No time on start date but time on the event\n";
+ mFloatingStatus = AllDay;
+}
+
+void Incidence::setStartDate( const QString& startDate )
+{
+ if ( startDate.length() > 10 )
+ // This is a date + time
+ setStartDate( stringToDateTime( startDate ) );
+ else
+ // This is only a date
+ setStartDate( stringToDate( startDate ) );
+}
+
+QDateTime Incidence::startDate() const
+{
+ return mStartDate;
+}
+
+void Incidence::setAlarm( float alarm )
+{
+ mAlarm = alarm;
+ mHasAlarm = true;
+}
+
+float Incidence::alarm() const
+{
+ return mAlarm;
+}
+
+Incidence::Recurrence Incidence::recurrence() const
+{
+ return mRecurrence;
+}
+
+void Incidence::addAttendee( const Attendee& attendee )
+{
+ mAttendees.append( attendee );
+}
+
+QValueList<Incidence::Attendee>& Incidence::attendees()
+{
+ return mAttendees;
+}
+
+const QValueList<Incidence::Attendee>& Incidence::attendees() const
+{
+ return mAttendees;
+}
+
+void Incidence::setInternalUID( const QString& iuid )
+{
+ mInternalUID = iuid;
+}
+
+QString Incidence::internalUID() const
+{
+ return mInternalUID;
+}
+
+void Incidence::setRevision( int revision )
+{
+ mRevision = revision;
+}
+
+int Incidence::revision() const
+{
+ return mRevision;
+}
+
+bool Incidence::loadAttendeeAttribute( QDomElement& element,
+ Attendee& attendee )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "display-name" )
+ attendee.displayName = e.text();
+ else if ( tagName == "smtp-address" )
+ attendee.smtpAddress = e.text();
+ else if ( tagName == "status" )
+ attendee.status = e.text();
+ else if ( tagName == "request-response" )
+ // This sets reqResp to false, if the text is "false". Otherwise it
+ // sets it to true. This means the default setting is true.
+ attendee.requestResponse = ( e.text().lower() != "false" );
+ else if ( tagName == "invitation-sent" )
+ // Like above, only this defaults to false
+ attendee.invitationSent = ( e.text().lower() != "true" );
+ else if ( tagName == "role" )
+ attendee.role = e.text();
+ else if ( tagName == "delegated-to" )
+ attendee.delegate = e.text();
+ else if ( tagName == "delegated-from" )
+ attendee.delegator = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ return true;
+}
+
+void Incidence::saveAttendeeAttribute( QDomElement& element,
+ const Attendee& attendee ) const
+{
+ QDomElement e = element.ownerDocument().createElement( "attendee" );
+ element.appendChild( e );
+ writeString( e, "display-name", attendee.displayName );
+ writeString( e, "smtp-address", attendee.smtpAddress );
+ writeString( e, "status", attendee.status );
+ writeString( e, "request-response",
+ ( attendee.requestResponse ? "true" : "false" ) );
+ writeString( e, "invitation-sent",
+ ( attendee.invitationSent ? "true" : "false" ) );
+ writeString( e, "role", attendee.role );
+ writeString( e, "delegated-to", attendee.delegate );
+ writeString( e, "delegated-from", attendee.delegator );
+}
+
+void Incidence::saveAttendees( QDomElement& element ) const
+{
+ QValueList<Attendee>::ConstIterator it = mAttendees.begin();
+ for ( ; it != mAttendees.end(); ++it )
+ saveAttendeeAttribute( element, *it );
+}
+
+void Incidence::saveAttachments( QDomElement& element ) const
+{
+ KCal::Attachment::List::ConstIterator it = mAttachments.begin();
+ for ( ; it != mAttachments.end(); ++it ) {
+ KCal::Attachment *a = (*it);
+ if ( a->isUri() ) {
+ writeString( element, "link-attachment", a->uri() );
+ } else if ( a->isBinary() ) {
+ writeString( element, "inline-attachment", a->label() );
+ }
+ }
+}
+
+void Incidence::saveRecurrence( QDomElement& element ) const
+{
+ QDomElement e = element.ownerDocument().createElement( "recurrence" );
+ element.appendChild( e );
+ e.setAttribute( "cycle", mRecurrence.cycle );
+ if ( !mRecurrence.type.isEmpty() )
+ e.setAttribute( "type", mRecurrence.type );
+ writeString( e, "interval", QString::number( mRecurrence.interval ) );
+ for( QStringList::ConstIterator it = mRecurrence.days.begin(); it != mRecurrence.days.end(); ++it ) {
+ writeString( e, "day", *it );
+ }
+ if ( !mRecurrence.dayNumber.isEmpty() )
+ writeString( e, "daynumber", mRecurrence.dayNumber );
+ if ( !mRecurrence.month.isEmpty() )
+ writeString( e, "month", mRecurrence.month );
+ if ( !mRecurrence.rangeType.isEmpty() ) {
+ QDomElement range = element.ownerDocument().createElement( "range" );
+ e.appendChild( range );
+ range.setAttribute( "type", mRecurrence.rangeType );
+ QDomText t = element.ownerDocument().createTextNode( mRecurrence.range );
+ range.appendChild( t );
+ }
+ for( QValueList<QDate>::ConstIterator it = mRecurrence.exclusions.begin();
+ it != mRecurrence.exclusions.end(); ++it ) {
+ writeString( e, "exclusion", dateToString( *it ) );
+ }
+}
+
+void Incidence::loadRecurrence( const QDomElement& element )
+{
+ mRecurrence.interval = 0;
+ mRecurrence.cycle = element.attribute( "cycle" );
+ mRecurrence.type = element.attribute( "type" );
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "interval" )
+ mRecurrence.interval = e.text().toInt();
+ else if ( tagName == "day" ) // can be present multiple times
+ mRecurrence.days.append( e.text() );
+ else if ( tagName == "daynumber" )
+ mRecurrence.dayNumber = e.text();
+ else if ( tagName == "month" )
+ mRecurrence.month = e.text();
+ else if ( tagName == "range" ) {
+ mRecurrence.rangeType = e.attribute( "type" );
+ mRecurrence.range = e.text();
+ } else if ( tagName == "exclusion" ) {
+ mRecurrence.exclusions.append( stringToDate( e.text() ) );
+ } else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ }
+ }
+}
+
+bool Incidence::loadAttribute( QDomElement& element )
+{
+ QString tagName = element.tagName();
+
+ if ( tagName == "summary" )
+ setSummary( element.text() );
+ else if ( tagName == "location" )
+ setLocation( element.text() );
+ else if ( tagName == "organizer" ) {
+ Email email;
+ if ( loadEmailAttribute( element, email ) ) {
+ setOrganizer( email );
+ return true;
+ } else
+ return false;
+ } else if ( tagName == "start-date" )
+ setStartDate( element.text() );
+ else if ( tagName == "recurrence" )
+ loadRecurrence( element );
+ else if ( tagName == "attendee" ) {
+ Attendee attendee;
+ if ( loadAttendeeAttribute( element, attendee ) ) {
+ addAttendee( attendee );
+ return true;
+ } else
+ return false;
+ } else if ( tagName == "link-attachment" ) {
+ mAttachments.push_back( new KCal::Attachment( element.text() ) );
+ } else if ( tagName == "alarm" )
+ // Alarms should be minutes before. Libkcal uses event time + alarm time
+ setAlarm( - element.text().toInt() );
+ else if ( tagName == "x-kde-internaluid" )
+ setInternalUID( element.text() );
+ else if ( tagName == "revision" ) {
+ bool ok;
+ int revision = element.text().toInt( &ok );
+ if ( ok )
+ setRevision( revision );
+ } else if ( tagName == "x-custom" )
+ loadCustomAttributes( element );
+ else {
+ bool ok = KolabBase::loadAttribute( element );
+ if ( !ok ) {
+ // Unhandled tag - save for later storage
+ kdDebug() << "Saving unhandled tag " << element.tagName() << endl;
+ Custom c;
+ c.key = QCString( "X-KDE-KolabUnhandled-" ) + element.tagName().latin1();
+ c.value = element.text();
+ mCustomList.append( c );
+ }
+ }
+ // We handled this
+ return true;
+}
+
+bool Incidence::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ if ( mFloatingStatus == HasTime )
+ writeString( element, "start-date", dateTimeToString( startDate() ) );
+ else
+ writeString( element, "start-date", dateToString( startDate().date() ) );
+ writeString( element, "summary", summary() );
+ writeString( element, "location", location() );
+ saveEmailAttribute( element, organizer(), "organizer" );
+ if ( !mRecurrence.cycle.isEmpty() )
+ saveRecurrence( element );
+ saveAttendees( element );
+ saveAttachments( element );
+ if ( mHasAlarm ) {
+ // Alarms should be minutes before. Libkcal uses event time + alarm time
+ int alarmTime = qRound( -alarm() );
+ writeString( element, "alarm", QString::number( alarmTime ) );
+ }
+ writeString( element, "x-kde-internaluid", internalUID() );
+ writeString( element, "revision", QString::number( revision() ) );
+ saveCustomAttributes( element );
+ return true;
+}
+
+void Incidence::saveCustomAttributes( QDomElement& element ) const
+{
+ QValueList<Custom>::ConstIterator it = mCustomList.begin();
+ for ( ; it != mCustomList.end(); ++it ) {
+ QString key = (*it).key;
+ Q_ASSERT( !key.isEmpty() );
+ if ( key.startsWith( "X-KDE-KolabUnhandled-" ) ) {
+ key = key.mid( strlen( "X-KDE-KolabUnhandled-" ) );
+ writeString( element, key, (*it).value );
+ } else {
+ // Let's use attributes so that other tag-preserving-code doesn't need sub-elements
+ QDomElement e = element.ownerDocument().createElement( "x-custom" );
+ element.appendChild( e );
+ e.setAttribute( "key", key );
+ e.setAttribute( "value", (*it).value );
+ }
+ }
+}
+
+void Incidence::loadCustomAttributes( QDomElement& element )
+{
+ Custom custom;
+ custom.key = element.attribute( "key" ).latin1();
+ custom.value = element.attribute( "value" );
+ mCustomList.append( custom );
+}
+
+static KCal::Attendee::PartStat attendeeStringToStatus( const QString& s )
+{
+ if ( s == "none" )
+ return KCal::Attendee::NeedsAction;
+ if ( s == "tentative" )
+ return KCal::Attendee::Tentative;
+ if ( s == "declined" )
+ return KCal::Attendee::Declined;
+ if ( s == "delegated" )
+ return KCal::Attendee::Delegated;
+
+ // Default:
+ return KCal::Attendee::Accepted;
+}
+
+static QString attendeeStatusToString( KCal::Attendee::PartStat status )
+{
+ switch( status ) {
+ case KCal::Attendee::NeedsAction:
+ return "none";
+ case KCal::Attendee::Accepted:
+ return "accepted";
+ case KCal::Attendee::Declined:
+ return "declined";
+ case KCal::Attendee::Tentative:
+ return "tentative";
+ case KCal::Attendee::Delegated:
+ return "delegated";
+ case KCal::Attendee::Completed:
+ case KCal::Attendee::InProcess:
+ // These don't have any meaning in the Kolab format, so just use:
+ return "accepted";
+ }
+
+ // Default for the case that there are more added later:
+ return "accepted";
+}
+
+static KCal::Attendee::Role attendeeStringToRole( const QString& s )
+{
+ if ( s == "optional" )
+ return KCal::Attendee::OptParticipant;
+ if ( s == "resource" )
+ return KCal::Attendee::NonParticipant;
+ return KCal::Attendee::ReqParticipant;
+}
+
+static QString attendeeRoleToString( KCal::Attendee::Role role )
+{
+ switch( role ) {
+ case KCal::Attendee::ReqParticipant:
+ return "required";
+ case KCal::Attendee::OptParticipant:
+ return "optional";
+ case KCal::Attendee::Chair:
+ // We don't have the notion of chair, so use
+ return "required";
+ case KCal::Attendee::NonParticipant:
+ // In Kolab, a non-participant is a resource
+ return "resource";
+ }
+
+ // Default for the case that there are more added later:
+ return "required";
+}
+
+static const char *s_weekDayName[] =
+{
+ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"
+};
+
+static const char *s_monthName[] =
+{
+ "january", "february", "march", "april", "may", "june", "july",
+ "august", "september", "october", "november", "december"
+};
+
+void Incidence::setRecurrence( KCal::Recurrence* recur )
+{
+ mRecurrence.interval = recur->frequency();
+ switch ( recur->recurrenceType() ) {
+ case KCal::Recurrence::rMinutely: // Not handled by the kolab XML
+ mRecurrence.cycle = "minutely";
+ break;
+ case KCal::Recurrence::rHourly: // Not handled by the kolab XML
+ mRecurrence.cycle = "hourly";
+ break;
+ case KCal::Recurrence::rDaily:
+ mRecurrence.cycle = "daily";
+ break;
+ case KCal::Recurrence::rWeekly: // every X weeks
+ mRecurrence.cycle = "weekly";
+ {
+ QBitArray arr = recur->days();
+ for ( uint idx = 0 ; idx < 7 ; ++idx )
+ if ( arr.testBit( idx ) )
+ mRecurrence.days.append( s_weekDayName[idx] );
+ }
+ break;
+ case KCal::Recurrence::rMonthlyPos: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "weekday";
+ QValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->monthPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+ // TODO: Handle multiple days in the same week
+ mRecurrence.dayNumber = QString::number( monthPos.pos() );
+ mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+ // Not (properly) handled(?): monthPos.negative (nth days before end of month)
+ }
+ break;
+ }
+ case KCal::Recurrence::rMonthlyDay: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "daynumber";
+ QValueList<int> monthDays = recur->monthDays();
+ // ####### Kolab XML limitation: only the first month day is used
+ if ( !monthDays.isEmpty() )
+ mRecurrence.dayNumber = QString::number( monthDays.first() );
+ break;
+ }
+ case KCal::Recurrence::rYearlyMonth: // (day n of Month Y)
+ {
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "monthday";
+ QValueList<int> rmd = recur->yearDates();
+ int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day();
+ mRecurrence.dayNumber = QString::number( day );
+ QValueList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ break;
+ }
+ case KCal::Recurrence::rYearlyDay: // YearlyDay (day N of the year). Not supported by Outlook
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "yearday";
+ mRecurrence.dayNumber = QString::number( recur->yearDays().first() );
+ break;
+ case KCal::Recurrence::rYearlyPos: // (weekday X of week N of month Y)
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "weekday";
+ QValueList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ QValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->yearPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+ // TODO: Handle multiple days in the same week
+ mRecurrence.dayNumber = QString::number( monthPos.pos() );
+ mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+
+ //mRecurrence.dayNumber = QString::number( *recur->yearNums().getFirst() );
+ // Not handled: monthPos.negative (nth days before end of month)
+ }
+ break;
+ }
+ int howMany = recur->duration();
+ if ( howMany > 0 ) {
+ mRecurrence.rangeType = "number";
+ mRecurrence.range = QString::number( howMany );
+ } else if ( howMany == 0 ) {
+ mRecurrence.rangeType = "date";
+ mRecurrence.range = dateToString( recur->endDate() );
+ } else {
+ mRecurrence.rangeType = "none";
+ }
+}
+
+void Incidence::setFields( const KCal::Incidence* incidence )
+{
+ KolabBase::setFields( incidence );
+
+ if ( incidence->doesFloat() ) {
+ // This is a floating event. Don't timezone move this one
+ mFloatingStatus = AllDay;
+ setStartDate( incidence->dtStart().date() );
+ } else {
+ mFloatingStatus = HasTime;
+ setStartDate( localToUTC( incidence->dtStart() ) );
+ }
+
+ setSummary( incidence->summary() );
+ setLocation( incidence->location() );
+
+ // Alarm
+ mHasAlarm = false; // Will be set to true, if we actually have one
+ if ( incidence->isAlarmEnabled() ) {
+ const KCal::Alarm::List& alarms = incidence->alarms();
+ if ( !alarms.isEmpty() ) {
+ const KCal::Alarm* alarm = alarms.first();
+ if ( alarm->hasStartOffset() ) {
+ int dur = alarm->startOffset().asSeconds();
+ setAlarm( (float)dur / 60.0 );
+ }
+ }
+ }
+
+ Email org( incidence->organizer().name(), incidence->organizer().email() );
+ setOrganizer( org );
+
+ // Attendees:
+ KCal::Attendee::List attendees = incidence->attendees();
+ KCal::Attendee::List::ConstIterator it;
+ for ( it = attendees.begin(); it != attendees.end(); ++it ) {
+ KCal::Attendee* kcalAttendee = *it;
+ Attendee attendee;
+
+ attendee.displayName = kcalAttendee->name();
+ attendee.smtpAddress = kcalAttendee->email();
+ attendee.status = attendeeStatusToString( kcalAttendee->status() );
+ attendee.requestResponse = kcalAttendee->RSVP();
+ // TODO: KCal::Attendee::mFlag is not accessible
+ // attendee.invitationSent = kcalAttendee->mFlag;
+ // DF: Hmm? mFlag is set to true and never used at all.... Did you mean another field?
+ attendee.role = attendeeRoleToString( kcalAttendee->role() );
+ attendee.delegate = kcalAttendee->delegate();
+ attendee.delegator = kcalAttendee->delegator();
+
+ addAttendee( attendee );
+ }
+
+ mAttachments.clear();
+
+ // Attachments
+ KCal::Attachment::List attachments = incidence->attachments();
+ KCal::Attachment::List::ConstIterator it2;
+ for ( it2 = attachments.begin(); it2 != attachments.end(); ++it2 ) {
+ KCal::Attachment *a = *it2;
+ mAttachments.push_back( a );
+ }
+
+ if ( incidence->doesRecur() ) {
+ setRecurrence( incidence->recurrence() );
+ mRecurrence.exclusions = incidence->recurrence()->exDates();
+ }
+
+ // Handle the scheduling ID
+ if ( incidence->schedulingID() == incidence->uid() ) {
+ // There is no scheduling ID
+ setInternalUID( QString::null );
+ } else {
+ // We've internally been using a different uid, so save that as the
+ // temporary (internal) uid and restore the original uid, the one that
+ // is used in the folder and the outside world
+ setUid( incidence->schedulingID() );
+ setInternalUID( incidence->uid() );
+ }
+
+ if ( incidence->pilotId() != 0 )
+ setPilotSyncId( incidence->pilotId() );
+
+ setPilotSyncStatus( incidence->syncStatus() );
+
+ // Unhandled tags and other custom properties (see libkcal/customproperties.h)
+ const QMap<QCString, QString> map = incidence->customProperties();
+ QMap<QCString, QString>::ConstIterator cit = map.begin();
+ for ( ; cit != map.end() ; ++cit ) {
+ Custom c;
+ c.key = cit.key();
+ c.value = cit.data();
+ mCustomList.append( c );
+ }
+}
+
+static QBitArray daysListToBitArray( const QStringList& days )
+{
+ QBitArray arr( 7 );
+ arr.fill( false );
+ for( QStringList::ConstIterator it = days.begin(); it != days.end(); ++it ) {
+ for ( uint i = 0; i < 7 ; ++i )
+ if ( *it == s_weekDayName[i] )
+ arr.setBit( i, true );
+ }
+ return arr;
+}
+
+
+void Incidence::saveTo( KCal::Incidence* incidence )
+{
+ KolabBase::saveTo( incidence );
+
+ if ( mFloatingStatus == AllDay ) {
+ // This is a floating event. Don't timezone move this one
+ incidence->setDtStart( startDate() );
+ incidence->setFloats( true );
+ } else {
+ incidence->setDtStart( utcToLocal( startDate() ) );
+ incidence->setFloats( false );
+ }
+
+ incidence->setSummary( summary() );
+ incidence->setLocation( location() );
+
+ if ( mHasAlarm ) {
+ KCal::Alarm* alarm = incidence->newAlarm();
+ alarm->setStartOffset( qRound( mAlarm * 60.0 ) );
+ alarm->setEnabled( true );
+ }
+
+ if ( organizer().displayName.isEmpty() )
+ incidence->setOrganizer( organizer().smtpAddress );
+ else
+ incidence->setOrganizer( organizer().displayName + "<"
+ + organizer().smtpAddress + ">" );
+
+ incidence->clearAttendees();
+ QValueList<Attendee>::ConstIterator it;
+ for ( it = mAttendees.begin(); it != mAttendees.end(); ++it ) {
+ KCal::Attendee::PartStat status = attendeeStringToStatus( (*it).status );
+ KCal::Attendee::Role role = attendeeStringToRole( (*it).role );
+ KCal::Attendee* attendee = new KCal::Attendee( (*it).displayName,
+ (*it).smtpAddress,
+ (*it).requestResponse,
+ status, role );
+ attendee->setDelegate( (*it).delegate );
+ attendee->setDelegator( (*it).delegator );
+ incidence->addAttendee( attendee );
+ }
+
+ incidence->clearAttachments();
+ KCal::Attachment::List::ConstIterator it2;
+ for ( it2 = mAttachments.begin(); it2 != mAttachments.end(); ++it2 ) {
+ KCal::Attachment *a = (*it2);
+ // TODO should we copy?
+ incidence->addAttachment( a );
+ }
+
+ if ( !mRecurrence.cycle.isEmpty() ) {
+ KCal::Recurrence* recur = incidence->recurrence(); // yeah, this creates it
+ // done below recur->setFrequency( mRecurrence.interval );
+ if ( mRecurrence.cycle == "minutely" ) {
+ recur->setMinutely( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "hourly" ) {
+ recur->setHourly( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "daily" ) {
+ recur->setDaily( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "weekly" ) {
+ QBitArray rDays = daysListToBitArray( mRecurrence.days );
+ recur->setWeekly( mRecurrence.interval, rDays );
+ } else if ( mRecurrence.cycle == "monthly" ) {
+ recur->setMonthly( mRecurrence.interval );
+ if ( mRecurrence.type == "weekday" ) {
+ recur->addMonthlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+ } else if ( mRecurrence.type == "daynumber" ) {
+ recur->addMonthlyDate( mRecurrence.dayNumber.toInt() );
+ } else kdWarning() << "Unhandled monthly recurrence type " << mRecurrence.type << endl;
+ } else if ( mRecurrence.cycle == "yearly" ) {
+ recur->setYearly( mRecurrence.interval );
+ if ( mRecurrence.type == "monthday" ) {
+ recur->addYearlyDate( mRecurrence.dayNumber.toInt() );
+ for ( int i = 0; i < 12; ++i )
+ if ( s_monthName[ i ] == mRecurrence.month )
+ recur->addYearlyMonth( i+1 );
+ } else if ( mRecurrence.type == "yearday" ) {
+ recur->addYearlyDay( mRecurrence.dayNumber.toInt() );
+ } else if ( mRecurrence.type == "weekday" ) {
+ for ( int i = 0; i < 12; ++i )
+ if ( s_monthName[ i ] == mRecurrence.month )
+ recur->addYearlyMonth( i+1 );
+ recur->addYearlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+ } else kdWarning() << "Unhandled yearly recurrence type " << mRecurrence.type << endl;
+ } else kdWarning() << "Unhandled recurrence cycle " << mRecurrence.cycle << endl;
+
+ if ( mRecurrence.rangeType == "number" ) {
+ recur->setDuration( mRecurrence.range.toInt() );
+ } else if ( mRecurrence.rangeType == "date" ) {
+ recur->setEndDate( stringToDate( mRecurrence.range ) );
+ } // "none" is default since tje set*ly methods set infinite recurrence
+
+ incidence->recurrence()->setExDates( mRecurrence.exclusions );
+
+ }
+ /* If we've stored a uid to be used internally instead of the real one
+ * (to deal with duplicates of events in different folders) before, then
+ * restore it, so it does not change. Keep the original uid around for
+ * scheduling purposes. */
+ if ( !internalUID().isEmpty() ) {
+ incidence->setUid( internalUID() );
+ incidence->setSchedulingID( uid() );
+ }
+ if ( hasPilotSyncId() )
+ incidence->setPilotId( pilotSyncId() );
+ if ( hasPilotSyncStatus() )
+ incidence->setSyncStatus( pilotSyncStatus() );
+
+ for( QValueList<Custom>::ConstIterator it = mCustomList.begin(); it != mCustomList.end(); ++it ) {
+ incidence->setNonKDECustomProperty( (*it).key, (*it).value );
+ }
+
+}
+
+void Incidence::loadAttachments()
+{
+ QStringList attachments;
+ if ( mResource->kmailListAttachments( attachments, mSubResource, mSernum ) ) {
+ for ( QStringList::ConstIterator it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
+ QByteArray data;
+ KURL url;
+ if ( mResource->kmailGetAttachment( url, mSubResource, mSernum, *it ) && !url.isEmpty() ) {
+ QFile f( url.path() );
+ if ( f.open( IO_ReadOnly ) ) {
+ data = f.readAll();
+ QString mimeType;
+ if ( !mResource->kmailAttachmentMimetype( mimeType, mSubResource, mSernum, *it ) )
+ mimeType = "application/octet-stream";
+ KCal::Attachment *a = new KCal::Attachment( KCodecs::base64Encode( data ).data(), mimeType );
+ a->setLabel( *it );
+ mAttachments.append( a );
+ f.close();
+ }
+ f.remove();
+ }
+ }
+ }
+}
+
+QString Incidence::productID() const
+{
+ return QString( "KOrganizer " ) + korgVersion + ", Kolab resource";
+}
+
+// Unhandled KCal::Incidence fields:
+// revision, status (unused), priority (done in tasks), attendee.uid,
+// mComments, mReadOnly
+
diff --git a/kresources/kolab/kcal/incidence.h b/kresources/kolab/kcal/incidence.h
new file mode 100644
index 00000000..de549328
--- /dev/null
+++ b/kresources/kolab/kcal/incidence.h
@@ -0,0 +1,174 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KOLAB_INCIDENCE_H
+#define KOLAB_INCIDENCE_H
+
+#include <kolabbase.h>
+
+class QDomElement;
+
+namespace KCal {
+ class Incidence;
+ class Recurrence;
+ class Attachment;
+ class ResourceKolab;
+}
+
+namespace Kolab {
+
+/**
+ * This abstract class represents an incidence which has the shared
+ * fields, of events and tasks and knows how to load/save these
+ * from/to XML, and from/to a KCal::Incidence.
+ */
+class Incidence : public KolabBase {
+public:
+ struct Recurrence {
+ QString cycle;
+ QString type;
+ int interval;
+ QStringList days; // list of days-of-the-week
+ QString dayNumber;
+ QString month;
+ QString rangeType;
+ QString range; // date or number or nothing
+ QValueList<QDate> exclusions;
+ };
+
+ struct Attendee : Email {
+ Attendee() : requestResponse( true ), invitationSent( false ) {}
+ QString status;
+ bool requestResponse;
+ bool invitationSent;
+ QString role;
+ QString delegate;
+ QString delegator;
+ };
+
+ explicit Incidence( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz );
+ virtual ~Incidence();
+
+ void saveTo( KCal::Incidence* incidence );
+
+ virtual void setSummary( const QString& summary );
+ virtual QString summary() const;
+
+ virtual void setLocation( const QString& location );
+ virtual QString location() const;
+
+ virtual void setOrganizer( const Email& organizer );
+ virtual Email organizer() const;
+
+ virtual void setStartDate( const QDateTime& startDate );
+ virtual void setStartDate( const QDate& startDate );
+ virtual void setStartDate( const QString& startDate );
+ virtual QDateTime startDate() const;
+
+ virtual void setAlarm( float alarm );
+ virtual float alarm() const;
+
+ virtual void setRecurrence( KCal::Recurrence* recur );
+ virtual Recurrence recurrence() const;
+
+ virtual void addAttendee( const Attendee& attendee );
+ QValueList<Attendee>& attendees();
+ const QValueList<Attendee>& attendees() const;
+
+ /**
+ * The internal uid is used as the uid inside KOrganizer whenever
+ * two or more events with the same uid appear, which KOrganizer
+ * can't handle. To avoid keep that interal uid from changing all the
+ * time, it is persisted in the XML between a save and the next load.
+ */
+ void setInternalUID( const QString& iuid );
+ QString internalUID() const;
+
+ virtual void setRevision( int );
+ virtual int revision() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+protected:
+ enum FloatingStatus { Unset, AllDay, HasTime };
+
+ // Read all known fields from this ical incidence
+ void setFields( const KCal::Incidence* );
+
+ bool loadAttendeeAttribute( QDomElement&, Attendee& );
+ void saveAttendeeAttribute( QDomElement& element,
+ const Attendee& attendee ) const;
+ void saveAttendees( QDomElement& element ) const;
+ void saveAttachments( QDomElement& element ) const;
+
+ void loadRecurrence( const QDomElement& element );
+ void saveRecurrence( QDomElement& element ) const;
+ void saveCustomAttributes( QDomElement& element ) const;
+ void loadCustomAttributes( QDomElement& element );
+
+ void loadAttachments();
+
+ QString productID() const;
+
+ QString mSummary;
+ QString mLocation;
+ Email mOrganizer;
+ QDateTime mStartDate;
+ FloatingStatus mFloatingStatus;
+ float mAlarm;
+ bool mHasAlarm;
+ Recurrence mRecurrence;
+ QValueList<Attendee> mAttendees;
+ QValueList<KCal::Attachment*> mAttachments;
+ QString mInternalUID;
+ int mRevision;
+
+ struct Custom {
+ QCString key;
+ QString value;
+ };
+ QValueList<Custom> mCustomList;
+
+ KCal::ResourceKolab *mResource;
+ QString mSubResource;
+ Q_UINT32 mSernum;
+};
+
+}
+
+#endif // KOLAB_INCIDENCE_H
diff --git a/kresources/kolab/kcal/journal.cpp b/kresources/kolab/kcal/journal.cpp
new file mode 100644
index 00000000..45b91587
--- /dev/null
+++ b/kresources/kolab/kcal/journal.cpp
@@ -0,0 +1,184 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "journal.h"
+
+#include <libkcal/journal.h>
+#include <korganizer/version.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCal::Journal* Journal::xmlToJournal( const QString& xml, const QString& tz )
+{
+ Journal journal( tz );
+ journal.load( xml );
+ KCal::Journal* kcalJournal = new KCal::Journal();
+ journal.saveTo( kcalJournal );
+ return kcalJournal;
+}
+
+QString Journal::journalToXML( KCal::Journal* kcalJournal, const QString& tz )
+{
+ Journal journal( tz, kcalJournal );
+ return journal.saveXML();
+}
+
+Journal::Journal( const QString& tz, KCal::Journal* journal )
+ : KolabBase( tz )
+{
+ if ( journal )
+ setFields( journal );
+}
+
+Journal::~Journal()
+{
+}
+
+void Journal::setSummary( const QString& summary )
+{
+ mSummary = summary;
+}
+
+QString Journal::summary() const
+{
+ return mSummary;
+}
+
+void Journal::setStartDate( const QDateTime& startDate )
+{
+ mStartDate = startDate;
+}
+
+QDateTime Journal::startDate() const
+{
+ return mStartDate;
+}
+
+void Journal::setEndDate( const QDateTime& endDate )
+{
+ mEndDate = endDate;
+}
+
+QDateTime Journal::endDate() const
+{
+ return mEndDate;
+}
+
+bool Journal::loadAttribute( QDomElement& element )
+{
+ QString tagName = element.tagName();
+
+ if ( tagName == "summary" )
+ setSummary( element.text() );
+ else if ( tagName == "start-date" )
+ setStartDate( stringToDateTime( element.text() ) );
+ else
+ // Not handled here
+ return KolabBase::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Journal::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ writeString( element, "summary", summary() );
+ writeString( element, "start-date", dateTimeToString( startDate() ) );
+
+ return true;
+}
+
+
+bool Journal::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "journal" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected Journal",
+ top.tagName().ascii() );
+ return false;
+ }
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ if ( !loadAttribute( e ) ) {
+ // Unhandled tag - save for later storage
+ //qDebug( "Unhandled tag: %s", e.toCString().data() );
+ }
+ } else
+ qDebug( "Node is not a comment or an element???" );
+ }
+
+ return true;
+}
+
+QString Journal::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "journal" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Journal::saveTo( KCal::Journal* journal )
+{
+ KolabBase::saveTo( journal );
+
+ journal->setSummary( summary() );
+ journal->setDtStart( utcToLocal( startDate() ) );
+}
+
+void Journal::setFields( const KCal::Journal* journal )
+{
+ // Set baseclass fields
+ KolabBase::setFields( journal );
+
+ // Set our own fields
+ setSummary( journal->summary() );
+ setStartDate( localToUTC( journal->dtStart() ) );
+}
+
+QString Journal::productID() const
+{
+ return QString( "KOrganizer " ) + korgVersion + ", Kolab resource";
+}
diff --git a/kresources/kolab/kcal/journal.h b/kresources/kolab/kcal/journal.h
new file mode 100644
index 00000000..d8b09bb9
--- /dev/null
+++ b/kresources/kolab/kcal/journal.h
@@ -0,0 +1,103 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KOLAB_JOURNAL_H
+#define KOLAB_JOURNAL_H
+
+#include <kolabbase.h>
+
+class QDomElement;
+
+namespace KCal {
+ class Journal;
+}
+
+namespace Kolab {
+
+/**
+ * This class represents a journal entry, and knows how to load/save it
+ * from/to XML, and from/to a KCal::Journal.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Journal : public KolabBase {
+public:
+ /// Use this to parse an xml string to a journal entry
+ /// The caller is responsible for deleting the returned journal
+ static KCal::Journal* xmlToJournal( const QString& xml, const QString& tz );
+
+ /// Use this to get an xml string describing this journal entry
+ static QString journalToXML( KCal::Journal*, const QString& tz );
+
+ explicit Journal( const QString& tz, KCal::Journal* journal = 0 );
+ virtual ~Journal();
+
+ virtual QString type() const { return "Journal"; }
+
+ void saveTo( KCal::Journal* journal );
+
+ virtual void setSummary( const QString& summary );
+ virtual QString summary() const;
+
+ virtual void setStartDate( const QDateTime& startDate );
+ virtual QDateTime startDate() const;
+
+ virtual void setEndDate( const QDateTime& endDate );
+ virtual QDateTime endDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this journal by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this journal to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical journal
+ void setFields( const KCal::Journal* );
+
+ QString productID() const;
+
+ QString mSummary;
+ QDateTime mStartDate;
+ QDateTime mEndDate;
+};
+
+}
+
+#endif // KOLAB_JOURNAL_H
diff --git a/kresources/kolab/kcal/kolab.desktop b/kresources/kolab/kcal/kolab.desktop
new file mode 100644
index 00000000..579e6406
--- /dev/null
+++ b/kresources/kolab/kcal/kolab.desktop
@@ -0,0 +1,53 @@
+[Desktop Entry]
+Name=Calendar on IMAP Server via KMail
+Name[af]=Kalender op IMAP bediener via KMail
+Name[bg]=Календар на сървър IMAP през KMail
+Name[br]=Deiziadur en ur servijer IMAP gant KMail
+Name[ca]=Calendari sobre servidor IMAP mitjançant KMail
+Name[cs]=Kalendář na IMAP serveru přes KMail
+Name[da]=Kalender på IMAP-server via KMail
+Name[de]=Kalender auf einem IMAP-Server via KMail
+Name[el]=Ημερολόγιο σε εξυπηρετητή IMAP μέσω του KMail
+Name[es]=Calendario en servidor IMAP por medio de KMail
+Name[et]=Kalender IMAP-serveris (KMaili vahendusel)
+Name[eu]=Egutegia IMAP zerbitzarian KMail-en bidez
+Name[fa]=تقویم روی کارساز IMAP از طریق KMail
+Name[fi]=Kalenteri IMAP-palvelimella KMailin avulla
+Name[fr]=Agenda sur serveur IMAP (via KMail)
+Name[fy]=Aginda op IMAP-tsjinner fia KMail
+Name[ga]=Féilire ar Fhreastalaí IMAP via KMail
+Name[gl]=Calendario no servidor IMAP mediante KMail
+Name[hu]=IMAP-kiszolgálón tárolt naptár a KMailen keresztül
+Name[is]=Dagatal á IMAP þjóni gegnum KMail
+Name[it]=Calendario su server IMAP via KMail
+Name[ja]=KMail 経由 IMAP サーバのカレンダー
+Name[ka]=კალენდარი IMAP სერვერზე KMail-ის საშუალებით
+Name[kk]=KMail арқылы IMAP серверіндегі күнтізбе
+Name[km]=ប្រតិទិន​លើ​ម៉ាស៊ីន​បម្រើ IMAP តាម​រយៈ KMail
+Name[lt]=Kalendorius IMAP serveryje per KMail
+Name[mk]=Календар на IMAP-сервер преку КПошта
+Name[ms]=Kalendar pada pelayan IMAP melalui KMail
+Name[nb]=Kalender på IMAP-tjener via KMail
+Name[nds]=Kalenner op IMAP-Server över KMail
+Name[ne]=केडीई मेल मार्फत IMAP सर्भरमा क्यालेन्डर
+Name[nl]=Agenda op IMAP-server via KMail
+Name[nn]=Kalender på IMAP-tenar via KMail
+Name[pl]=Kalendarz na serwerze IMAP za pośrednictwem KMail
+Name[pt]=Calendário em Servidor IMAP via KMail
+Name[pt_BR]=Calendário em Servidor IMAP via KMail
+Name[ru]=Календарь на сервере IMAP через KMail
+Name[sk]=Kalendár na IMAP serveri pomocou KMail
+Name[sl]=Koledar na strežniku IMAP preko KMaila
+Name[sr]=Календар на IMAP серверу преко KMail-а
+Name[sr@Latn]=Kalendar na IMAP serveru preko KMail-a
+Name[sv]=Kalender på IMAP-server via Kmail
+Name[ta]=IMAP சேவையக வழியாக கேஅஞ்சலில் நாட்காட்டி
+Name[tr]=KMail Aracılığı ile IMAP Sunucusunda Takvim
+Name[uk]=Календар на сервері IMAP через KMail
+Name[zh_CN]=通过 KMail 访问 IMAP 服务器上的日历
+Name[zh_TW]=透過 KMail 取得 IMAP 伺服器上的行事曆
+X-KDE-Library=kcal_kolab
+Type=Service
+ServiceTypes=KResources/Plugin
+X-KDE-ResourceFamily=calendar
+X-KDE-ResourceType=imap
diff --git a/kresources/kolab/kcal/resourcekolab.cpp b/kresources/kolab/kcal/resourcekolab.cpp
new file mode 100644
index 00000000..3c1c80e1
--- /dev/null
+++ b/kresources/kolab/kcal/resourcekolab.cpp
@@ -0,0 +1,1153 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+ 2004 Till Adam <till@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "resourcekolab.h"
+#include "event.h"
+#include "task.h"
+#include "journal.h"
+
+#include <kio/observer.h>
+#include <kio/uiserver_stub.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <libkcal/icalformat.h>
+#include <libkdepim/kincidencechooser.h>
+#include <kabc/locknull.h>
+#include <kmainwindow.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <ktempfile.h>
+#include <kmdcodec.h>
+
+#include <qfile.h>
+#include <qobject.h>
+#include <qtimer.h>
+#include <qapplication.h>
+
+#include <assert.h>
+
+using namespace KCal;
+using namespace Kolab;
+
+static const char* kmailCalendarContentsType = "Calendar";
+static const char* kmailTodoContentsType = "Task";
+static const char* kmailJournalContentsType = "Journal";
+static const char* eventAttachmentMimeType = "application/x-vnd.kolab.event";
+static const char* todoAttachmentMimeType = "application/x-vnd.kolab.task";
+static const char* journalAttachmentMimeType = "application/x-vnd.kolab.journal";
+static const char* incidenceInlineMimeType = "text/calendar";
+
+
+ResourceKolab::ResourceKolab( const KConfig *config )
+ : ResourceCalendar( config ), ResourceKolabBase( "ResourceKolab-libkcal" ),
+ mCalendar( QString::fromLatin1("UTC") ), mOpen( false ),mResourceChangedTimer( 0,
+ "mResourceChangedTimer" )
+{
+ setType( "imap" );
+ connect( &mResourceChangedTimer, SIGNAL( timeout() ),
+ this, SLOT( slotEmitResourceChanged() ) );
+}
+
+ResourceKolab::~ResourceKolab()
+{
+ // The resource is deleted on exit (StdAddressBook's KStaticDeleter),
+ // and it wasn't closed before that, so close here to save the config.
+ if ( mOpen ) {
+ close();
+ }
+}
+
+void ResourceKolab::loadSubResourceConfig( KConfig& config,
+ const QString& name,
+ const QString& label,
+ bool writable,
+ bool alarmRelevant,
+ ResourceMap& subResource )
+{
+ KConfigGroup group( &config, name );
+ bool active = group.readBoolEntry( "Active", true );
+ subResource.insert( name, Kolab::SubResource( active, writable,
+ alarmRelevant, label ) );
+}
+
+bool ResourceKolab::openResource( KConfig& config, const char* contentType,
+ ResourceMap& map )
+{
+ // Read the subresource entries from KMail
+ QValueList<KMailICalIface::SubResource> subResources;
+ if ( !kmailSubresources( subResources, contentType ) )
+ return false;
+ map.clear();
+ QValueList<KMailICalIface::SubResource>::ConstIterator it;
+ for ( it = subResources.begin(); it != subResources.end(); ++it )
+ loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable,
+ (*it).alarmRelevant, map );
+ return true;
+}
+
+bool ResourceKolab::doOpen()
+{
+ if ( mOpen )
+ // Already open
+ return true;
+ mOpen = true;
+
+ KConfig config( configFile() );
+ config.setGroup( "General" );
+ mProgressDialogIncidenceLimit = config.readNumEntry("ProgressDialogIncidenceLimit", 200);
+
+ return openResource( config, kmailCalendarContentsType, mEventSubResources )
+ && openResource( config, kmailTodoContentsType, mTodoSubResources )
+ && openResource( config, kmailJournalContentsType, mJournalSubResources );
+}
+
+static void closeResource( KConfig& config, ResourceMap& map )
+{
+ ResourceMap::ConstIterator it;
+ for ( it = map.begin(); it != map.end(); ++it ) {
+ config.setGroup( it.key() );
+ config.writeEntry( "Active", it.data().active() );
+ }
+}
+
+void ResourceKolab::doClose()
+{
+ if ( !mOpen )
+ // Not open
+ return;
+ mOpen = false;
+
+ KConfig config( configFile() );
+ closeResource( config, mEventSubResources );
+ closeResource( config, mTodoSubResources );
+ closeResource( config, mJournalSubResources );
+}
+
+bool ResourceKolab::loadSubResource( const QString& subResource,
+ const char* mimetype )
+{
+ int count = 0;
+ if ( !kmailIncidencesCount( count, mimetype, subResource ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::load()\n";
+ return false;
+ }
+
+ if ( !count )
+ return true;
+
+ const int nbMessages = 200; // read 200 mails at a time (see kabc resource)
+
+ const QString labelTxt = !strcmp(mimetype, "application/x-vnd.kolab.task") ? i18n( "Loading tasks..." )
+ : !strcmp(mimetype, "application/x-vnd.kolab.journal") ? i18n( "Loading journals..." )
+ : i18n( "Loading events..." );
+ const bool useProgress = qApp && qApp->type() != QApplication::Tty && count > mProgressDialogIncidenceLimit;
+ if ( useProgress )
+ (void)::Observer::self(); // ensure kio_uiserver is running
+ UIServer_stub uiserver( "kio_uiserver", "UIServer" );
+ int progressId = 0;
+ if ( useProgress ) {
+ progressId = uiserver.newJob( kapp->dcopClient()->appId(), true );
+ uiserver.totalFiles( progressId, count );
+ uiserver.infoMessage( progressId, labelTxt );
+ uiserver.transferring( progressId, labelTxt );
+ }
+
+ for ( int startIndex = 0; startIndex < count; startIndex += nbMessages ) {
+ QMap<Q_UINT32, QString> lst;
+ if ( !kmailIncidences( lst, mimetype, subResource, startIndex, nbMessages ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::load()\n";
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return false;
+ }
+
+ { // for RAII scoping below
+ TemporarySilencer t( this );
+ for( QMap<Q_UINT32, QString>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
+ addIncidence( mimetype, it.data(), subResource, it.key() );
+ }
+ }
+ if ( progressId ) {
+ uiserver.processedFiles( progressId, startIndex );
+ uiserver.percent( progressId, 100 * startIndex / count );
+ }
+
+// if ( progress.wasCanceled() ) {
+// uiserver.jobFinished( progressId );
+// return false;
+// }
+ }
+
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return true;
+}
+
+bool ResourceKolab::doLoad()
+{
+ if (!mUidMap.isEmpty() ) {
+ return true;
+ }
+ mUidMap.clear();
+
+ return loadAllEvents() & loadAllTodos() & loadAllJournals();
+}
+
+bool ResourceKolab::doLoadAll( ResourceMap& map, const char* mimetype )
+{
+ bool rc = true;
+ for ( ResourceMap::ConstIterator it = map.begin(); it != map.end(); ++it ) {
+ if ( !it.data().active() )
+ // This resource is disabled
+ continue;
+
+ rc &= loadSubResource( it.key(), mimetype );
+ }
+ return rc;
+}
+
+bool ResourceKolab::loadAllEvents()
+{
+ removeIncidences( "Event" );
+ mCalendar.deleteAllEvents();
+ bool kolabStyle = doLoadAll( mEventSubResources, eventAttachmentMimeType );
+ bool icalStyle = doLoadAll( mEventSubResources, incidenceInlineMimeType );
+ return kolabStyle && icalStyle;
+}
+
+bool ResourceKolab::loadAllTodos()
+{
+ removeIncidences( "Todo" );
+ mCalendar.deleteAllTodos();
+ bool kolabStyle = doLoadAll( mTodoSubResources, todoAttachmentMimeType );
+ bool icalStyle = doLoadAll( mTodoSubResources, incidenceInlineMimeType );
+
+ return kolabStyle && icalStyle;
+}
+
+bool ResourceKolab::loadAllJournals()
+{
+ removeIncidences( "Journal" );
+ mCalendar.deleteAllJournals();
+ bool kolabStyle = doLoadAll( mJournalSubResources, journalAttachmentMimeType );
+ bool icalStyle = doLoadAll( mJournalSubResources, incidenceInlineMimeType );
+
+ return kolabStyle && icalStyle;
+}
+
+void ResourceKolab::removeIncidences( const QCString& incidenceType )
+{
+ Kolab::UidMap::Iterator mapIt = mUidMap.begin();
+ while ( mapIt != mUidMap.end() )
+ {
+ Kolab::UidMap::Iterator it = mapIt++;
+ // Check the type of this uid: event, todo or journal.
+ // Need to look up in mCalendar for that. Given the implementation of incidence(uid),
+ // better call event(uid), todo(uid) etc. directly.
+
+ // A faster but hackish way would probably be to check the type of the resource,
+ // like mEventSubResources.find( it.data().resource() ) != mEventSubResources.end() ?
+ const QString& uid = it.key();
+ if ( incidenceType == "Event" && mCalendar.event( uid ) )
+ mUidMap.remove( it );
+ else if ( incidenceType == "Todo" && mCalendar.todo( uid ) )
+ mUidMap.remove( it );
+ else if ( incidenceType == "Journal" && mCalendar.journal( uid ) )
+ mUidMap.remove( it );
+ }
+}
+
+bool ResourceKolab::doSave()
+{
+ return true;
+ /*
+ return kmailTriggerSync( kmailCalendarContentsType )
+ && kmailTriggerSync( kmailTodoContentsType )
+ && kmailTriggerSync( kmailJournalContentsType );
+ */
+}
+void ResourceKolab::incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase)
+{
+ const QString uid = incidencebase->uid();
+ //kdDebug() << k_funcinfo << uid << endl;
+
+ if ( mUidsPendingUpdate.contains( uid ) || mUidsPendingAdding.contains( uid ) ) {
+ /* We are currently processing this event ( removing and readding or
+ * adding it ). If so, ignore this update. Keep the last of these around
+ * and process once we hear back from KMail on this event. */
+ mPendingUpdates.replace( uid, incidencebase );
+ return;
+ }
+
+ QString subResource;
+ Q_UINT32 sernum = 0;
+ if ( mUidMap.contains( uid ) ) {
+ subResource = mUidMap[ uid ].resource();
+ sernum = mUidMap[ uid ].serialNumber();
+ mUidsPendingUpdate.append( uid );
+ }
+ sendKMailUpdate( incidencebase, subResource, sernum );
+
+}
+void ResourceKolab::incidenceUpdated( KCal::IncidenceBase* incidencebase )
+{
+ if ( incidencebase->isReadOnly() ) return;
+ incidencebase->setSyncStatusSilent( KCal::Event::SYNCMOD );
+ incidencebase->setLastModified( QDateTime::currentDateTime() );
+ // we should probably update the revision number here,
+ // or internally in the Event itself when certain things change.
+ // need to verify with ical documentation.
+ incidenceUpdatedSilent( incidencebase );
+
+}
+
+void ResourceKolab::resolveConflict( KCal::Incidence* inc, const QString& subresource, Q_UINT32 sernum )
+{
+ if ( ! inc )
+ return;
+ if ( ! mResolveConflict ) {
+ // we should do no conflict resolution
+ delete inc;
+ return;
+ }
+ const QString origUid = inc->uid();
+ Incidence* local = mCalendar.incidence( origUid );
+ Incidence* localIncidence = 0;
+ Incidence* addedIncidence = 0;
+ Incidence* result = 0;
+ if ( local ) {
+ if (*local == *inc) {
+ // real duplicate, remove the second one
+ result = local;
+ } else {
+ KIncidenceChooser* ch = new KIncidenceChooser();
+ ch->setIncidence( local ,inc );
+ if ( KIncidenceChooser::chooseMode == KIncidenceChooser::ask ) {
+ connect ( this, SIGNAL( useGlobalMode() ), ch, SLOT ( useGlobalMode() ) );
+ if ( ch->exec() )
+ if ( KIncidenceChooser::chooseMode != KIncidenceChooser::ask )
+ emit useGlobalMode() ;
+ }
+ result = ch->getIncidence();
+ delete ch;
+ }
+ } else {
+ // nothing there locally, just take the new one. Can't Happen (TM)
+ result = inc;
+ }
+ if ( result == local ) {
+ delete inc;
+ localIncidence = local;
+ } else if ( result == inc ) {
+ addedIncidence = inc;
+ } else if ( result == 0 ) { // take both
+ addedIncidence = inc;
+ addedIncidence->setSummary( i18n("Copy of: %1").arg( addedIncidence->summary() ) );
+ addedIncidence->setUid( CalFormat::createUniqueId() );
+ localIncidence = local;
+ }
+ bool silent = mSilent;
+ mSilent = false;
+ if ( !localIncidence ) {
+ deleteIncidence( local ); // remove local from kmail
+ }
+ mUidsPendingDeletion.append( origUid );
+ if ( addedIncidence ) {
+ sendKMailUpdate( addedIncidence, subresource, sernum );
+ } else {
+ kmailDeleteIncidence( subresource, sernum );// remove new from kmail
+ }
+ mSilent = silent;
+}
+void ResourceKolab::addIncidence( const char* mimetype, const QString& data,
+ const QString& subResource, Q_UINT32 sernum )
+{
+ // This uses pointer comparison, so it only works if we use the static
+ // objects defined in the top of the file
+ if ( mimetype == eventAttachmentMimeType )
+ addEvent( data, subResource, sernum );
+ else if ( mimetype == todoAttachmentMimeType )
+ addTodo( data, subResource, sernum );
+ else if ( mimetype == journalAttachmentMimeType )
+ addJournal( data, subResource, sernum );
+ else if ( mimetype == incidenceInlineMimeType ) {
+ Incidence *inc = mFormat.fromString( data );
+ addIncidence( inc, subResource, sernum );
+ }
+}
+
+
+bool ResourceKolab::sendKMailUpdate( KCal::IncidenceBase* incidencebase, const QString& subresource,
+ Q_UINT32 sernum )
+{
+ const QString& type = incidencebase->type();
+ const char* mimetype = 0;
+ QString data;
+ bool isXMLStorageFormat = kmailStorageFormat( subresource ) == KMailICalIface::StorageXML;
+ if ( type == "Event" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = eventAttachmentMimeType;
+ data = Kolab::Event::eventToXML( static_cast<KCal::Event *>(incidencebase),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Event *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else if ( type == "Todo" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = todoAttachmentMimeType;
+ data = Kolab::Task::taskToXML( static_cast<KCal::Todo *>(incidencebase),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Todo *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else if ( type == "Journal" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = journalAttachmentMimeType;
+ data = Kolab::Journal::journalToXML( static_cast<KCal::Journal *>(incidencebase ),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Journal *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else {
+ kdWarning(5006) << "Can't happen: unhandled type=" << type << endl;
+ }
+
+// kdDebug() << k_funcinfo << "Data string:\n" << data << endl;
+
+ KCal::Incidence* incidence = static_cast<KCal::Incidence *>( incidencebase );
+
+ KCal::Attachment::List atts = incidence->attachments();
+ QStringList attURLs, attMimeTypes, attNames;
+ QValueList<KTempFile*> tmpFiles;
+ for ( KCal::Attachment::List::ConstIterator it = atts.constBegin(); it != atts.constEnd(); ++it ) {
+ KTempFile* tempFile = new KTempFile;
+ QCString decoded = KCodecs::base64Decode( QCString( (*it)->data() ) );
+ tempFile->file()->writeBlock( decoded.data(), decoded.length() );
+ tempFile->close();
+ KURL url;
+ url.setPath( tempFile->name() );
+ attURLs.append( url.url() );
+ attMimeTypes.append( (*it)->mimeType() );
+ attNames.append( (*it)->label() );
+ }
+ QStringList deletedAtts;
+ if ( kmailListAttachments( deletedAtts, subresource, sernum ) ) {
+ for ( QStringList::ConstIterator it = attNames.constBegin(); it != attNames.constEnd(); ++it ) {
+ deletedAtts.remove( *it );
+ }
+ }
+ CustomHeaderMap customHeaders;
+ if ( incidence->schedulingID() != incidence->uid() )
+ customHeaders.insert( "X-Kolab-SchedulingID", incidence->schedulingID() );
+
+ QString subject = incidencebase->uid();
+ if ( !isXMLStorageFormat ) subject.prepend( "iCal " ); // conform to the old style
+
+ // behold, sernum is an in-parameter
+ const bool rc = kmailUpdate( subresource, sernum, data, mimetype, subject, customHeaders, attURLs, attMimeTypes, attNames, deletedAtts );
+ // update the serial number
+ if ( mUidMap.contains( incidencebase->uid() ) ) {
+ mUidMap[ incidencebase->uid() ].setSerialNumber( sernum );
+ }
+
+ for( QValueList<KTempFile *>::Iterator it = tmpFiles.begin(); it != tmpFiles.end(); ++it ) {
+ (*it)->setAutoDelete( true );
+ delete (*it);
+ }
+
+ return rc;
+}
+
+bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const QString& _subresource,
+ Q_UINT32 sernum )
+{
+ Q_ASSERT( incidence );
+ if ( !incidence ) return false;
+ QString uid = incidence->uid();
+ QString subResource = _subresource;
+
+ Kolab::ResourceMap *map = &mEventSubResources; // don't use a ref here!
+
+ const QString& type = incidence->type();
+ if ( type == "Event" )
+ map = &mEventSubResources;
+ else if ( type == "Todo" )
+ map = &mTodoSubResources;
+ else if ( type == "Journal" )
+ map = &mJournalSubResources;
+ else
+ kdWarning() << "unknown type " << type << endl;
+
+ if ( !mSilent ) { /* We got this one from the user, tell KMail. */
+ // Find out if this event was previously stored in KMail
+ bool newIncidence = _subresource.isEmpty();
+ if ( newIncidence ) {
+ // Add a description of the incidence
+ QString text = "<b><font size=\"+1\">";
+ if ( incidence->type() == "Event" )
+ text += i18n( "Choose the folder where you want to store this event" );
+ else if ( incidence->type() == "Todo" )
+ text += i18n( "Choose the folder where you want to store this task" );
+ else
+ text += i18n( "Choose the folder where you want to store this incidence" );
+ text += "<font></b><br>";
+ if ( !incidence->summary().isEmpty() )
+ text += i18n( "<b>Summary:</b> %1" ).arg( incidence->summary() ) + "<br>";
+ if ( !incidence->location().isEmpty() )
+ text += i18n( "<b>Location:</b> %1" ).arg( incidence->location() );
+ text += "<br>";
+ if ( !incidence->doesFloat() )
+ text += i18n( "<b>Start:</b> %1, %2" )
+ .arg( incidence->dtStartDateStr(), incidence->dtStartTimeStr() );
+ else
+ text += i18n( "<b>Start:</b> %1" ).arg( incidence->dtStartDateStr() );
+ text += "<br>";
+ if ( incidence->type() == "Event" ) {
+ Event* event = static_cast<Event*>( incidence );
+ if ( event->hasEndDate() )
+ if ( !event->doesFloat() )
+ text += i18n( "<b>End:</b> %1, %2" )
+ .arg( event->dtEndDateStr(), event->dtEndTimeStr() );
+ else
+ text += i18n( "<b>End:</b> %1" ).arg( event->dtEndDateStr() );
+ text += "<br>";
+ }
+ subResource = findWritableResource( *map, text );
+ }
+
+ if ( subResource.isEmpty() )
+ return false;
+
+ mNewIncidencesMap.insert( uid, subResource );
+
+ if ( !sendKMailUpdate( incidence, subResource, sernum ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::addIncidence()\n";
+ return false;
+ } else {
+ // KMail is doing it's best to add the event now, put a sticker on it,
+ // so we know it's one of our transient ones
+ mUidsPendingAdding.append( uid );
+
+ /* Add to the cache immediately if this is a new event coming from
+ * KOrganizer. It relies on the incidence being in the calendar when
+ * addIncidence returns. */
+ if ( newIncidence ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ }
+ } else { /* KMail told us */
+ bool ourOwnUpdate = mUidsPendingUpdate.contains( uid );
+ /* Check if we updated this one, which means kmail deleted and added it.
+ * We know the new state, so lets just not do much at all. The old incidence
+ * in the calendar remains valid, but the serial number changed, so we need to
+ * update that */
+ if ( ourOwnUpdate ) {
+ mUidsPendingUpdate.remove( uid );
+ mUidMap.remove( uid );
+ mUidMap[ uid ] = StorageReference( subResource, sernum );
+ } else {
+ /* This is a real add, from KMail, we didn't trigger this ourselves.
+ * If this uid already exists in this folder, do conflict resolution,
+ * unless the folder is read-only, in which case the user should not be
+ * offered a means of putting mails in a folder she'll later be unable to
+ * upload. Skip the incidence, in this case. */
+ if ( mUidMap.contains( uid ) ) {
+ if ( mUidMap[ uid ].resource() == subResource ) {
+ if ( (*map)[ subResource ].writable() ) {
+ resolveConflict( incidence, subResource, sernum );
+ } else {
+ kdWarning( 5650 ) << "Duplicate event in a read-only folder detected! "
+ "Please inform the owner of the folder. " << endl;
+ }
+ return true;
+ } else {
+ // duplicate uid in a different folder, do the internal-uid tango
+ incidence->setSchedulingID( uid );
+ incidence->setUid(CalFormat::createUniqueId( ) );
+ uid = incidence->uid();
+ }
+ }
+ /* Add to the cache if the add didn't come from KOrganizer, in which case
+ * we've already added it, and listen to updates from KOrganizer for it. */
+ if ( !mUidsPendingAdding.contains( uid ) ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ if ( !subResource.isEmpty() && sernum != 0 ) {
+ mUidMap[ uid ] = StorageReference( subResource, sernum );
+ incidence->setReadOnly( !(*map)[ subResource ].writable() );
+ }
+ }
+ /* Check if there are updates for this uid pending and if so process them. */
+ if ( KCal::IncidenceBase *update = mPendingUpdates.find( uid ) ) {
+ mSilent = false; // we do want to tell KMail
+ mPendingUpdates.remove( uid );
+ incidenceUpdated( update );
+ } else {
+ /* If the uid was added by KMail, KOrganizer needs to be told, so
+ * schedule emitting of the resourceChanged signal. */
+ if ( !mUidsPendingAdding.contains( uid ) ) {
+ if ( !ourOwnUpdate ) mResourceChangedTimer.changeInterval( 100 );
+ } else {
+ mUidsPendingAdding.remove( uid );
+ }
+ }
+
+ mNewIncidencesMap.remove( uid );
+ }
+ return true;
+}
+
+
+bool ResourceKolab::addEvent( KCal::Event* event )
+{
+ if ( mUidMap.contains( event->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( event, QString::null, 0 );
+}
+
+void ResourceKolab::addEvent( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum )
+{
+ KCal::Event* event = Kolab::Event::xmlToEvent( xml, mCalendar.timeZoneId(), this, subresource, sernum );
+ Q_ASSERT( event );
+ if( event ) {
+ addIncidence( event, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteIncidence( KCal::Incidence* incidence )
+{
+ if ( incidence->isReadOnly() ) return false;
+
+ const QString uid = incidence->uid();
+ if( !mUidMap.contains( uid ) ) return false; // Odd
+ /* The user told us to delete, tell KMail */
+ if ( !mSilent ) {
+ kmailDeleteIncidence( mUidMap[ uid ].resource(),
+ mUidMap[ uid ].serialNumber() );
+ mUidsPendingDeletion.append( uid );
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ mUidMap.remove( uid );
+ } else {
+ assert( false ); // If this still happens, something is very wrong
+ }
+ return true;
+}
+
+bool ResourceKolab::deleteEvent( KCal::Event* event )
+{
+ return deleteIncidence( event );
+}
+
+KCal::Event* ResourceKolab::event( const QString& uid )
+{
+ return mCalendar.event(uid);
+}
+
+KCal::Event::List ResourceKolab::rawEvents( EventSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawEvents( sortField, sortDirection );
+}
+
+KCal::Event::List ResourceKolab::rawEventsForDate( const QDate& date,
+ EventSortField sortField,
+ SortDirection sortDirection )
+{
+ return mCalendar.rawEventsForDate( date, sortField, sortDirection );
+}
+
+KCal::Event::List ResourceKolab::rawEventsForDate( const QDateTime& qdt )
+{
+ return mCalendar.rawEventsForDate( qdt );
+}
+
+KCal::Event::List ResourceKolab::rawEvents( const QDate& start,
+ const QDate& end,
+ bool inclusive )
+{
+ return mCalendar.rawEvents( start, end, inclusive );
+}
+
+bool ResourceKolab::addTodo( KCal::Todo* todo )
+{
+ if ( mUidMap.contains( todo->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( todo, QString::null, 0 );
+}
+
+void ResourceKolab::addTodo( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum )
+{
+ KCal::Todo* todo = Kolab::Task::xmlToTask( xml, mCalendar.timeZoneId(), this, subresource, sernum );
+ Q_ASSERT( todo );
+ if( todo )
+ addIncidence( todo, subresource, sernum );
+}
+
+bool ResourceKolab::deleteTodo( KCal::Todo* todo )
+{
+ return deleteIncidence( todo );
+}
+
+KCal::Todo* ResourceKolab::todo( const QString& uid )
+{
+ return mCalendar.todo( uid );
+}
+
+KCal::Todo::List ResourceKolab::rawTodos( TodoSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawTodos( sortField, sortDirection );
+}
+
+KCal::Todo::List ResourceKolab::rawTodosForDate( const QDate& date )
+{
+ return mCalendar.rawTodosForDate( date );
+}
+
+bool ResourceKolab::addJournal( KCal::Journal* journal )
+{
+ if ( mUidMap.contains( journal->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( journal, QString::null, 0 );
+}
+
+void ResourceKolab::addJournal( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum )
+{
+ KCal::Journal* journal =
+ Kolab::Journal::xmlToJournal( xml, mCalendar.timeZoneId() );
+ Q_ASSERT( journal );
+ if( journal ) {
+ addIncidence( journal, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteJournal( KCal::Journal* journal )
+{
+ return deleteIncidence( journal );
+}
+
+KCal::Journal* ResourceKolab::journal( const QString& uid )
+{
+ return mCalendar.journal(uid);
+}
+
+KCal::Journal::List ResourceKolab::rawJournals( JournalSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawJournals( sortField, sortDirection );
+}
+
+KCal::Journal::List ResourceKolab::rawJournalsForDate( const QDate &date )
+{
+ return mCalendar.rawJournalsForDate( date );
+}
+
+KCal::Alarm::List ResourceKolab::relevantAlarms( const KCal::Alarm::List &alarms )
+{
+ KCal::Alarm::List relevantAlarms;
+ KCal::Alarm::List::ConstIterator it( alarms.begin() );
+ while ( it != alarms.end() ) {
+ KCal::Alarm *a = (*it);
+ ++it;
+ const QString &uid = a->parent()->uid();
+ if ( mUidMap.contains( uid ) ) {
+ const QString &sr = mUidMap[ uid ].resource();
+ Kolab::SubResource *subResource = 0;
+ if ( mEventSubResources.contains( sr ) )
+ subResource = &( mEventSubResources[ sr ] );
+ else if ( mTodoSubResources.contains( sr ) )
+ subResource = &( mTodoSubResources[ sr ] );
+ assert( subResource );
+ if ( subResource->alarmRelevant() )
+ relevantAlarms.append ( a );
+ else {
+ kdDebug(5650) << "Alarm skipped, not relevant." << endl;
+ }
+ }
+ }
+ return relevantAlarms;
+}
+
+
+
+KCal::Alarm::List ResourceKolab::alarms( const QDateTime& from,
+ const QDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarms( from, to ) );
+}
+
+KCal::Alarm::List ResourceKolab::alarmsTo( const QDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarmsTo(to) );
+}
+
+void ResourceKolab::setTimeZoneId( const QString& tzid )
+{
+ mCalendar.setTimeZoneId( tzid );
+ mFormat.setTimeZone( mCalendar.timeZoneId(), !mCalendar.isLocalTime() );
+}
+
+bool ResourceKolab::fromKMailAddIncidence( const QString& type,
+ const QString& subResource,
+ Q_UINT32 sernum,
+ int format,
+ const QString& data )
+{
+ bool rc = true;
+ TemporarySilencer t( this ); // RAII
+ if ( type != kmailCalendarContentsType && type != kmailTodoContentsType
+ && type != kmailJournalContentsType )
+ // Not ours
+ return false;
+ if ( !subresourceActive( subResource ) ) return true;
+
+ if ( format == KMailICalIface::StorageXML ) {
+ // If this data file is one of ours, load it here
+ if ( type == kmailCalendarContentsType )
+ addEvent( data, subResource, sernum );
+ else if ( type == kmailTodoContentsType )
+ addTodo( data, subResource, sernum );
+ else if ( type == kmailJournalContentsType )
+ addJournal( data, subResource, sernum );
+ else
+ rc = false;
+ } else {
+ Incidence *inc = mFormat.fromString( data );
+ if ( !inc )
+ rc = false;
+ else
+ addIncidence( inc, subResource, sernum );
+ }
+ return rc;
+}
+
+void ResourceKolab::fromKMailDelIncidence( const QString& type,
+ const QString& subResource,
+ const QString& uid )
+{
+ if ( type != kmailCalendarContentsType && type != kmailTodoContentsType
+ && type != kmailJournalContentsType )
+ // Not ours
+ return;
+ if ( !subresourceActive( subResource ) ) return;
+
+ // Can't be in both, by contract
+ if ( mUidsPendingDeletion.find( uid ) != mUidsPendingDeletion.end() ) {
+ mUidsPendingDeletion.remove( mUidsPendingDeletion.find( uid ) );
+ } else if ( mUidsPendingUpdate.contains( uid ) ) {
+ // It's good to know if was deleted, but we are waiting on a new one to
+ // replace it, so let's just sit tight.
+ } else {
+ // We didn't trigger this, so KMail did, remove the reference to the uid
+ KCal::Incidence* incidence = mCalendar.incidence( uid );
+ if( incidence ) {
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ }
+ mUidMap.remove( uid );
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+}
+
+void ResourceKolab::fromKMailRefresh( const QString& type,
+ const QString& /*subResource*/ )
+{
+ // TODO: Only load the specified subResource
+ if ( type == "Calendar" )
+ loadAllEvents();
+ else if ( type == "Task" )
+ loadAllTodos();
+ else if ( type == "Journal" )
+ loadAllJournals();
+ else
+ kdWarning(5006) << "KCal Kolab resource: fromKMailRefresh: unknown type " << type << endl;
+ mResourceChangedTimer.changeInterval( 100 );
+}
+
+void ResourceKolab::fromKMailAddSubresource( const QString& type,
+ const QString& subResource,
+ const QString& label,
+ bool writable, bool alarmRelevant )
+{
+ ResourceMap* map = 0;
+ const char* mimetype = 0;
+ if ( type == kmailCalendarContentsType ) {
+ map = &mEventSubResources;
+ mimetype = eventAttachmentMimeType;
+ } else if ( type == kmailTodoContentsType ) {
+ map = &mTodoSubResources;
+ mimetype = todoAttachmentMimeType;
+ } else if ( type == kmailJournalContentsType ) {
+ map = &mJournalSubResources;
+ mimetype = journalAttachmentMimeType;
+ } else
+ // Not ours
+ return;
+
+ if ( map->contains( subResource ) )
+ // Already registered
+ return;
+
+ KConfig config( configFile() );
+ config.setGroup( subResource );
+
+ bool active = config.readBoolEntry( subResource, true );
+ (*map)[ subResource ] = Kolab::SubResource( active, writable,
+ alarmRelevant, label );
+ loadSubResource( subResource, mimetype );
+ emit signalSubresourceAdded( this, type, subResource, label );
+}
+
+void ResourceKolab::fromKMailDelSubresource( const QString& type,
+ const QString& subResource )
+{
+ ResourceMap* map = subResourceMap( type );
+ if ( !map ) // not ours
+ return;
+ if ( map->contains( subResource ) )
+ map->erase( subResource );
+ else
+ // Not registered
+ return;
+
+ // Delete from the config file
+ KConfig config( configFile() );
+ config.deleteGroup( subResource );
+ config.sync();
+
+ unloadSubResource( subResource );
+
+ emit signalSubresourceRemoved( this, type, subResource );
+}
+
+QStringList ResourceKolab::subresources() const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+ return ( mEventSubResources.keys()
+ + mTodoSubResources.keys()
+ + mJournalSubResources.keys() );
+}
+
+const QString
+ResourceKolab::labelForSubresource( const QString& subresource ) const
+{
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].label();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].label();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].label();
+ return subresource;
+}
+
+void ResourceKolab::fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder )
+{
+ TemporarySilencer t( this );
+ for( QMap<Q_UINT32, QString>::ConstIterator it = map.begin(); it != map.end(); ++it )
+ addIncidence( type.latin1(), it.data(), folder, it.key() );
+}
+
+bool ResourceKolab::subresourceActive( const QString& subresource ) const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].active();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].active();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].active();
+
+ // Safe default bet:
+ kdDebug(5650) << "subresourceActive( " << subresource << " ): Safe bet\n";
+
+ return true;
+}
+
+void ResourceKolab::setSubresourceActive( const QString &subresource, bool v )
+{
+ ResourceMap *map = 0;
+ const char* mimeType = 0;
+ if ( mEventSubResources.contains( subresource ) ) {
+ map = &mEventSubResources;
+ mimeType = eventAttachmentMimeType;
+ }
+ if ( mTodoSubResources.contains( subresource ) ) {
+ map = &mTodoSubResources;
+ mimeType = todoAttachmentMimeType;
+ }
+ if ( mJournalSubResources.contains( subresource ) ) {
+ map = &mJournalSubResources;
+ mimeType = journalAttachmentMimeType;
+ }
+
+ if ( map && ( ( *map )[ subresource ].active() != v ) ) {
+ ( *map )[ subresource ].setActive( v );
+ if ( v ) {
+ loadSubResource( subresource, mimeType );
+ } else {
+ unloadSubResource( subresource );
+ }
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+}
+
+void ResourceKolab::slotEmitResourceChanged()
+{
+ kdDebug(5650) << "KCal Kolab resource: emitting resource changed " << endl;
+ mResourceChangedTimer.stop();
+ emit resourceChanged( this );
+}
+
+KABC::Lock* ResourceKolab::lock()
+{
+ return new KABC::LockNull( true );
+}
+
+
+Kolab::ResourceMap* ResourceKolab::subResourceMap( const QString& contentsType )
+{
+ if ( contentsType == kmailCalendarContentsType ) {
+ return &mEventSubResources;
+ } else if ( contentsType == kmailTodoContentsType ) {
+ return &mTodoSubResources;
+ } else if ( contentsType == kmailJournalContentsType ) {
+ return &mJournalSubResources;
+ }
+ // Not ours
+ return 0;
+}
+
+
+/*virtual*/
+bool ResourceKolab::addSubresource( const QString& resource, const QString& parent )
+{
+ kdDebug(5650) << "KCal Kolab resource - adding subresource: " << resource << endl;
+ QString contentsType = kmailCalendarContentsType;
+ if ( !parent.isEmpty() ) {
+ if ( mEventSubResources.contains( parent ) )
+ contentsType = kmailCalendarContentsType;
+ else if ( mTodoSubResources.contains( parent ) )
+ contentsType = kmailTodoContentsType;
+ else if ( mJournalSubResources.contains( parent ) )
+ contentsType = kmailJournalContentsType;
+ } else {
+ QStringList contentTypeChoices;
+ contentTypeChoices << i18n("Calendar") << i18n("Tasks") << i18n("Journals");
+ const QString caption = i18n("Which kind of subresource should this be?");
+ const QString choice = KInputDialog::getItem( caption, QString::null, contentTypeChoices );
+ if ( choice == contentTypeChoices[0] )
+ contentsType = kmailCalendarContentsType;
+ else if ( choice == contentTypeChoices[1] )
+ contentsType = kmailTodoContentsType;
+ else if ( choice == contentTypeChoices[2] )
+ contentsType = kmailJournalContentsType;
+ }
+
+ return kmailAddSubresource( resource, parent, contentsType );
+}
+
+/*virtual*/
+bool ResourceKolab::removeSubresource( const QString& resource )
+{
+ kdDebug(5650) << "KCal Kolab resource - removing subresource: " << resource << endl;
+ return kmailRemoveSubresource( resource );
+}
+
+/*virtual*/
+QString ResourceKolab::subresourceIdentifier( Incidence *incidence )
+{
+ QString uid = incidence->uid();
+ if ( mUidMap.contains( uid ) )
+ return mUidMap[ uid ].resource();
+ else
+ if ( mNewIncidencesMap.contains( uid ) )
+ return mNewIncidencesMap[ uid ];
+ else
+ return QString();
+}
+
+
+bool ResourceKolab::unloadSubResource( const QString& subResource )
+{
+ const bool silent = mSilent;
+ mSilent = true;
+ Kolab::UidMap::Iterator mapIt = mUidMap.begin();
+ while ( mapIt != mUidMap.end() )
+ {
+ Kolab::UidMap::Iterator it = mapIt++;
+ const StorageReference ref = it.data();
+ if ( ref.resource() != subResource ) continue;
+ // FIXME incidence() is expensive
+ KCal::Incidence* incidence = mCalendar.incidence( it.key() );
+ if( incidence ) {
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ }
+ mUidMap.remove( it );
+ }
+ mSilent = silent;
+ return true;
+}
+
+QString ResourceKolab::subresourceType( const QString &resource )
+{
+ if ( mEventSubResources.contains( resource ) )
+ return "event";
+ if ( mTodoSubResources.contains( resource ) )
+ return "todo";
+ if ( mJournalSubResources.contains( resource ) )
+ return "journal";
+ return QString();
+}
+
+#include "resourcekolab.moc"
diff --git a/kresources/kolab/kcal/resourcekolab.h b/kresources/kolab/kcal/resourcekolab.h
new file mode 100644
index 00000000..0974e4ad
--- /dev/null
+++ b/kresources/kolab/kcal/resourcekolab.h
@@ -0,0 +1,237 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+ 2004 Till Adam <till@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KCAL_RESOURCEKOLAB_H
+#define KCAL_RESOURCEKOLAB_H
+
+#include <qtimer.h>
+
+#include <kdepimmacros.h>
+#include <libkcal/calendarlocal.h>
+#include <libkcal/icalformat.h>
+#include <libkcal/resourcecalendar.h>
+#include "../shared/resourcekolabbase.h"
+
+namespace KCal {
+
+struct TemporarySilencer;
+
+class KDE_EXPORT ResourceKolab : public KCal::ResourceCalendar,
+ public KCal::IncidenceBase::Observer,
+ public Kolab::ResourceKolabBase
+{
+ Q_OBJECT
+ friend struct TemporarySilencer;
+
+public:
+ ResourceKolab( const KConfig* );
+ virtual ~ResourceKolab();
+
+ /// Load resource data.
+ bool doLoad();
+
+ /// Save resource data.
+ bool doSave();
+
+ /// Open the notes resource.
+ bool doOpen();
+ /// Close the notes resource.
+ void doClose();
+
+ // The libkcal functions. See the resource for descriptions
+ bool addEvent( KCal::Event* anEvent );
+ bool deleteEvent( KCal::Event* );
+ KCal::Event* event( const QString &UniqueStr );
+ KCal::Event::List rawEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
+ KCal::Event::List rawEventsForDate(
+ const QDate& date,
+ EventSortField sortField=EventSortUnsorted,
+ SortDirection sortDirection=SortDirectionAscending );
+ KCal::Event::List rawEventsForDate( const QDateTime& qdt );
+ KCal::Event::List rawEvents( const QDate& start, const QDate& end,
+ bool inclusive = false );
+
+ bool addTodo( KCal::Todo* todo );
+ bool deleteTodo( KCal::Todo* );
+ KCal::Todo* todo( const QString& uid );
+ KCal::Todo::List rawTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
+ KCal::Todo::List rawTodosForDate( const QDate& date );
+
+ bool addJournal( KCal::Journal* );
+ bool deleteJournal( KCal::Journal* );
+ KCal::Journal* journal( const QString& uid );
+ KCal::Journal::List rawJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
+ KCal::Journal::List rawJournalsForDate( const QDate &date );
+
+ KCal::Alarm::List alarms( const QDateTime& from, const QDateTime& to );
+ KCal::Alarm::List alarmsTo( const QDateTime& to );
+
+ void setTimeZoneId( const QString& tzid );
+
+ bool deleteIncidence( KCal::Incidence* i );
+
+ /// The ResourceKolabBase methods called by KMail
+ bool fromKMailAddIncidence( const QString& type, const QString& subResource,
+ Q_UINT32 sernum, int format, const QString& data );
+ void fromKMailDelIncidence( const QString& type, const QString& subResource,
+ const QString& uid );
+ void fromKMailRefresh( const QString& type, const QString& subResource );
+
+ /// Listen to KMail changes in the amount of sub resources
+ void fromKMailAddSubresource( const QString& type, const QString& subResource,
+ const QString& label, bool writable,
+ bool alarmRelevant );
+ void fromKMailDelSubresource( const QString& type, const QString& subResource );
+
+ void fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder );
+
+ /** Return the list of subresources. */
+ QStringList subresources() const;
+
+ bool canHaveSubresources() const { return true; }
+
+ /** Is this subresource active? */
+ bool subresourceActive( const QString& ) const;
+ /** (De)activate the subresource */
+ virtual void setSubresourceActive( const QString &, bool );
+
+ /** What is the label for this subresource? */
+ virtual const QString labelForSubresource( const QString& resource ) const;
+
+ virtual QString subresourceIdentifier( Incidence *incidence );
+
+ virtual bool addSubresource( const QString& resource, const QString& parent );
+ virtual bool removeSubresource( const QString& resource );
+
+ virtual QString subresourceType( const QString &resource );
+
+ KABC::Lock* lock();
+
+signals:
+ void useGlobalMode();
+protected slots:
+ void slotEmitResourceChanged();
+protected:
+ /**
+ * Return list of alarms which are relevant for the current user. These
+ * are the ones coming from folders which the user has "Administer" rights
+ * for, as per ACL */
+ KCal::Alarm::List relevantAlarms( const KCal::Alarm::List &alarms );
+
+private:
+ void removeIncidences( const QCString& incidenceType );
+ void resolveConflict( KCal::Incidence*, const QString& subresource, Q_UINT32 sernum );
+ void addIncidence( const char* mimetype, const QString& xml,
+ const QString& subResource, Q_UINT32 sernum );
+
+ bool addIncidence( KCal::Incidence* i, const QString& subresource,
+ Q_UINT32 sernum );
+
+ void addEvent( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum );
+ void addTodo( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum );
+ void addJournal( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum );
+
+
+ bool loadAllEvents();
+ bool loadAllTodos();
+ bool loadAllJournals();
+
+ bool doLoadAll( Kolab::ResourceMap& map, const char* mimetype );
+
+ /// Reimplemented from IncidenceBase::Observer to know when an incidence was changed
+ void incidenceUpdated( KCal::IncidenceBase* );
+ void incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase);
+
+ bool openResource( KConfig& config, const char* contentType,
+ Kolab::ResourceMap& map );
+ void loadSubResourceConfig( KConfig& config, const QString& name,
+ const QString& label, bool writable,
+ bool alarmRelevant, Kolab::ResourceMap& subResource );
+ bool loadSubResource( const QString& subResource, const char* mimetype );
+ bool unloadSubResource( const QString& subResource );
+
+ QString configFile() const {
+ return ResourceKolabBase::configFile( "kcal" );
+ }
+
+ Kolab::ResourceMap* subResourceMap( const QString& contentsType );
+
+ bool sendKMailUpdate( KCal::IncidenceBase* incidence, const QString& _subresource,
+ Q_UINT32 sernum );
+
+
+ KCal::CalendarLocal mCalendar;
+
+ // The list of subresources
+ Kolab::ResourceMap mEventSubResources, mTodoSubResources, mJournalSubResources;
+
+ bool mOpen; // If the resource is open, this is true
+ QDict<KCal::IncidenceBase> mPendingUpdates;
+ QTimer mResourceChangedTimer;
+ ICalFormat mFormat;
+
+ /**
+ This map contains the association between a new added incidence
+ and the subresource it belongs to.
+ That's needed to return the correct mapping in subresourceIdentifier().
+
+ We can't trust on mUidMap here, because it contains only non-pending uids.
+ */
+ QMap<QString, QString> mNewIncidencesMap;
+ int mProgressDialogIncidenceLimit;
+};
+
+struct TemporarySilencer {
+ TemporarySilencer( ResourceKolab *_resource )
+ {
+ resource = _resource;
+ oldValue = resource->mSilent;
+ resource->mSilent = true;
+ }
+ ~TemporarySilencer()
+ {
+ resource->mSilent = oldValue;
+ }
+ ResourceKolab *resource;
+ bool oldValue;
+};
+
+}
+
+#endif // KCAL_RESOURCEKOLAB_H
diff --git a/kresources/kolab/kcal/resourcekolab_plugin.cpp b/kresources/kolab/kcal/resourcekolab_plugin.cpp
new file mode 100644
index 00000000..afc83e1d
--- /dev/null
+++ b/kresources/kolab/kcal/resourcekolab_plugin.cpp
@@ -0,0 +1,49 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarlvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "resourcekolab.h"
+
+class KolabFactory : public KRES::PluginFactoryBase
+{
+public:
+ KRES::Resource *resource( const KConfig *config )
+ {
+ return new KCal::ResourceKolab( config );
+ }
+
+ KRES::ConfigWidget *configWidget( QWidget* )
+ {
+ return 0;
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY(kcal_kolab,KolabFactory)
diff --git a/kresources/kolab/kcal/task.cpp b/kresources/kolab/kcal/task.cpp
new file mode 100644
index 00000000..b66c9827
--- /dev/null
+++ b/kresources/kolab/kcal/task.cpp
@@ -0,0 +1,336 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "task.h"
+
+#include <libkcal/todo.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCal::Todo* Task::xmlToTask( const QString& xml, const QString& tz, KCal::ResourceKolab *res,
+ const QString& subResource, Q_UINT32 sernum )
+{
+ Task task( res, subResource, sernum, tz );
+ task.load( xml );
+ KCal::Todo* todo = new KCal::Todo();
+ task.saveTo( todo );
+ return todo;
+}
+
+QString Task::taskToXML( KCal::Todo* todo, const QString& tz )
+{
+ Task task( 0, QString::null, 0, tz, todo );
+ return task.saveXML();
+}
+
+Task::Task( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz, KCal::Todo* task )
+ : Incidence( res, subResource, sernum, tz ),
+ mPriority( 5 ), mPercentCompleted( 0 ),
+ mStatus( KCal::Incidence::StatusNone ),
+ mHasStartDate( false ), mHasDueDate( false ),
+ mHasCompletedDate( false )
+{
+ if ( task )
+ setFields( task );
+}
+
+Task::~Task()
+{
+}
+
+void Task::setPriority( int priority )
+{
+ mPriority = priority;
+}
+
+int Task::priority() const
+{
+ return mPriority;
+}
+
+void Task::setPercentCompleted( int percent )
+{
+ mPercentCompleted = percent;
+}
+
+int Task::percentCompleted() const
+{
+ return mPercentCompleted;
+}
+
+void Task::setStatus( KCal::Incidence::Status status )
+{
+ mStatus = status;
+}
+
+KCal::Incidence::Status Task::status() const
+{
+ return mStatus;
+}
+
+void Task::setParent( const QString& parentUid )
+{
+ mParent = parentUid;
+}
+
+QString Task::parent() const
+{
+ return mParent;
+}
+
+void Task::setDueDate( const QDateTime& date )
+{
+ mDueDate = date;
+ mHasDueDate = true;
+}
+
+QDateTime Task::dueDate() const
+{
+ return mDueDate;
+}
+
+void Task::setHasStartDate( bool v )
+{
+ mHasStartDate = v;
+}
+
+bool Task::hasStartDate() const
+{
+ return mHasStartDate;
+}
+
+bool Task::hasDueDate() const
+{
+ return mHasDueDate;
+}
+
+void Task::setCompletedDate( const QDateTime& date )
+{
+ mCompletedDate = date;
+ mHasCompletedDate = true;
+}
+
+QDateTime Task::completedDate() const
+{
+ return mCompletedDate;
+}
+
+bool Task::hasCompletedDate() const
+{
+ return mHasCompletedDate;
+}
+
+bool Task::loadAttribute( QDomElement& element )
+{
+ QString tagName = element.tagName();
+
+ if ( tagName == "priority" ) {
+ bool ok;
+ int priority = element.text().toInt( &ok );
+ if ( !ok || priority < 0 || priority > 9 )
+ priority = 5;
+ setPriority( priority );
+ } else if ( tagName == "completed" ) {
+ bool ok;
+ int percent = element.text().toInt( &ok );
+ if ( !ok || percent < 0 || percent > 100 )
+ percent = 0;
+ setPercentCompleted( percent );
+ } else if ( tagName == "status" ) {
+ if ( element.text() == "in-progress" )
+ setStatus( KCal::Incidence::StatusInProcess );
+ else if ( element.text() == "completed" )
+ setStatus( KCal::Incidence::StatusCompleted );
+ else if ( element.text() == "waiting-on-someone-else" )
+ setStatus( KCal::Incidence::StatusNeedsAction );
+ else if ( element.text() == "deferred" )
+ // Guessing a status here
+ setStatus( KCal::Incidence::StatusCanceled );
+ else
+ // Default
+ setStatus( KCal::Incidence::StatusNone );
+ } else if ( tagName == "due-date" )
+ setDueDate( stringToDateTime( element.text() ) );
+ else if ( tagName == "parent" )
+ setParent( element.text() );
+ else if ( tagName == "x-completed-date" )
+ setCompletedDate( stringToDateTime( element.text() ) );
+ else if ( tagName == "start-date" ) {
+ setHasStartDate( true );
+ setStartDate( element.text() );
+ } else
+ return Incidence::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Task::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ Incidence::saveAttributes( element );
+
+ writeString( element, "priority", QString::number( priority() ) );
+ writeString( element, "completed", QString::number( percentCompleted() ) );
+
+ switch( status() ) {
+ case KCal::Incidence::StatusInProcess:
+ writeString( element, "status", "in-progress" );
+ break;
+ case KCal::Incidence::StatusCompleted:
+ writeString( element, "status", "completed" );
+ break;
+ case KCal::Incidence::StatusNeedsAction:
+ writeString( element, "status", "waiting-on-someone-else" );
+ break;
+ case KCal::Incidence::StatusCanceled:
+ writeString( element, "status", "deferred" );
+ break;
+ case KCal::Incidence::StatusNone:
+ writeString( element, "status", "not-started" );
+ break;
+ case KCal::Incidence::StatusTentative:
+ case KCal::Incidence::StatusConfirmed:
+ case KCal::Incidence::StatusDraft:
+ case KCal::Incidence::StatusFinal:
+ case KCal::Incidence::StatusX:
+ // All of these are saved as StatusNone.
+ writeString( element, "status", "not-started" );
+ break;
+ }
+
+ if ( hasDueDate() )
+ writeString( element, "due-date", dateTimeToString( dueDate() ) );
+
+ if ( !parent().isNull() )
+ writeString( element, "parent", parent() );
+
+ if ( hasCompletedDate() && percentCompleted() == 100)
+ writeString( element, "x-completed-date", dateTimeToString( completedDate() ) );
+
+ return true;
+}
+
+
+bool Task::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "task" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected task",
+ top.tagName().ascii() );
+ return false;
+ }
+ setHasStartDate( false ); // todo's don't necessarily have one
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ if ( !loadAttribute( e ) )
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ loadAttachments();
+ return true;
+}
+
+QString Task::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "task" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ if ( !hasStartDate() ) {
+ // events and journals always have a start date, but tasks don't.
+ // Remove the entry done by the inherited save above, because we
+ // don't have one.
+ QDomNodeList l = element.elementsByTagName( "start-date" );
+ Q_ASSERT( l.count() == 1 );
+ element.removeChild( l.item( 0 ) );
+ }
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Task::setFields( const KCal::Todo* task )
+{
+ Incidence::setFields( task );
+
+ setPriority( task->priority() );
+ setPercentCompleted( task->percentComplete() );
+ setStatus( task->status() );
+ setHasStartDate( task->hasStartDate() );
+
+ if ( task->hasDueDate() )
+ setDueDate( localToUTC( task->dtDue() ) );
+ else
+ mHasDueDate = false;
+ if ( task->relatedTo() )
+ setParent( task->relatedTo()->uid() );
+ else if ( !task->relatedToUid().isEmpty() )
+ setParent( task->relatedToUid() );
+ else
+ setParent( QString::null );
+
+ if ( task->hasCompletedDate() && task->percentComplete() == 100 )
+ setCompletedDate( localToUTC( task->completed() ) );
+ else
+ mHasCompletedDate = false;
+}
+
+void Task::saveTo( KCal::Todo* task )
+{
+ Incidence::saveTo( task );
+
+ task->setPriority( priority() );
+ task->setPercentComplete( percentCompleted() );
+ task->setStatus( status() );
+ task->setHasStartDate( hasStartDate() );
+ task->setHasDueDate( hasDueDate() );
+ if ( hasDueDate() )
+ task->setDtDue( utcToLocal( dueDate() ) );
+
+ if ( !parent().isNull() )
+ task->setRelatedToUid( parent() );
+
+ if ( hasCompletedDate() && task->percentComplete() == 100 )
+ task->setCompleted( utcToLocal( mCompletedDate ) );
+}
diff --git a/kresources/kolab/kcal/task.h b/kresources/kolab/kcal/task.h
new file mode 100644
index 00000000..96bcafd9
--- /dev/null
+++ b/kresources/kolab/kcal/task.h
@@ -0,0 +1,129 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KOLAB_TASK_H
+#define KOLAB_TASK_H
+
+#include <incidence.h>
+
+#include <libkcal/incidence.h>
+
+class QDomElement;
+
+namespace KCal {
+ class Todo;
+ class ResourceKolab;
+}
+
+namespace Kolab {
+
+/**
+ * This class represents a task, and knows how to load/save it
+ * from/to XML, and from/to a KCal::Todo.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Task : public Incidence {
+public:
+ /// Use this to parse an xml string to a task entry
+ /// The caller is responsible for deleting the returned task
+ static KCal::Todo* xmlToTask( const QString& xml, const QString& tz, KCal::ResourceKolab *res = 0,
+ const QString& subResource = QString::null, Q_UINT32 sernum = 0 );
+
+ /// Use this to get an xml string describing this task entry
+ static QString taskToXML( KCal::Todo*, const QString& tz );
+
+ explicit Task( KCal::ResourceKolab *res, const QString& subResource, Q_UINT32 sernum,
+ const QString& tz, KCal::Todo* todo = 0 );
+ virtual ~Task();
+
+ virtual QString type() const { return "Task"; }
+
+ void saveTo( KCal::Todo* todo );
+
+ virtual void setPriority( int priority );
+ virtual int priority() const;
+
+ virtual void setPercentCompleted( int percent );
+ virtual int percentCompleted() const;
+
+ virtual void setStatus( KCal::Incidence::Status status );
+ virtual KCal::Incidence::Status status() const;
+
+ virtual void setParent( const QString& parentUid );
+ virtual QString parent() const;
+
+ virtual void setHasStartDate( bool );
+ virtual bool hasStartDate() const;
+
+ virtual void setDueDate( const QDateTime& date );
+ virtual QDateTime dueDate() const;
+ virtual bool hasDueDate() const;
+
+ virtual void setCompletedDate( const QDateTime& date );
+ virtual QDateTime completedDate() const;
+ virtual bool hasCompletedDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this task by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this task to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical todo
+ void setFields( const KCal::Todo* );
+
+ int mPriority;
+ int mPercentCompleted;
+ KCal::Incidence::Status mStatus;
+ QString mParent;
+
+ bool mHasStartDate;
+
+ bool mHasDueDate;
+ QDateTime mDueDate;
+
+ bool mHasCompletedDate;
+ QDateTime mCompletedDate;
+};
+
+}
+
+#endif // KOLAB_TASK_H
diff --git a/kresources/kolab/knotes/Makefile.am b/kresources/kolab/knotes/Makefile.am
new file mode 100644
index 00000000..de4ff1dd
--- /dev/null
+++ b/kresources/kolab/knotes/Makefile.am
@@ -0,0 +1,29 @@
+METASOURCES = AUTO
+
+INCLUDES = -I$(top_srcdir)/kresources/kolab/shared \
+ -I$(top_srcdir) -I$(top_srcdir)/knotes -I$(top_builddir)/libkdepim $(all_includes)
+
+# The kolab wizard links to this library too
+lib_LTLIBRARIES = libknoteskolab.la
+
+libknoteskolab_la_SOURCES = resourcekolab.cpp note.cpp
+libknoteskolab_la_LDFLAGS = $(all_libraries) -no-undefined
+libknoteskolab_la_LIBADD = \
+ $(top_builddir)/kresources/kolab/shared/libresourcekolabshared.la \
+ $(top_builddir)/knotes/libknotesresources.la \
+ $(top_builddir)/libkcal/libkcal.la \
+ -lkresources -lkdeprint
+
+kde_module_LTLIBRARIES = knotes_kolab.la
+
+knotes_kolab_la_SOURCES = resourcekolab_plugin.cpp
+knotes_kolab_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined
+knotes_kolab_la_LIBADD = libknoteskolab.la
+
+servicedir = $(kde_servicesdir)/kresources/knotes
+service_DATA = kolabresource.desktop
+
+install-data-local: $(srcdir)/../uninstall.desktop
+ $(mkinstalldirs) $(DESTDIR)$(servicedir)
+ $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop
+
diff --git a/kresources/kolab/knotes/kolabresource.desktop b/kresources/kolab/knotes/kolabresource.desktop
new file mode 100644
index 00000000..d74e3f59
--- /dev/null
+++ b/kresources/kolab/knotes/kolabresource.desktop
@@ -0,0 +1,53 @@
+[Desktop Entry]
+Name=IMAP Server via KMail
+Name[af]=IMAP bediener via KMail
+Name[bg]=Сървър IMAP през KMail
+Name[br]=Servijer IMAP gant KMail
+Name[ca]=Servidor IMAP mitjançant KMail
+Name[cs]=IMAP server přes KMail
+Name[da]=IMAP-server via KMail
+Name[de]=IMAP-Server via KMail
+Name[el]=Εξυπηρετητής IMAP μέσω του KMail
+Name[es]=Servidor IMAP por medio de KMail
+Name[et]=IMAP-server (KMaili vahendusel)
+Name[eu]=IMAP zerbitzaria KMail-en bidez
+Name[fa]=کارساز IMAP از طریق KMail
+Name[fi]=IMAP-palvelin KMailin avulla
+Name[fr]=Serveur IMAP (via KMail)
+Name[fy]=IMAP-tsjinner fia KMail
+Name[ga]=Freastalaí IMAP via KMail
+Name[gl]=Servidor MAP mediante KMail
+Name[hu]=IMAP-kiszolgáló a KMailen keresztül
+Name[is]=IMAP þjónn gegnum KMail
+Name[it]=Server IMAP via KMail
+Name[ja]=KMail 経由 IMAP サーバ
+Name[ka]= IMAP-ს სერვერთან დაშვება KMail-ის საშუალებით
+Name[kk]=KMail арқылы IMAP сервері
+Name[km]=ម៉ាស៊ីន​បម្រើ IMAP តាម​រយៈ KMail
+Name[lt]=IMAP serveris per KMail
+Name[mk]=IMAP-сервер преку КПошта
+Name[ms]=Pelayan IMAP melalui KMail
+Name[nb]=IMAP-tjener via KMail
+Name[nds]=IMAP-Server över KMail
+Name[ne]=केडीई मेल मार्फत IMAP सर्भर
+Name[nl]=IMAP-server via KMail
+Name[nn]=IMAP-tenar via KMail
+Name[pl]=Serwer IMAP za pośrednictwem KMail
+Name[pt]=Servidor IMAP via KMail
+Name[pt_BR]=Servidor IMAP via KMail
+Name[ru]=Доступ к серверу IMAP через KMail
+Name[sk]=IMAP-server pomocou KMail
+Name[sl]=Strežnik IMAP preko KMaila
+Name[sr]=IMAP сервер преко KMail-а
+Name[sr@Latn]=IMAP server preko KMail-a
+Name[sv]=IMAP-server via Kmail
+Name[ta]=IMAP சேவகன் மூலாம் கேஅஞ்சல்
+Name[tr]=KMail aracılığı ile IMAP Sunucu
+Name[uk]=Сервер IMAP через KMail
+Name[zh_CN]=通过 KMail 访问 IMAP 服务器
+Name[zh_TW]=透過 KMail 取得 IMAP 伺服器
+X-KDE-Library=knotes_kolab
+Type=Service
+ServiceTypes=KResources/Plugin
+X-KDE-ResourceFamily=notes
+X-KDE-ResourceType=imap
diff --git a/kresources/kolab/knotes/note.cpp b/kresources/kolab/knotes/note.cpp
new file mode 100644
index 00000000..76ecdd9d
--- /dev/null
+++ b/kresources/kolab/knotes/note.cpp
@@ -0,0 +1,210 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "note.h"
+
+#include <libkcal/journal.h>
+#include <knotes/version.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCal::Journal* Note::xmlToJournal( const QString& xml )
+{
+ Note note;
+ note.load( xml );
+ KCal::Journal* journal = new KCal::Journal();
+ note.saveTo( journal );
+ return journal;
+}
+
+QString Note::journalToXML( KCal::Journal* journal )
+{
+ Note note( journal );
+ return note.saveXML();
+}
+
+Note::Note( KCal::Journal* journal ) : mRichText( false )
+{
+ if ( journal )
+ setFields( journal );
+}
+
+Note::~Note()
+{
+}
+
+void Note::setSummary( const QString& summary )
+{
+ mSummary = summary;
+}
+
+QString Note::summary() const
+{
+ return mSummary;
+}
+
+void Note::setBackgroundColor( const QColor& bgColor )
+{
+ mBackgroundColor = bgColor;
+}
+
+QColor Note::backgroundColor() const
+{
+ return mBackgroundColor;
+}
+
+void Note::setForegroundColor( const QColor& fgColor )
+{
+ mForegroundColor = fgColor;
+}
+
+QColor Note::foregroundColor() const
+{
+ return mForegroundColor;
+}
+
+void Note::setRichText( bool richText )
+{
+ mRichText = richText;
+}
+
+bool Note::richText() const
+{
+ return mRichText;
+}
+
+bool Note::loadAttribute( QDomElement& element )
+{
+ QString tagName = element.tagName();
+
+ if ( tagName == "summary" )
+ setSummary( element.text() );
+ else if ( tagName == "foreground-color" )
+ setForegroundColor( stringToColor( element.text() ) );
+ else if ( tagName == "background-color" )
+ setBackgroundColor( stringToColor( element.text() ) );
+ else if ( tagName == "knotes-richtext" )
+ mRichText = ( element.text() == "true" );
+ else
+ return KolabBase::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Note::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ // Save the elements
+#if 0
+ QDomComment c = element.ownerDocument().createComment( "Note specific attributes" );
+ element.appendChild( c );
+#endif
+
+ writeString( element, "summary", summary() );
+ writeString( element, "foreground-color", colorToString( foregroundColor() ) );
+ writeString( element, "background-color", colorToString( backgroundColor() ) );
+ writeString( element, "knotes-richtext", mRichText ? "true" : "false" );
+
+ return true;
+}
+
+
+bool Note::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "note" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected note",
+ top.tagName().ascii() );
+ return false;
+ }
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ if ( !loadAttribute( e ) )
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ return true;
+}
+
+QString Note::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "note" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Note::setFields( const KCal::Journal* journal )
+{
+ KolabBase::setFields( journal );
+
+ // TODO: background and foreground
+ setSummary( journal->summary() );
+ setBackgroundColor( journal->customProperty( "KNotes", "BgColor" ) );
+ setForegroundColor( journal->customProperty( "KNotes", "FgColor" ) );
+ setRichText( journal->customProperty( "KNotes", "RichText" ) == "true" );
+}
+
+void Note::saveTo( KCal::Journal* journal )
+{
+ KolabBase::saveTo( journal );
+
+ // TODO: background and foreground
+ journal->setSummary( summary() );
+ journal->setCustomProperty( "KNotes", "FgColor",
+ colorToString( foregroundColor() ) );
+ journal->setCustomProperty( "KNotes", "BgColor",
+ colorToString( backgroundColor() ) );
+ journal->setCustomProperty( "KNotes", "RichText",
+ richText() ? "true" : "false" );
+}
+
+QString Note::productID() const
+{
+ return QString( "KNotes %1, Kolab resource" ).arg( KNOTES_VERSION );
+}
diff --git a/kresources/kolab/knotes/note.h b/kresources/kolab/knotes/note.h
new file mode 100644
index 00000000..45769f97
--- /dev/null
+++ b/kresources/kolab/knotes/note.h
@@ -0,0 +1,111 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KOLAB_NOTE_H
+#define KOLAB_NOTE_H
+
+#include <kolabbase.h>
+
+class QDomElement;
+
+namespace KCal {
+ class Journal;
+}
+
+namespace Kolab {
+
+/**
+ * This class represents a note, and knows how to load/save it
+ * from/to XML, and from/to a KCal::Journal.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Note : public KolabBase {
+public:
+ /// Use this to parse an xml string to a journal entry
+ /// The caller is responsible for deleting the returned journal
+ static KCal::Journal* xmlToJournal( const QString& xml );
+
+ /// Use this to get an xml string describing this journal entry
+ static QString journalToXML( KCal::Journal* );
+
+ /// Create a note object and
+ explicit Note( KCal::Journal* journal = 0 );
+ virtual ~Note();
+
+ void saveTo( KCal::Journal* journal );
+
+ virtual QString type() const { return "Note"; }
+
+ virtual void setSummary( const QString& summary );
+ virtual QString summary() const;
+
+ virtual void setBackgroundColor( const QColor& bgColor );
+ virtual QColor backgroundColor() const;
+
+ virtual void setForegroundColor( const QColor& fgColor );
+ virtual QColor foregroundColor() const;
+
+ virtual void setRichText( bool richText );
+ virtual bool richText() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this note by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this note to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical incidence
+ void setFields( const KCal::Journal* );
+
+ // Save all known fields into this ical incidence
+ void saveTo( KCal::Incidence* ) const;
+
+ QString productID() const;
+
+ QString mSummary;
+ QColor mBackgroundColor;
+ QColor mForegroundColor;
+ bool mRichText;
+};
+
+}
+
+#endif // KOLAB_NOTE_H
diff --git a/kresources/kolab/knotes/resourcekolab.cpp b/kresources/kolab/knotes/resourcekolab.cpp
new file mode 100644
index 00000000..b5cad153
--- /dev/null
+++ b/kresources/kolab/knotes/resourcekolab.cpp
@@ -0,0 +1,437 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+ Copyright (c) 2004 Till Adam <adam@kde.org>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "resourcekolab.h"
+#include "note.h"
+
+#include <knotes/resourcemanager.h>
+
+#include <libkcal/icalformat.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+
+using namespace Kolab;
+
+static const char* configGroupName = "Note";
+static const char* kmailContentsType = "Note";
+static const char* attachmentMimeType = "application/x-vnd.kolab.note";
+static const char* inlineMimeType = "text/calendar";
+
+ResourceKolab::ResourceKolab( const KConfig *config )
+ : ResourceNotes( config ), ResourceKolabBase( "ResourceKolab-KNotes" ),
+ mCalendar( QString::fromLatin1("UTC") )
+{
+ setType( "imap" );
+}
+
+ResourceKolab::~ResourceKolab()
+{
+}
+
+bool ResourceKolab::doOpen()
+{
+ KConfig config( configFile() );
+ config.setGroup( configGroupName );
+
+ // Get the list of Notes folders from KMail
+ QValueList<KMailICalIface::SubResource> subResources;
+ if ( !kmailSubresources( subResources, kmailContentsType ) )
+ return false;
+
+ // Make the resource map from the folder list
+ QValueList<KMailICalIface::SubResource>::ConstIterator it;
+ mSubResources.clear();
+ for ( it = subResources.begin(); it != subResources.end(); ++it ) {
+ const QString subResource = (*it).location;
+ const bool active = config.readBoolEntry( subResource, true );
+ mSubResources[ subResource ] = Kolab::SubResource( active, (*it).writable, (*it).label );
+ }
+
+ return true;
+}
+
+void ResourceKolab::doClose()
+{
+ KConfig config( configFile() );
+ config.setGroup( configGroupName );
+ Kolab::ResourceMap::ConstIterator it;
+ for ( it = mSubResources.begin(); it != mSubResources.end(); ++it )
+ config.writeEntry( it.key(), it.data().active() );
+}
+
+bool ResourceKolab::loadSubResource( const QString& subResource,
+ const QString &mimetype )
+{
+ // Get the list of journals
+ int count = 0;
+ if ( !kmailIncidencesCount( count, mimetype, subResource ) ) {
+ kdError() << "Communication problem in ResourceKolab::load()\n";
+ return false;
+ }
+
+ QMap<Q_UINT32, QString> lst;
+ if( !kmailIncidences( lst, mimetype, subResource, 0, count ) ) {
+ kdError(5500) << "Communication problem in "
+ << "ResourceKolab::getIncidenceList()\n";
+ return false;
+ }
+
+ kdDebug(5500) << "Notes kolab resource: got " << lst.count() << " notes in " << subResource << endl;
+
+ // Populate with the new entries
+ const bool silent = mSilent;
+ mSilent = true;
+ QMap<Q_UINT32, QString>::Iterator it;
+ for ( it = lst.begin(); it != lst.end(); ++it ) {
+ KCal::Journal* journal = addNote( it.data(), subResource, it.key(), mimetype );
+ if ( !journal )
+ kdDebug(5500) << "loading note " << it.key() << " failed" << endl;
+ else
+ manager()->registerNote( this, journal );
+ }
+ mSilent = silent;
+
+ return true;
+}
+
+bool ResourceKolab::load()
+{
+ // We get a fresh list of events, so clean out the old ones
+ mCalendar.deleteAllEvents();
+ mUidMap.clear();
+
+ bool rc = true;
+ Kolab::ResourceMap::ConstIterator itR;
+ for ( itR = mSubResources.begin(); itR != mSubResources.end(); ++itR ) {
+ if ( !itR.data().active() )
+ // This subResource is disabled
+ continue;
+
+ QString mimetype = inlineMimeType;
+ rc &= loadSubResource( itR.key(), mimetype );
+ mimetype = attachmentMimeType;
+ rc &= loadSubResource( itR.key(), mimetype );
+ }
+
+ return rc;
+}
+
+bool ResourceKolab::save()
+{
+ // Nothing to do here, we save everything in incidenceUpdated()
+ return true;
+}
+
+bool ResourceKolab::addNote( KCal::Journal* journal )
+{
+ return addNote( journal, QString::null, 0 );
+}
+
+KCal::Journal* ResourceKolab::addNote( const QString& data, const QString& subresource,
+ Q_UINT32 sernum, const QString &mimetype )
+{
+ KCal::Journal* journal = 0;
+ // FIXME: This does not take into account the time zone!
+ KCal::ICalFormat formatter;
+ if ( mimetype == attachmentMimeType )
+ journal = Note::xmlToJournal( data );
+ else
+ journal = static_cast<KCal::Journal*>( formatter.fromString( data ) );
+
+ Q_ASSERT( journal );
+ if( journal && !mUidMap.contains( journal->uid() ) )
+ if ( addNote( journal, subresource, sernum ) )
+ return journal;
+ else
+ delete journal;
+ return 0;
+}
+
+bool ResourceKolab::addNote( KCal::Journal* journal,
+ const QString& subresource, Q_UINT32 sernum )
+{
+ kdDebug(5500) << "ResourceKolab::addNote( KCal::Journal*, '" << subresource << "', " << sernum << " )\n";
+
+ journal->registerObserver( this );
+
+ // Find out if this note was previously stored in KMail
+ bool newNote = subresource.isEmpty();
+ mCalendar.addJournal( journal );
+
+ QString resource =
+ newNote ? findWritableResource( mSubResources ) : subresource;
+ if ( resource.isEmpty() ) // canceled
+ return false;
+
+ if ( !mSilent ) {
+ QString xml = Note::journalToXML( journal );
+ kdDebug(5500) << k_funcinfo << "XML string:\n" << xml << endl;
+
+ if( !kmailUpdate( resource, sernum, xml, attachmentMimeType, journal->uid() ) ) {
+ kdError(5500) << "Communication problem in ResourceKolab::addNote()\n";
+ return false;
+ }
+ }
+
+ if ( !resource.isEmpty() && sernum != 0 ) {
+ mUidMap[ journal->uid() ] = StorageReference( resource, sernum );
+ return true;
+ }
+
+ return false;
+}
+
+bool ResourceKolab::deleteNote( KCal::Journal* journal )
+{
+ const QString uid = journal->uid();
+ if ( !mUidMap.contains( uid ) )
+ // Odd
+ return false;
+
+ if ( !mSilent ) {
+ kmailDeleteIncidence( mUidMap[ uid ].resource(),
+ mUidMap[ uid ].serialNumber() );
+ }
+ mUidMap.remove( uid );
+ manager()->deleteNote( journal );
+ mCalendar.deleteJournal( journal );
+ return true;
+}
+
+KCal::Alarm::List ResourceKolab::alarms( const QDateTime& from, const QDateTime& to )
+{
+ KCal::Alarm::List alarms;
+ KCal::Journal::List notes = mCalendar.journals();
+ KCal::Journal::List::ConstIterator note;
+ for ( note = notes.begin(); note != notes.end(); ++note )
+ {
+ QDateTime preTime = from.addSecs( -1 );
+ KCal::Alarm::List::ConstIterator it;
+ for( it = (*note)->alarms().begin(); it != (*note)->alarms().end(); ++it )
+ {
+ if ( (*it)->enabled() )
+ {
+ QDateTime dt = (*it)->nextRepetition( preTime );
+ if ( dt.isValid() && dt <= to )
+ alarms.append( *it );
+ }
+ }
+ }
+
+ return alarms;
+}
+
+void ResourceKolab::incidenceUpdated( KCal::IncidenceBase* i )
+{
+ QString subResource;
+ Q_UINT32 sernum;
+ if ( mUidMap.contains( i->uid() ) ) {
+ subResource = mUidMap[ i->uid() ].resource();
+ sernum = mUidMap[ i->uid() ].serialNumber();
+ } else { // can this happen?
+ subResource = findWritableResource( mSubResources );
+ if ( subResource.isEmpty() ) // canceled
+ return;
+ sernum = 0;
+ }
+
+ KCal::Journal* journal = dynamic_cast<KCal::Journal*>( i );
+ QString xml = Note::journalToXML( journal );
+ if( !xml.isEmpty() && kmailUpdate( subResource, sernum, xml, attachmentMimeType, journal->uid() ) )
+ mUidMap[ i->uid() ] = StorageReference( subResource, sernum );
+}
+
+/*
+ * These are the DCOP slots that KMail call to notify when something
+ * changed.
+ */
+bool ResourceKolab::fromKMailAddIncidence( const QString& type,
+ const QString& subResource,
+ Q_UINT32 sernum,
+ int format,
+ const QString& note )
+{
+ // Check if this is a note
+ if( type != kmailContentsType ) return false;
+
+ const bool silent = mSilent;
+ mSilent = true;
+ QString mimetype;
+ if ( format == KMailICalIface::StorageXML )
+ mimetype = attachmentMimeType;
+ else
+ mimetype = inlineMimeType;
+ KCal::Journal* journal = addNote( note, subResource, sernum, mimetype );
+ if ( journal )
+ manager()->registerNote( this, journal );
+ mSilent = silent;
+ return true;
+}
+
+void ResourceKolab::fromKMailDelIncidence( const QString& type,
+ const QString& /*subResource*/,
+ const QString& uid )
+{
+ // Check if this is a note
+ if( type != kmailContentsType ) return;
+
+ kdDebug(5500) << "ResourceKolab::fromKMailDelIncidence( " << type << ", " << uid
+ << " )" << endl;
+
+ const bool silent = mSilent;
+ mSilent = true;
+ KCal::Journal* j = mCalendar.journal( uid );
+ if( j )
+ deleteNote( j );
+ mSilent = silent;
+}
+
+void ResourceKolab::fromKMailRefresh( const QString& type,
+ const QString& /*subResource*/ )
+{
+ if ( type == kmailContentsType )
+ load(); // ### should call loadSubResource(subResource) probably
+}
+
+void ResourceKolab::fromKMailAddSubresource( const QString& type,
+ const QString& subResource,
+ const QString& /*label*/,
+ bool writable,
+ bool /*alarmRelevant*/ )
+{
+ if ( type != kmailContentsType )
+ // Not ours
+ return;
+
+ if ( mSubResources.contains( subResource ) )
+ // Already registered
+ return;
+
+ KConfig config( configFile() );
+ config.setGroup( configGroupName );
+
+ bool active = config.readBoolEntry( subResource, true );
+ mSubResources[ subResource ] = Kolab::SubResource( active, writable, subResource );
+ loadSubResource( subResource, attachmentMimeType );
+ emit signalSubresourceAdded( this, type, subResource );
+}
+
+void ResourceKolab::fromKMailDelSubresource( const QString& type,
+ const QString& subResource )
+{
+ if ( type != configGroupName )
+ // Not ours
+ return;
+
+ if ( !mSubResources.contains( subResource ) )
+ // Not registered
+ return;
+
+ // Ok, it's our job, and we have it here
+ mSubResources.erase( subResource );
+
+ KConfig config( configFile() );
+ config.setGroup( configGroupName );
+ config.deleteEntry( subResource );
+ config.sync();
+
+ // Make a list of all uids to remove
+ Kolab::UidMap::ConstIterator mapIt;
+ QStringList uids;
+ for ( mapIt = mUidMap.begin(); mapIt != mUidMap.end(); ++mapIt )
+ if ( mapIt.data().resource() == subResource )
+ // We have a match
+ uids << mapIt.key();
+
+ // Finally delete all the incidences
+ if ( !uids.isEmpty() ) {
+ const bool silent = mSilent;
+ mSilent = true;
+ QStringList::ConstIterator it;
+ for ( it = uids.begin(); it != uids.end(); ++it ) {
+ KCal::Journal* j = mCalendar.journal( *it );
+ if( j )
+ deleteNote( j );
+ }
+ mSilent = silent;
+ }
+
+ emit signalSubresourceRemoved( this, type, subResource );
+}
+
+void ResourceKolab::fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder )
+{
+ // We are only interested in notes
+ if ( ( type != attachmentMimeType ) && ( type != inlineMimeType ) ) return;
+ // Populate with the new entries
+ const bool silent = mSilent;
+ mSilent = true;
+ QString mimetype;
+ if ( kmailStorageFormat( folder ) == KMailICalIface::StorageXML )
+ mimetype = attachmentMimeType;
+ else
+ mimetype = inlineMimeType;
+ for( QMap<Q_UINT32, QString>::ConstIterator it = map.begin(); it != map.end(); ++it ) {
+ KCal::Journal* journal = addNote( it.data(), folder, it.key(), mimetype );
+ if ( !journal )
+ kdDebug(5500) << "loading note " << it.key() << " failed" << endl;
+ else
+ manager()->registerNote( this, journal );
+ }
+ mSilent = silent;
+}
+
+
+QStringList ResourceKolab::subresources() const
+{
+ return mSubResources.keys();
+}
+
+bool ResourceKolab::subresourceActive( const QString& res ) const
+{
+ if ( mSubResources.contains( res ) ) {
+ return mSubResources[ res ].active();
+ }
+
+ // Safe default bet:
+ kdDebug(5650) << "subresourceActive( " << res << " ): Safe bet\n";
+
+ return true;
+}
+
+
+#include "resourcekolab.moc"
diff --git a/kresources/kolab/knotes/resourcekolab.h b/kresources/kolab/knotes/resourcekolab.h
new file mode 100644
index 00000000..44ce480b
--- /dev/null
+++ b/kresources/kolab/knotes/resourcekolab.h
@@ -0,0 +1,129 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KNOTES_RESOURCEKOLAB_H
+#define KNOTES_RESOURCEKOLAB_H
+
+#include <resourcenotes.h>
+#include <libkcal/incidencebase.h>
+#include <libkcal/calendarlocal.h>
+#include "../shared/resourcekolabbase.h"
+#include "../shared/subresource.h"
+#include <kdepimmacros.h>
+
+
+namespace Kolab {
+
+/**
+ * This class implements a KNotes resource that keeps its
+ * addresses in an IMAP folder in KMail (or other conforming email
+ * clients).
+ */
+class KDE_EXPORT ResourceKolab : public ResourceNotes,
+ public KCal::IncidenceBase::Observer,
+ public ResourceKolabBase
+{
+ Q_OBJECT
+
+public:
+ ResourceKolab( const KConfig* );
+ virtual ~ResourceKolab();
+
+ /// Load resource data.
+ bool load();
+
+ /// Save resource data.
+ bool save();
+
+ /// Open the notes resource.
+ bool doOpen();
+ /// Close the notes resource.
+ void doClose();
+
+ bool addNote( KCal::Journal* );
+
+ bool deleteNote( KCal::Journal* );
+
+ KCal::Alarm::List alarms( const QDateTime& from, const QDateTime& to );
+
+ /// Reimplemented from IncidenceBase::Observer to know when a note was changed
+ void incidenceUpdated( KCal::IncidenceBase* );
+
+ /// The ResourceKolabBase methods called by KMail
+ bool fromKMailAddIncidence( const QString& type, const QString& resource,
+ Q_UINT32 sernum, int format, const QString& note );
+ void fromKMailDelIncidence( const QString& type, const QString& resource,
+ const QString& uid );
+ void fromKMailRefresh( const QString& type, const QString& resource );
+
+ /// Listen to KMail changes in the amount of sub resources
+ void fromKMailAddSubresource( const QString& type, const QString& resource,
+ const QString& label, bool writable,
+ bool alarmRelevant );
+ void fromKMailDelSubresource( const QString& type, const QString& resource );
+
+ void fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder );
+
+ /** Return the list of subresources. */
+ QStringList subresources() const;
+
+ /** Is this subresource active? */
+ bool subresourceActive( const QString& ) const;
+
+signals:
+ void signalSubresourceAdded( Resource*, const QString&, const QString& );
+ void signalSubresourceRemoved( Resource*, const QString&, const QString& );
+
+private:
+ bool addNote( KCal::Journal* journal, const QString& resource,
+ Q_UINT32 sernum );
+ KCal::Journal* addNote( const QString& data, const QString& subresource,
+ Q_UINT32 sernum, const QString &mimetype );
+
+ bool loadSubResource( const QString& resource, const QString& mimetype );
+
+ QString configFile() const {
+ return ResourceKolabBase::configFile( "knotes" );
+ }
+
+ KCal::CalendarLocal mCalendar;
+
+ // The list of subresources
+ Kolab::ResourceMap mSubResources;
+};
+
+}
+
+#endif // KNOTES_RESOURCEKOLAB_H
diff --git a/kresources/kolab/knotes/resourcekolab_plugin.cpp b/kresources/kolab/knotes/resourcekolab_plugin.cpp
new file mode 100644
index 00000000..cd97ecf6
--- /dev/null
+++ b/kresources/kolab/knotes/resourcekolab_plugin.cpp
@@ -0,0 +1,50 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarlvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "resourcekolab.h"
+
+class KolabFactory : public KRES::PluginFactoryBase
+{
+public:
+ KRES::Resource *resource( const KConfig *config )
+ {
+ return new Kolab::ResourceKolab( config );
+ }
+
+ KRES::ConfigWidget *configWidget( QWidget* )
+ {
+ return 0;
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY(knotes_kolab,KolabFactory())
+
diff --git a/kresources/kolab/kolab-resource.upd b/kresources/kolab/kolab-resource.upd
new file mode 100644
index 00000000..d4c9b962
--- /dev/null
+++ b/kresources/kolab/kolab-resource.upd
@@ -0,0 +1,12 @@
+# Update the name of the resource
+Id=kolab-calendar-resource-rename
+File=kresources/calendar/stdrc
+Script=upgrade-resourcetype.pl,perl
+
+Id=kolab-contact-resource-rename
+File=kresources/contact/stdrc
+Script=upgrade-resourcetype.pl,perl
+
+Id=kolab-notes-resource-rename
+File=kresources/notes/stdrc
+Script=upgrade-resourcetype.pl,perl
diff --git a/kresources/kolab/shared/Makefile.am b/kresources/kolab/shared/Makefile.am
new file mode 100644
index 00000000..f75af881
--- /dev/null
+++ b/kresources/kolab/shared/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/kresources/lib $(all_includes)
+
+noinst_HEADERS = resourcekolabbase.h kolabbase.h subresource.h
+
+noinst_LTLIBRARIES = libresourcekolabshared.la
+
+libresourcekolabshared_la_SOURCES = \
+ resourcekolabbase.cpp kmailconnection.cpp kolabbase.cpp \
+ subresource.cpp \
+ kmailconnection.skel kmailicalIface.stub
+libresourcekolabshared_la_METASOURCES = AUTO
+libresourcekolabshared_la_LIBADD = $(top_builddir)/libkcal/libkcal.la $(top_builddir)/libkdepim/libkdepim.la ../../lib/libkgroupwarebase.la
+libresourcekolabshared_la_LDFLAGS = -no-undefined
+
+kmailicalIface_DCOPIDLNG = true
+
+kmailicalIface_DIR = $(top_srcdir)/kmail
diff --git a/kresources/kolab/shared/kmailconnection.cpp b/kresources/kolab/shared/kmailconnection.cpp
new file mode 100644
index 00000000..6db9f3da
--- /dev/null
+++ b/kresources/kolab/shared/kmailconnection.cpp
@@ -0,0 +1,317 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "kmailconnection.h"
+#include "resourcekolabbase.h"
+
+#include <kdebug.h>
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <kdcopservicestarter.h>
+#include <klocale.h>
+
+#include "kmailicalIface_stub.h"
+
+
+using namespace Kolab;
+
+
+KMailConnection::KMailConnection( ResourceKolabBase* resource,
+ const QCString& objId )
+ : DCOPObject( objId ), mResource( resource ), mKMailIcalIfaceStub( 0 )
+{
+ // Make the connection to KMail ready
+ mDCOPClient = new DCOPClient();
+ mDCOPClient->attach();
+ mDCOPClient->registerAs( objId, true );
+
+ kapp->dcopClient()->setNotifications( true );
+ connect( kapp->dcopClient(), SIGNAL( applicationRemoved( const QCString& ) ),
+ this, SLOT( unregisteredFromDCOP( const QCString& ) ) );
+}
+
+KMailConnection::~KMailConnection()
+{
+ kapp->dcopClient()->setNotifications( false );
+ delete mKMailIcalIfaceStub;
+ mKMailIcalIfaceStub = 0;
+ delete mDCOPClient;
+ mDCOPClient = 0;
+}
+
+static const QCString dcopObjectId = "KMailICalIface";
+bool KMailConnection::connectToKMail()
+{
+ if ( !mKMailIcalIfaceStub ) {
+ QString error;
+ QCString dcopService;
+ int result = KDCOPServiceStarter::self()->
+ findServiceFor( "DCOP/ResourceBackend/IMAP", QString::null,
+ QString::null, &error, &dcopService );
+ if ( result != 0 ) {
+ kdError(5650) << "Couldn't connect to the IMAP resource backend\n";
+ // TODO: You might want to show "error" (if not empty) here,
+ // using e.g. KMessageBox
+ return false;
+ }
+
+ mKMailIcalIfaceStub = new KMailICalIface_stub( kapp->dcopClient(),
+ dcopService, dcopObjectId );
+
+ // Attach to the KMail signals
+ if ( !connectKMailSignal( "incidenceAdded(QString,QString,Q_UINT32,int,QString)",
+ "fromKMailAddIncidence(QString,QString,Q_UINT32,int,QString)" ) )
+ kdError(5650) << "DCOP connection to incidenceAdded failed" << endl;
+ if ( !connectKMailSignal( "incidenceDeleted(QString,QString,QString)",
+ "fromKMailDelIncidence(QString,QString,QString)" ) )
+ kdError(5650) << "DCOP connection to incidenceDeleted failed" << endl;
+ if ( !connectKMailSignal( "signalRefresh(QString,QString)",
+ "fromKMailRefresh(QString,QString)" ) )
+ kdError(5650) << "DCOP connection to signalRefresh failed" << endl;
+ if ( !connectKMailSignal( "subresourceAdded( QString, QString, QString, bool, bool )",
+ "fromKMailAddSubresource( QString, QString, QString, bool, bool )" ) )
+ kdError(5650) << "DCOP connection to subresourceAdded failed" << endl;
+ if ( !connectKMailSignal( "subresourceDeleted(QString,QString)",
+ "fromKMailDelSubresource(QString,QString)" ) )
+ kdError(5650) << "DCOP connection to subresourceDeleted failed" << endl;
+ if ( !connectKMailSignal( "asyncLoadResult(QMap<Q_UINT32, QString>, QString, QString)",
+ "fromKMailAsyncLoadResult(QMap<Q_UINT32, QString>, QString, QString)" ) )
+ kdError(5650) << "DCOP connection to asyncLoadResult failed" << endl;
+ }
+
+ return ( mKMailIcalIfaceStub != 0 );
+}
+
+bool KMailConnection::fromKMailAddIncidence( const QString& type,
+ const QString& folder,
+ Q_UINT32 sernum,
+ int format,
+ const QString& data )
+{
+ if ( format != KMailICalIface::StorageXML
+ && format != KMailICalIface::StorageIcalVcard )
+ return false;
+// kdDebug(5650) << "KMailConnection::fromKMailAddIncidence( " << type << ", "
+// << folder << " ). iCal:\n" << ical << endl;
+ return mResource->fromKMailAddIncidence( type, folder, sernum, format, data );
+}
+
+void KMailConnection::fromKMailDelIncidence( const QString& type,
+ const QString& folder,
+ const QString& xml )
+{
+// kdDebug(5650) << "KMailConnection::fromKMailDelIncidence( " << type << ", "
+// << folder << ", " << uid << " )\n";
+ mResource->fromKMailDelIncidence( type, folder, xml );
+}
+
+void KMailConnection::fromKMailRefresh( const QString& type, const QString& folder )
+{
+// kdDebug(5650) << "KMailConnection::fromKMailRefresh( " << type << ", "
+// << folder << " )\n";
+ mResource->fromKMailRefresh( type, folder );
+}
+
+void KMailConnection::fromKMailAddSubresource( const QString& type,
+ const QString& resource,
+ const QString& label,
+ bool writable,
+ bool alarmRelevant )
+{
+// kdDebug(5650) << "KMailConnection::fromKMailAddSubresource( " << type << ", "
+// << resource << " )\n";
+ mResource->fromKMailAddSubresource( type, resource, label,
+ writable, alarmRelevant );
+}
+
+void KMailConnection::fromKMailDelSubresource( const QString& type,
+ const QString& resource )
+{
+// kdDebug(5650) << "KMailConnection::fromKMailDelSubresource( " << type << ", "
+// << resource << " )\n";
+ mResource->fromKMailDelSubresource( type, resource );
+}
+
+void KMailConnection::fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder )
+{
+ mResource->fromKMailAsyncLoadResult( map, type, folder );
+}
+
+bool KMailConnection::connectKMailSignal( const QCString& signal,
+ const QCString& method )
+{
+ return connectDCOPSignal( "kmail", dcopObjectId, signal, method, false )
+ && connectDCOPSignal( "kontact", dcopObjectId, signal, method, false );
+}
+
+bool KMailConnection::kmailSubresources( QValueList<KMailICalIface::SubResource>& lst,
+ const QString& contentsType )
+{
+ if ( !connectToKMail() )
+ return false;
+
+ lst = mKMailIcalIfaceStub->subresourcesKolab( contentsType );
+ return mKMailIcalIfaceStub->ok();
+}
+
+bool KMailConnection::kmailIncidencesCount( int& count,
+ const QString& mimetype,
+ const QString& resource )
+{
+ if ( !connectToKMail() )
+ return false;
+
+ count = mKMailIcalIfaceStub->incidencesKolabCount( mimetype, resource );
+ return mKMailIcalIfaceStub->ok();
+}
+
+bool KMailConnection::kmailIncidences( QMap<Q_UINT32, QString>& lst,
+ const QString& mimetype,
+ const QString& resource,
+ int startIndex,
+ int nbMessages )
+{
+ if ( !connectToKMail() )
+ return false;
+
+ lst = mKMailIcalIfaceStub->incidencesKolab( mimetype, resource, startIndex, nbMessages );
+ return mKMailIcalIfaceStub->ok();
+}
+
+
+bool KMailConnection::kmailGetAttachment( KURL& url,
+ const QString& resource,
+ Q_UINT32 sernum,
+ const QString& filename )
+{
+ if ( !connectToKMail() )
+ return false;
+
+ url = mKMailIcalIfaceStub->getAttachment( resource, sernum, filename );
+ return mKMailIcalIfaceStub->ok();
+}
+
+bool KMailConnection::kmailAttachmentMimetype( QString & mimeType,
+ const QString & resource,
+ Q_UINT32 sernum,
+ const QString & filename )
+{
+ if ( !connectToKMail() )
+ return false;
+ mimeType = mKMailIcalIfaceStub->attachmentMimetype( resource, sernum, filename );
+ return mKMailIcalIfaceStub->ok();
+}
+
+bool KMailConnection::kmailListAttachments(QStringList &list,
+ const QString & resource, Q_UINT32 sernum)
+{
+ if ( !connectToKMail() )
+ return false;
+
+ list = mKMailIcalIfaceStub->listAttachments( resource, sernum );
+ return mKMailIcalIfaceStub->ok();
+}
+
+bool KMailConnection::kmailDeleteIncidence( const QString& resource,
+ Q_UINT32 sernum )
+{
+ return connectToKMail()
+ && mKMailIcalIfaceStub->deleteIncidenceKolab( resource, sernum )
+ && mKMailIcalIfaceStub->ok();
+}
+
+bool KMailConnection::kmailUpdate( const QString& resource,
+ Q_UINT32& sernum,
+ const QString& subject,
+ const QString& plainTextBody,
+ const QMap<QCString, QString>& customHeaders,
+ const QStringList& attachmentURLs,
+ const QStringList& attachmentMimetypes,
+ const QStringList& attachmentNames,
+ const QStringList& deletedAttachments )
+{
+ //kdDebug(5006) << kdBacktrace() << endl;
+ if ( connectToKMail() ) {
+ sernum = mKMailIcalIfaceStub->update( resource, sernum, subject, plainTextBody, customHeaders,
+ attachmentURLs, attachmentMimetypes, attachmentNames,
+ deletedAttachments );
+ return sernum && mKMailIcalIfaceStub->ok();
+ } else
+ return false;
+}
+
+bool KMailConnection::kmailAddSubresource( const QString& resource,
+ const QString& parent,
+ const QString& contentsType )
+{
+ return connectToKMail()
+ && mKMailIcalIfaceStub->addSubresource( resource, parent, contentsType )
+ && mKMailIcalIfaceStub->ok();
+}
+
+bool KMailConnection::kmailRemoveSubresource( const QString& resource )
+{
+ return connectToKMail()
+ && mKMailIcalIfaceStub->removeSubresource( resource )
+ && mKMailIcalIfaceStub->ok();
+}
+
+
+bool KMailConnection::kmailStorageFormat( KMailICalIface::StorageFormat& type,
+ const QString& folder )
+{
+ bool ok = connectToKMail();
+ type = mKMailIcalIfaceStub->storageFormat( folder );
+ return ok && mKMailIcalIfaceStub->ok();
+}
+
+
+bool KMailConnection::kmailTriggerSync( const QString &contentsType )
+{
+ bool ok = connectToKMail();
+ return ok && mKMailIcalIfaceStub->triggerSync( contentsType );
+}
+
+void KMailConnection::unregisteredFromDCOP( const QCString& appId )
+{
+ if ( mKMailIcalIfaceStub && mKMailIcalIfaceStub->app() == appId ) {
+ // Delete the stub so that the next time we need to talk to kmail,
+ // we'll know that we need to start a new one.
+ delete mKMailIcalIfaceStub;
+ mKMailIcalIfaceStub = 0;
+ }
+}
+
+#include "kmailconnection.moc"
diff --git a/kresources/kolab/shared/kmailconnection.h b/kresources/kolab/shared/kmailconnection.h
new file mode 100644
index 00000000..cbb69a15
--- /dev/null
+++ b/kresources/kolab/shared/kmailconnection.h
@@ -0,0 +1,128 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KMAILCONNECTION_H
+#define KMAILCONNECTION_H
+
+#include <dcopobject.h>
+#include <kmail/kmailicalIface.h>
+
+class KURL;
+class DCOPClient;
+class KMailICalIface_stub;
+
+namespace Kolab {
+
+class ResourceKolabBase;
+
+/**
+ This class provides the kmail connectivity for IMAP resources.
+*/
+class KMailConnection : public QObject, public DCOPObject {
+ Q_OBJECT
+ K_DCOP
+
+ // These are the methods called by KMail when the resource changes
+k_dcop:
+ bool fromKMailAddIncidence( const QString& type, const QString& resource,
+ Q_UINT32 sernum, int format, const QString& xml );
+ void fromKMailDelIncidence( const QString& type, const QString& resource,
+ const QString& xml );
+ void fromKMailRefresh( const QString& type, const QString& resource );
+ void fromKMailAddSubresource( const QString& type, const QString& resource,
+ const QString& label, bool writable,
+ bool alarmRelevant );
+ void fromKMailDelSubresource( const QString& type, const QString& resource );
+ void fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map, const QString& type,
+ const QString& folder );
+
+public:
+ KMailConnection( ResourceKolabBase* resource, const QCString& objId );
+ virtual ~KMailConnection();
+
+ /**
+ * Do the connection to KMail.
+ */
+ bool connectToKMail();
+
+ // Call the DCOP methods
+ bool kmailSubresources( QValueList<KMailICalIface::SubResource>& lst,
+ const QString& contentsType );
+ bool kmailIncidencesCount( int& count,
+ const QString& mimetype,
+ const QString& resource );
+ bool kmailIncidences( QMap<Q_UINT32, QString>& lst, const QString& mimetype,
+ const QString& resource,
+ int startIndex,
+ int nbMessages );
+
+ bool kmailGetAttachment( KURL& url, const QString& resource, Q_UINT32 sernum,
+ const QString& filename );
+ bool kmailAttachmentMimetype( QString &mimeType, const QString &resource,
+ Q_UINT32 sernum, const QString &filename );
+ bool kmailListAttachments( QStringList &list, const QString &resource,
+ Q_UINT32 sernum );
+ bool kmailDeleteIncidence( const QString& resource, Q_UINT32 sernum );
+ bool kmailUpdate( const QString& resource,
+ Q_UINT32& sernum,
+ const QString& subject,
+ const QString& plainTextBody,
+ const QMap<QCString, QString>& customHeaders,
+ const QStringList& attachmentURLs,
+ const QStringList& attachmentMimetypes,
+ const QStringList& attachmentNames,
+ const QStringList& deletedAttachments );
+
+ bool kmailStorageFormat( KMailICalIface::StorageFormat& type, const QString& folder);
+
+ bool kmailTriggerSync( const QString& contentsType );
+ bool kmailAddSubresource( const QString& resource,
+ const QString& parent,
+ const QString& contentsType );
+ bool kmailRemoveSubresource( const QString& resource );
+
+private slots:
+ virtual void unregisteredFromDCOP( const QCString& );
+
+private:
+ /** Connect a signal from KMail to a local slot. */
+ bool connectKMailSignal( const QCString&, const QCString& );
+
+ ResourceKolabBase* mResource;
+ DCOPClient* mDCOPClient;
+ KMailICalIface_stub* mKMailIcalIfaceStub;
+};
+
+}
+
+#endif // KMAILCONNECTION_H
diff --git a/kresources/kolab/shared/kolabbase.cpp b/kresources/kolab/shared/kolabbase.cpp
new file mode 100644
index 00000000..44c44ead
--- /dev/null
+++ b/kresources/kolab/shared/kolabbase.cpp
@@ -0,0 +1,474 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "kolabbase.h"
+
+#include <kabc/addressee.h>
+#include <libkcal/journal.h>
+#include <libkdepim/kpimprefs.h>
+#include <kdebug.h>
+#include <qfile.h>
+
+using namespace Kolab;
+
+
+KolabBase::KolabBase( const QString& tz )
+ : mCreationDate( QDateTime::currentDateTime() ),
+ mLastModified( QDateTime::currentDateTime() ),
+ mSensitivity( Public ), mTimeZoneId( tz ),
+ mHasPilotSyncId( false ), mHasPilotSyncStatus( false )
+{
+}
+
+KolabBase::~KolabBase()
+{
+}
+
+void KolabBase::setFields( const KCal::Incidence* incidence )
+{
+ // So far unhandled KCal::IncidenceBase fields:
+ // mPilotID, mSyncStatus, mFloats
+
+ setUid( incidence->uid() );
+ setBody( incidence->description() );
+ setCategories( incidence->categoriesStr() );
+ setCreationDate( localToUTC( incidence->created() ) );
+ setLastModified( localToUTC( incidence->lastModified() ) );
+ setSensitivity( static_cast<Sensitivity>( incidence->secrecy() ) );
+ // TODO: Attachments
+}
+
+void KolabBase::saveTo( KCal::Incidence* incidence ) const
+{
+ incidence->setUid( uid() );
+ incidence->setDescription( body() );
+ incidence->setCategories( categories() );
+ incidence->setCreated( utcToLocal( creationDate() ) );
+ incidence->setLastModified( utcToLocal( lastModified() ) );
+ incidence->setSecrecy( sensitivity() );
+ // TODO: Attachments
+}
+
+void KolabBase::setFields( const KABC::Addressee* addressee )
+{
+ // An addressee does not have a creation date, so somehow we should
+ // make one, if this is a new entry
+
+ setUid( addressee->uid() );
+ setBody( addressee->note() );
+ setCategories( addressee->categories().join( "," ) );
+
+ // Set creation-time and last-modification-time
+ const QString creationString = addressee->custom( "KOLAB", "CreationDate" );
+ kdDebug(5006) << "Creation time string: " << creationString << endl;
+ QDateTime creationDate;
+ if ( creationString.isEmpty() ) {
+ creationDate = QDateTime::currentDateTime();
+ kdDebug(5006) << "Creation date set to current time\n";
+ }
+ else {
+ creationDate = stringToDateTime( creationString );
+ kdDebug(5006) << "Creation date loaded\n";
+ }
+ QDateTime modified = addressee->revision();
+ if ( !modified.isValid() )
+ modified = QDateTime::currentDateTime();
+ setLastModified( modified );
+ if ( modified < creationDate ) {
+ // It's not possible that the modification date is earlier than creation
+ creationDate = modified;
+ kdDebug(5006) << "Creation date set to modification date\n";
+ }
+ setCreationDate( creationDate );
+ const QString newCreationDate = dateTimeToString( creationDate );
+ if ( creationString != newCreationDate ) {
+ // We modified the creation date, so store it for future reference
+ const_cast<KABC::Addressee*>( addressee )
+ ->insertCustom( "KOLAB", "CreationDate", newCreationDate );
+ kdDebug(5006) << "Creation date modified. New one: " << newCreationDate << endl;
+ }
+
+ switch( addressee->secrecy().type() ) {
+ case KABC::Secrecy::Private:
+ setSensitivity( Private );
+ break;
+ case KABC::Secrecy::Confidential:
+ setSensitivity( Confidential );
+ break;
+ default:
+ setSensitivity( Public );
+ }
+
+ // TODO: Attachments
+}
+
+void KolabBase::saveTo( KABC::Addressee* addressee ) const
+{
+ addressee->setUid( uid() );
+ addressee->setNote( body() );
+ addressee->setCategories( QStringList::split( ',', categories() ) );
+ addressee->setRevision( lastModified() );
+ addressee->insertCustom( "KOLAB", "CreationDate",
+ dateTimeToString( creationDate() ) );
+
+ switch( sensitivity() ) {
+ case Private:
+ addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Private ) );
+ break;
+ case Confidential:
+ addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Confidential ) );
+ break;
+ default:
+ addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Public ) );
+ break;
+ }
+
+ // TODO: Attachments
+}
+
+void KolabBase::setUid( const QString& uid )
+{
+ mUid = uid;
+}
+
+QString KolabBase::uid() const
+{
+ return mUid;
+}
+
+void KolabBase::setBody( const QString& body )
+{
+ mBody = body;
+}
+
+QString KolabBase::body() const
+{
+ return mBody;
+}
+
+void KolabBase::setCategories( const QString& categories )
+{
+ mCategories = categories;
+}
+
+QString KolabBase::categories() const
+{
+ return mCategories;
+}
+
+void KolabBase::setCreationDate( const QDateTime& date )
+{
+ mCreationDate = date;
+}
+
+QDateTime KolabBase::creationDate() const
+{
+ return mCreationDate;
+}
+
+void KolabBase::setLastModified( const QDateTime& date )
+{
+ mLastModified = date;
+}
+
+QDateTime KolabBase::lastModified() const
+{
+ return mLastModified;
+}
+
+void KolabBase::setSensitivity( Sensitivity sensitivity )
+{
+ mSensitivity = sensitivity;
+}
+
+KolabBase::Sensitivity KolabBase::sensitivity() const
+{
+ return mSensitivity;
+}
+
+void KolabBase::setPilotSyncId( unsigned long id )
+{
+ mHasPilotSyncId = true;
+ mPilotSyncId = id;
+}
+
+bool KolabBase::hasPilotSyncId() const
+{
+ return mHasPilotSyncId;
+}
+
+unsigned long KolabBase::pilotSyncId() const
+{
+ return mPilotSyncId;
+}
+
+void KolabBase::setPilotSyncStatus( int status )
+{
+ mHasPilotSyncStatus = true;
+ mPilotSyncStatus = status;
+}
+
+bool KolabBase::hasPilotSyncStatus() const
+{
+ return mHasPilotSyncStatus;
+}
+
+int KolabBase::pilotSyncStatus() const
+{
+ return mPilotSyncStatus;
+}
+
+bool KolabBase::loadEmailAttribute( QDomElement& element, Email& email )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ const QString tagName = e.tagName();
+
+ if ( tagName == "display-name" )
+ email.displayName = e.text();
+ else if ( tagName == "smtp-address" )
+ email.smtpAddress = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ return true;
+}
+
+void KolabBase::saveEmailAttribute( QDomElement& element, const Email& email,
+ const QString& tagName ) const
+{
+ QDomElement e = element.ownerDocument().createElement( tagName );
+ element.appendChild( e );
+ writeString( e, "display-name", email.displayName );
+ writeString( e, "smtp-address", email.smtpAddress );
+}
+
+bool KolabBase::loadAttribute( QDomElement& element )
+{
+ const QString tagName = element.tagName();
+ switch ( tagName[0].latin1() ) {
+ case 'u':
+ if ( tagName == "uid" ) {
+ setUid( element.text() );
+ return true;
+ }
+ break;
+ case 'b':
+ if ( tagName == "body" ) {
+ setBody( element.text() );
+ return true;
+ }
+ break;
+ case 'c':
+ if ( tagName == "categories" ) {
+ setCategories( element.text() );
+ return true;
+ }
+ if ( tagName == "creation-date" ) {
+ setCreationDate( stringToDateTime( element.text() ) );
+ return true;
+ }
+ break;
+ case 'l':
+ if ( tagName == "last-modification-date" ) {
+ setLastModified( stringToDateTime( element.text() ) );
+ return true;
+ }
+ break;
+ case 's':
+ if ( tagName == "sensitivity" ) {
+ setSensitivity( stringToSensitivity( element.text() ) );
+ return true;
+ }
+ break;
+ case 'p':
+ if ( tagName == "product-id" )
+ return true; // ignore this field
+ if ( tagName == "pilot-sync-id" ) {
+ setPilotSyncId( element.text().toULong() );
+ return true;
+ }
+ if ( tagName == "pilot-sync-status" ) {
+ setPilotSyncStatus( element.text().toInt() );
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool KolabBase::saveAttributes( QDomElement& element ) const
+{
+ writeString( element, "product-id", productID() );
+ writeString( element, "uid", uid() );
+ writeString( element, "body", body() );
+ writeString( element, "categories", categories() );
+ writeString( element, "creation-date", dateTimeToString( creationDate() ) );
+ writeString( element, "last-modification-date",
+ dateTimeToString( lastModified() ) );
+ writeString( element, "sensitivity", sensitivityToString( sensitivity() ) );
+ if ( hasPilotSyncId() )
+ writeString( element, "pilot-sync-id", QString::number( pilotSyncId() ) );
+ if ( hasPilotSyncStatus() )
+ writeString( element, "pilot-sync-status", QString::number( pilotSyncStatus() ) );
+ return true;
+}
+
+bool KolabBase::load( const QString& xml )
+{
+ QString errorMsg;
+ int errorLine, errorColumn;
+ QDomDocument document;
+ bool ok = document.setContent( xml, &errorMsg, &errorLine, &errorColumn );
+
+ if ( !ok ) {
+ qWarning( "Error loading document: %s, line %d, column %d",
+ errorMsg.latin1(), errorLine, errorColumn );
+ return false;
+ }
+
+ // XML file loaded into tree. Now parse it
+ return loadXML( document );
+}
+
+bool KolabBase::load( QFile& xml )
+{
+ QString errorMsg;
+ int errorLine, errorColumn;
+ QDomDocument document;
+ bool ok = document.setContent( &xml, &errorMsg, &errorLine, &errorColumn );
+
+ if ( !ok ) {
+ qWarning( "Error loading document: %s, line %d, column %d",
+ errorMsg.latin1(), errorLine, errorColumn );
+ return false;
+ }
+
+ // XML file loaded into tree. Now parse it
+ return loadXML( document );
+}
+
+QDomDocument KolabBase::domTree()
+{
+ QDomDocument document;
+
+ QString p = "version=\"1.0\" encoding=\"UTF-8\"";
+ document.appendChild(document.createProcessingInstruction( "xml", p ) );
+
+ return document;
+}
+
+
+QString KolabBase::dateTimeToString( const QDateTime& time )
+{
+ return time.toString( Qt::ISODate ) + 'Z';
+}
+
+QString KolabBase::dateToString( const QDate& date )
+{
+ return date.toString( Qt::ISODate );
+}
+
+QDateTime KolabBase::stringToDateTime( const QString& _date )
+{
+ QString date( _date );
+ if ( date.endsWith( "Z" ) )
+ date.truncate( date.length() - 1 );
+ return QDateTime::fromString( date, Qt::ISODate );
+}
+
+QDate KolabBase::stringToDate( const QString& date )
+{
+ return QDate::fromString( date, Qt::ISODate );
+}
+
+QString KolabBase::sensitivityToString( Sensitivity s )
+{
+ switch( s ) {
+ case Private: return "private";
+ case Confidential: return "confidential";
+ case Public: return "public";
+ }
+
+ return "What what what???";
+}
+
+KolabBase::Sensitivity KolabBase::stringToSensitivity( const QString& s )
+{
+ if ( s == "private" )
+ return Private;
+ if ( s == "confidential" )
+ return Confidential;
+ return Public;
+}
+
+QString KolabBase::colorToString( const QColor& color )
+{
+ // Color is in the format "#RRGGBB"
+ return color.name();
+}
+
+QColor KolabBase::stringToColor( const QString& s )
+{
+ return QColor( s );
+}
+
+void KolabBase::writeString( QDomElement& element, const QString& tag,
+ const QString& tagString )
+{
+ if ( !tagString.isEmpty() ) {
+ QDomElement e = element.ownerDocument().createElement( tag );
+ QDomText t = element.ownerDocument().createTextNode( tagString );
+ e.appendChild( t );
+ element.appendChild( e );
+ }
+}
+
+QDateTime KolabBase::localToUTC( const QDateTime& time ) const
+{
+ return KPimPrefs::localTimeToUtc( time, mTimeZoneId );
+}
+
+QDateTime KolabBase::utcToLocal( const QDateTime& time ) const
+{
+ return KPimPrefs::utcToLocalTime( time, mTimeZoneId );
+}
diff --git a/kresources/kolab/shared/kolabbase.h b/kresources/kolab/shared/kolabbase.h
new file mode 100644
index 00000000..05368398
--- /dev/null
+++ b/kresources/kolab/shared/kolabbase.h
@@ -0,0 +1,177 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef KOLABBASE_H
+#define KOLABBASE_H
+
+#include <qdom.h>
+#include <qdatetime.h>
+#include <qcolor.h>
+
+class QFile;
+
+namespace KCal {
+ class Incidence;
+}
+
+namespace KABC {
+ class Addressee;
+}
+
+namespace Kolab {
+
+class KolabBase {
+public:
+ struct Email {
+ public:
+ Email( const QString& name = QString::null,
+ const QString& email = QString::null )
+ : displayName( name ), smtpAddress( email )
+ {
+ }
+
+ QString displayName;
+ QString smtpAddress;
+ };
+
+ enum Sensitivity { Public = 0, Private = 1, Confidential = 2 };
+
+ explicit KolabBase( const QString& timezone = QString::null );
+ virtual ~KolabBase();
+
+ // Return a string identifying this type
+ virtual QString type() const = 0;
+
+ virtual void setUid( const QString& uid );
+ virtual QString uid() const;
+
+ virtual void setBody( const QString& body );
+ virtual QString body() const;
+
+ virtual void setCategories( const QString& categories );
+ virtual QString categories() const;
+
+ virtual void setCreationDate( const QDateTime& date );
+ virtual QDateTime creationDate() const;
+
+ virtual void setLastModified( const QDateTime& date );
+ virtual QDateTime lastModified() const;
+
+ virtual void setSensitivity( Sensitivity sensitivity );
+ virtual Sensitivity sensitivity() const;
+
+ virtual void setPilotSyncId( unsigned long id );
+ virtual bool hasPilotSyncId() const;
+ virtual unsigned long pilotSyncId() const;
+
+ virtual void setPilotSyncStatus( int status );
+ virtual bool hasPilotSyncStatus() const;
+ virtual int pilotSyncStatus() const;
+
+ // String - Date conversion methods
+ static QString dateTimeToString( const QDateTime& time );
+ static QString dateToString( const QDate& date );
+ static QDateTime stringToDateTime( const QString& time );
+ static QDate stringToDate( const QString& date );
+
+ // String - Sensitivity conversion methods
+ static QString sensitivityToString( Sensitivity );
+ static Sensitivity stringToSensitivity( const QString& );
+
+ // String - Color conversion methods
+ static QString colorToString( const QColor& );
+ static QColor stringToColor( const QString& );
+
+ // Load this object by reading the XML file
+ bool load( const QString& xml );
+ bool load( QFile& xml );
+
+ // Load this QDomDocument
+ virtual bool loadXML( const QDomDocument& xml ) = 0;
+
+ // Serialize this object to an XML string
+ virtual QString saveXML() const = 0;
+
+protected:
+ /// Read all known fields from this ical incidence
+ void setFields( const KCal::Incidence* );
+
+ /// Save all known fields into this ical incidence
+ void saveTo( KCal::Incidence* ) const;
+
+ /// Read all known fields from this contact
+ void setFields( const KABC::Addressee* );
+
+ /// Save all known fields into this contact
+ void saveTo( KABC::Addressee* ) const;
+
+ // This just makes the initial dom tree with version and doctype
+ static QDomDocument domTree();
+
+ bool loadEmailAttribute( QDomElement& element, Email& email );
+
+ void saveEmailAttribute( QDomElement& element, const Email& email,
+ const QString& tagName = "email" ) const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Return the product ID
+ virtual QString productID() const = 0;
+
+ // Write a string tag
+ static void writeString( QDomElement&, const QString&, const QString& );
+
+ QDateTime localToUTC( const QDateTime& time ) const;
+ QDateTime utcToLocal( const QDateTime& time ) const;
+
+ QString mUid;
+ QString mBody;
+ QString mCategories;
+ QDateTime mCreationDate;
+ QDateTime mLastModified;
+ Sensitivity mSensitivity;
+ QString mTimeZoneId;
+
+ // KPilot synchronization stuff
+ bool mHasPilotSyncId, mHasPilotSyncStatus;
+ unsigned long mPilotSyncId;
+ int mPilotSyncStatus;
+};
+
+}
+
+#endif // KOLABBASE_H
diff --git a/kresources/kolab/shared/resourcekolabbase.cpp b/kresources/kolab/shared/resourcekolabbase.cpp
new file mode 100644
index 00000000..d62a5bb5
--- /dev/null
+++ b/kresources/kolab/shared/resourcekolabbase.cpp
@@ -0,0 +1,255 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "resourcekolabbase.h"
+#include "kmailconnection.h"
+
+#include <folderselectdialog.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kinputdialog.h>
+#include <kurl.h>
+#include <ktempfile.h>
+#include <kmessagebox.h>
+#include <qtextstream.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+static unsigned int uniquifier = 0;
+
+ResourceKolabBase::ResourceKolabBase( const QCString& objId )
+ : mSilent( false )
+{
+ KGlobal::locale()->insertCatalogue( "kres_kolab" );
+ KGlobal::locale()->insertCatalogue( "libkcal" );
+ QString uniqueObjId = QString( objId ) + QString::number( uniquifier++ );
+ mConnection = new KMailConnection( this, uniqueObjId.utf8() );
+}
+
+ResourceKolabBase::~ResourceKolabBase()
+{
+ delete mConnection;
+}
+
+
+bool ResourceKolabBase::kmailSubresources( QValueList<KMailICalIface::SubResource>& lst,
+ const QString& contentsType ) const
+{
+ return mConnection->kmailSubresources( lst, contentsType );
+}
+
+bool ResourceKolabBase::kmailTriggerSync( const QString& contentsType ) const
+{
+ return mConnection->kmailTriggerSync( contentsType );
+}
+
+
+bool ResourceKolabBase::kmailIncidencesCount( int &count,
+ const QString& mimetype,
+ const QString& resource ) const
+{
+ return mConnection->kmailIncidencesCount( count, mimetype, resource );
+}
+
+bool ResourceKolabBase::kmailIncidences( QMap<Q_UINT32, QString>& lst,
+ const QString& mimetype,
+ const QString& resource,
+ int startIndex,
+ int nbMessages ) const
+{
+ return mConnection->kmailIncidences( lst, mimetype, resource, startIndex, nbMessages );
+}
+
+bool ResourceKolabBase::kmailGetAttachment( KURL& url, const QString& resource,
+ Q_UINT32 sernum,
+ const QString& filename ) const
+{
+ return mConnection->kmailGetAttachment( url, resource, sernum, filename );
+}
+
+bool ResourceKolabBase::kmailAttachmentMimetype( QString & mimeType, QString & resource,
+ Q_UINT32 sernum, const QString & filename ) const
+{
+ return mConnection->kmailAttachmentMimetype( mimeType, resource, sernum, filename );
+}
+
+bool ResourceKolabBase::kmailListAttachments( QStringList &list,
+ const QString & resource,
+ Q_UINT32 sernum ) const
+{
+ return mConnection->kmailListAttachments( list, resource, sernum );
+}
+
+bool ResourceKolabBase::kmailDeleteIncidence( const QString& resource,
+ Q_UINT32 sernum )
+{
+ return mSilent || mConnection->kmailDeleteIncidence( resource, sernum );
+}
+
+static QString plainTextBody()
+{
+ const char * firstPartTextToTranslate = I18N_NOOP(
+ "This is a Kolab Groupware object.\nTo view this object you"
+ " will need an email client that can understand the Kolab"
+ " Groupware format.\nFor a list of such email clients please"
+ " visit\n%1" );
+ const char * url = "http://www.kolab.org/kolab2-clients.html";
+ QString firstPartTextUntranslated = QString::fromLatin1( firstPartTextToTranslate ).arg( url );
+ QString firstPartText = i18n( firstPartTextToTranslate ).arg( url );
+ if ( firstPartText != firstPartTextUntranslated ) {
+ firstPartText.append("\n\n-----------------------------------------------------\n\n");
+ firstPartText.append( firstPartTextUntranslated );
+ }
+ return firstPartText;
+}
+
+bool ResourceKolabBase::kmailUpdate( const QString& resource,
+ Q_UINT32& sernum,
+ const QString& xml,
+ const QString& mimetype,
+ const QString& subject,
+ const CustomHeaderMap& _customHeaders,
+ const QStringList& _attachmentURLs,
+ const QStringList& _attachmentMimetypes,
+ const QStringList& _attachmentNames,
+ const QStringList& deletedAttachments )
+{
+ if ( mSilent )
+ return true;
+
+ QString subj = subject;
+ if ( subj.isEmpty() )
+ subj = i18n("Internal kolab data: Do not delete this mail.");
+
+ if ( mimetype.startsWith( "application/x-vnd.kolab" ) ) {
+
+ // Save the xml file. Will be deleted at the end of this method
+ KTempFile file;
+ file.setAutoDelete( true );
+ QTextStream* stream = file.textStream();
+ stream->setEncoding( QTextStream::UnicodeUTF8 );
+ *stream << xml;
+ file.close();
+
+ // Add the xml file as an attachment
+ QStringList attachmentURLs = _attachmentURLs;
+ QStringList attachmentMimeTypes = _attachmentMimetypes;
+ QStringList attachmentNames = _attachmentNames;
+ KURL url;
+ url.setPath( file.name() );
+ url.setFileEncoding( "UTF-8" );
+ attachmentURLs.prepend( url.url() );
+ attachmentMimeTypes.prepend( mimetype );
+ attachmentNames.prepend( "kolab.xml" );
+
+ CustomHeaderMap customHeaders( _customHeaders );
+ customHeaders.insert( "X-Kolab-Type", mimetype );
+
+ return mConnection->kmailUpdate( resource, sernum, subj, plainTextBody(), customHeaders,
+ attachmentURLs, attachmentMimeTypes, attachmentNames,
+ deletedAttachments );
+ } else {
+ // ical style, simply put the data inline
+ return mConnection->kmailUpdate( resource, sernum, subj, xml, _customHeaders,
+ _attachmentURLs, _attachmentMimetypes, _attachmentNames, deletedAttachments );
+ }
+}
+
+QString ResourceKolabBase::configFile( const QString& type ) const
+{
+ return locateLocal( "config",
+ QString( "kresources/kolab/%1rc" ).arg( type ) );
+}
+
+bool ResourceKolabBase::connectToKMail() const
+{
+ return mConnection->connectToKMail();
+}
+
+bool ResourceKolabBase::kmailAddSubresource( const QString& resource,
+ const QString& parent,
+ const QString& contentsType )
+{
+ return mConnection->kmailAddSubresource( resource, parent, contentsType );
+}
+
+bool ResourceKolabBase::kmailRemoveSubresource( const QString& resource )
+{
+ return mConnection->kmailRemoveSubresource( resource );
+}
+
+QString ResourceKolabBase::findWritableResource( const ResourceMap& resources,
+ const QString& text )
+{
+ // I have to use the label (shown in the dialog) as key here. But given how the
+ // label is made up, it should be unique. If it's not, well the dialog would suck anyway...
+ QMap<QString, QString> possible;
+ QStringList labels;
+ ResourceMap::ConstIterator it;
+ for ( it = resources.begin(); it != resources.end(); ++it ) {
+ if ( it.data().writable() && it.data().active() ) {
+ // Writable and active possibility
+ possible[ it.data().label() ] = it.key();
+ }
+ }
+
+ if ( possible.isEmpty() ) { // None found!!
+ kdWarning(5650) << "No writable resource found!" << endl;
+ KMessageBox::error( 0, i18n( "No writable resource was found, saving will not be possible. Reconfigure KMail first." ) );
+ return QString::null;
+ }
+ if ( possible.count() == 1 )
+ // Just one found
+ return possible.begin().data(); // yes this is the subresource key, i.e. location
+
+ QString t = text;
+ if ( t.isEmpty() )
+ i18n( "You have more than one writable resource folder. "
+ "Please select the one you want to write to." );
+
+ // Several found, ask the user
+ QString chosenLabel = KPIM::FolderSelectDialog::getItem( i18n( "Select Resource Folder" ),
+ t, possible.keys() );
+ if ( chosenLabel.isEmpty() ) // cancelled
+ return QString::null;
+ return possible[chosenLabel];
+}
+
+KMailICalIface::StorageFormat ResourceKolabBase::kmailStorageFormat( const QString &folder ) const
+{
+ KMailICalIface::StorageFormat format = (KMailICalIface::StorageFormat) 3;
+ mConnection->kmailStorageFormat( format, folder );
+ return format;
+}
diff --git a/kresources/kolab/shared/resourcekolabbase.h b/kresources/kolab/shared/resourcekolabbase.h
new file mode 100644
index 00000000..80622018
--- /dev/null
+++ b/kresources/kolab/shared/resourcekolabbase.h
@@ -0,0 +1,195 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef RESOURCEKOLABBASE_H
+#define RESOURCEKOLABBASE_H
+
+#include <qstring.h>
+#include <qmap.h>
+#include <qstringlist.h>
+
+#include "subresource.h"
+#include <kmail/kmailicalIface.h>
+
+class QCString;
+class KURL;
+
+namespace Kolab {
+
+class KMailConnection;
+
+/**
+ This class provides the kmail connectivity for IMAP resources.
+
+ The main methods are:
+
+ fromKMail...() : calls made _by_ KMail to add/delete data representation in the resource.
+
+ kmail...() : calls _into_ KMail made by the resource.
+
+ e.g. fromKMailAddIncidence() is called by KMail
+ when a new iCard is there after an IMAP sync.
+
+ By calling fromKMailAddIncidence() KMail notifies
+ the resource about the new incidence, so in the
+ addressbook a new address will appear like magic.
+
+ e.g. kmailAddIncidence() is called by the resource when
+ iCard must be stored by KMail because the user has added
+ an address in the addressbook.
+
+ By calling kmailAddIncidence() the resource causes
+ KMail to store the new address in the (IMAP) folder.
+*/
+class ResourceKolabBase {
+public:
+ ResourceKolabBase( const QCString& objId );
+ virtual ~ResourceKolabBase();
+
+ // These are the methods called by KMail when the resource changes
+ virtual bool fromKMailAddIncidence( const QString& type,
+ const QString& resource,
+ Q_UINT32 sernum,
+ int format,
+ const QString& data ) = 0;
+ virtual void fromKMailDelIncidence( const QString& type,
+ const QString& resource,
+ const QString& xml ) = 0;
+ virtual void fromKMailRefresh( const QString& type,
+ const QString& resource ) = 0;
+ virtual void fromKMailAddSubresource( const QString& type,
+ const QString& resource,
+ const QString& label,
+ bool writable,
+ bool alarmRelevant ) = 0;
+ virtual void fromKMailDelSubresource( const QString& type,
+ const QString& resource ) = 0;
+
+ virtual void fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder ) = 0;
+protected:
+ /// Do the connection to KMail.
+ bool connectToKMail() const;
+
+ // These are the KMail dcop function connections. The docs here say
+ // "Get", which here means that the first argument is the return arg
+
+ /// List all folders with a certain contentsType. Returns a QMap with
+ /// resourcename/writable pairs
+ bool kmailSubresources( QValueList<KMailICalIface::SubResource>& lst,
+ const QString& contentsType ) const;
+
+ /// Get the number of messages in this folder.
+ /// Used to iterate over kmailIncidences by chunks
+ bool kmailIncidencesCount( int& count, const QString& mimetype,
+ const QString& resource ) const;
+
+ /// Get the mimetype attachments from a chunk of messages from this folder.
+ /// Returns a QMap with serialNumber/attachment pairs.
+ bool kmailIncidences( QMap<Q_UINT32, QString>& lst, const QString& mimetype,
+ const QString& resource,
+ int startIndex,
+ int nbMessages ) const;
+
+ bool kmailTriggerSync( const QString& contentType ) const;
+
+public: // for Contact
+ /// Get an attachment from a mail. Returns a URL to it. This can
+ /// be called by the resource after obtaining the incidence.
+ /// The resource must delete the temp file.
+ bool kmailGetAttachment( KURL& url, const QString& resource,
+ Q_UINT32 sernum,
+ const QString& filename ) const;
+
+ /** Get the mimetype of the specified attachment. */
+ bool kmailAttachmentMimetype( QString &mimeType, QString &resource,
+ Q_UINT32 sernum, const QString &filename ) const;
+
+ /// List all attachments of a mail.
+ bool kmailListAttachments( QStringList &list, const QString &resource,
+ Q_UINT32 sernum ) const;
+
+protected:
+ /// Delete an incidence.
+ bool kmailDeleteIncidence( const QString& resource, Q_UINT32 sernum );
+
+ KMailICalIface::StorageFormat kmailStorageFormat( const QString& folder ) const;
+
+ typedef QMap<QCString, QString> CustomHeaderMap;
+
+ /// Update an incidence. The list of attachments are URLs.
+ /// The parameter sernum is updated with the right KMail serial number
+ bool kmailUpdate( const QString& resource, Q_UINT32& sernum,
+ const QString& xml,
+ const QString& mimetype,
+ const QString& subject,
+ const CustomHeaderMap& customHeaders = CustomHeaderMap(),
+ const QStringList& attachmentURLs = QStringList(),
+ const QStringList& attachmentMimetypes = QStringList(),
+ const QStringList& attachmentNames = QStringList(),
+ const QStringList& deletedAttachments = QStringList() );
+
+ bool kmailAddSubresource( const QString& resource, const QString& parent,
+ const QString& contentsType );
+ bool kmailRemoveSubresource( const QString& resource );
+
+ /// Get the full path of the config file.
+ QString configFile( const QString& type ) const;
+
+ /// If only one of these is writable, return that. Otherwise return null.
+ QString findWritableResource( const ResourceMap& resources,
+ const QString& text = QString::null );
+
+ bool mSilent;
+
+ /**
+ * This is used to store a mapping from the XML UID to the KMail
+ * serial number of the mail it's stored in. That provides a quick way
+ * to access the storage in KMail.
+ */
+ UidMap mUidMap;
+
+ /// This is used to distinguish operations triggered by the user,
+ /// from operations triggered by KMail
+ QStringList mUidsPendingAdding;
+ QStringList mUidsPendingDeletion;
+ QStringList mUidsPendingUpdate;
+
+private:
+ mutable KMailConnection* mConnection;
+};
+
+}
+
+#endif // RESOURCEKOLABBASE_H
diff --git a/kresources/kolab/shared/subresource.cpp b/kresources/kolab/shared/subresource.cpp
new file mode 100644
index 00000000..67216913
--- /dev/null
+++ b/kresources/kolab/shared/subresource.cpp
@@ -0,0 +1,133 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2004 Klarälvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#include "subresource.h"
+
+using namespace Kolab;
+
+SubResource::SubResource( bool active, bool writable,
+ bool alarmRelevant, const QString& label,
+ int completionWeight )
+ : mActive( active ), mWritable( writable ), mAlarmRelevant( alarmRelevant ),
+ mLabel( label ), mCompletionWeight( completionWeight )
+{
+}
+
+SubResource::SubResource( bool active, bool writable,
+ const QString& label, int completionWeight )
+ : mActive( active ), mWritable( writable ), mAlarmRelevant( false ),
+ mLabel( label ), mCompletionWeight( completionWeight )
+{
+}
+
+SubResource::~SubResource()
+{
+}
+
+void SubResource::setActive( bool active )
+{
+ mActive = active;
+}
+
+bool SubResource::active() const
+{
+ return mActive;
+}
+
+void SubResource::setAlarmRelevant( bool active )
+{
+ mAlarmRelevant = active;
+}
+
+bool SubResource::alarmRelevant() const
+{
+ return mAlarmRelevant;
+}
+
+void SubResource::setWritable( bool writable )
+{
+ mWritable = writable;
+}
+
+bool SubResource::writable() const
+{
+ return mWritable;
+}
+
+void SubResource::setLabel( const QString& label )
+{
+ mLabel = label;
+}
+
+QString SubResource::label() const
+{
+ return mLabel;
+}
+
+void SubResource::setCompletionWeight( int completionWeight )
+{
+ mCompletionWeight = completionWeight;
+}
+
+int SubResource::completionWeight() const
+{
+ return mCompletionWeight;
+}
+
+StorageReference::StorageReference( const QString& resource, Q_UINT32 sernum )
+ : mResource( resource ), mSerialNumber( sernum )
+{
+}
+
+StorageReference::~StorageReference()
+{
+}
+
+void StorageReference::setResource( const QString& resource )
+{
+ mResource = resource;
+}
+
+QString StorageReference::resource() const
+{
+ return mResource;
+}
+
+void StorageReference::setSerialNumber( Q_UINT32 serialNumber )
+{
+ mSerialNumber = serialNumber;
+}
+
+Q_UINT32 StorageReference::serialNumber() const
+{
+ return mSerialNumber;
+}
diff --git a/kresources/kolab/shared/subresource.h b/kresources/kolab/shared/subresource.h
new file mode 100644
index 00000000..1bdd98cd
--- /dev/null
+++ b/kresources/kolab/shared/subresource.h
@@ -0,0 +1,117 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2004 Klarälvdalens Datakonsult AB
+ <info@klaralvdalens-datakonsult.se>
+
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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.
+*/
+
+#ifndef SUBRESOURCE_H
+#define SUBRESOURCE_H
+
+#include <qstring.h>
+#include <qmap.h>
+
+
+namespace Kolab {
+
+/**
+ * This class is used to store in a map from resource id to this, providing
+ * a lookup of the subresource settings.
+ */
+class SubResource {
+public:
+ // This is just for QMap
+ SubResource() {}
+
+ SubResource( bool active, bool writable, const QString& label,
+ int completionWeight = 100 );
+
+ SubResource( bool active, bool writable, bool alarmRelevant,
+ const QString& label, int completionWeight = 100 );
+ virtual ~SubResource();
+
+ virtual void setActive( bool active );
+ virtual bool active() const;
+
+ virtual void setWritable( bool writable );
+ virtual bool writable() const;
+
+ virtual void setAlarmRelevant( bool active );
+ virtual bool alarmRelevant() const;
+
+ virtual void setLabel( const QString& label );
+ virtual QString label() const;
+
+ virtual void setCompletionWeight( int completionWeight );
+ virtual int completionWeight() const;
+
+private:
+ bool mActive; // Controlled by the applications
+ bool mWritable; // Set if the KMail folder is writable
+ bool mAlarmRelevant; // Set if the alarms from this resource are of
+ // interest to the user, as per folder acls
+ QString mLabel; // The GUI name of this resource
+
+ // This is just for the abc plugin. But as long as only this is here,
+ // it's just as cheap to have it in here as making a d-pointer that
+ // subclasses could add to. If more are added, then we should refactor
+ // to a d-pointer instead.
+ int mCompletionWeight;
+};
+
+typedef QMap<QString, SubResource> ResourceMap;
+
+/**
+ * This class is used to store a mapping from the XML UID to the KMail
+ * serial number of the mail it's stored in and the resource. That provides
+ * a quick way to access the storage in KMail.
+ */
+class StorageReference {
+public:
+ // Just for QMap
+ StorageReference() {}
+
+ StorageReference( const QString& resource, Q_UINT32 sernum );
+ virtual ~StorageReference();
+
+ virtual void setResource( const QString& resource );
+ virtual QString resource() const;
+
+ virtual void setSerialNumber( Q_UINT32 serialNumber );
+ virtual Q_UINT32 serialNumber() const;
+
+private:
+ QString mResource;
+ Q_UINT32 mSerialNumber;
+};
+
+typedef QMap<QString, StorageReference> UidMap;
+
+}
+
+#endif // SUBRESOURCE_H
diff --git a/kresources/kolab/uninstall.desktop b/kresources/kolab/uninstall.desktop
new file mode 100644
index 00000000..e1e3e173
--- /dev/null
+++ b/kresources/kolab/uninstall.desktop
@@ -0,0 +1,2 @@
+[Desktop Entry]
+Hidden=true
diff --git a/kresources/kolab/upgrade-resourcetype.pl b/kresources/kolab/upgrade-resourcetype.pl
new file mode 100644
index 00000000..98337a88
--- /dev/null
+++ b/kresources/kolab/upgrade-resourcetype.pl
@@ -0,0 +1,24 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+# This script updates some configuration keys
+
+# read the whole config file
+my $currentGroup = "";
+my %configFile;
+while ( <> ) {
+ chomp; # eat the trailing '\n'
+ next if ( /^$/ ); # skip empty lines
+ next if ( /^\#/ ); # skip comments
+ if ( /^\[(.+)\]$/ ) { # group begin
+ $currentGroup = $1;
+ next;
+ } elsif ( $currentGroup =~ /^Resource/ ) {
+ my ($key,$value) = split /=/;
+ if ( $key eq "ResourceType" and $value eq "kolab" ) {
+ print "# DELETE [$currentGroup]$key\n";
+ print "[$currentGroup]\nResourceType=imap\n";
+ }
+ }
+}