diff options
Diffstat (limited to 'kresources/kolab')
40 files changed, 9068 insertions, 0 deletions
diff --git a/kresources/kolab/Makefile.am b/kresources/kolab/Makefile.am new file mode 100644 index 000000000..0f144e05c --- /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 000000000..2a906a45e --- /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 000000000..c5e819b3f --- /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 000000000..0c06dfb8a --- /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 000000000..d75090a7a --- /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 000000000..464c9c610 --- /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 000000000..e01922681 --- /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 000000000..be87772b3 --- /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 000000000..bc0af8ddf --- /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 000000000..a53ca4162 --- /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 000000000..ad33db520 --- /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 000000000..8c74e3bdf --- /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 000000000..de549328e --- /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 000000000..45b91587d --- /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 000000000..d8b09bb96 --- /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 000000000..579e6406d --- /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 000000000..3c1c80e10 --- /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 000000000..0974e4ada --- /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 000000000..afc83e1d6 --- /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 000000000..b66c9827b --- /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 000000000..96bcafd96 --- /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 000000000..de4ff1dd8 --- /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 000000000..d74e3f59b --- /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 000000000..76ecdd9db --- /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 000000000..45769f97b --- /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 000000000..b5cad153a --- /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 000000000..44ce480b7 --- /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 000000000..cd97ecf6c --- /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 000000000..d4c9b9629 --- /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 000000000..f75af8817 --- /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 000000000..6db9f3dab --- /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 000000000..cbb69a155 --- /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 000000000..44c44ead2 --- /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 000000000..053683989 --- /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 000000000..d62a5bb54 --- /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 000000000..806220189 --- /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 000000000..67216913a --- /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 000000000..1bdd98cd5 --- /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 000000000..e1e3e1732 --- /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 000000000..98337a88d --- /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"; + } + } +} |