diff options
Diffstat (limited to 'kresources/kolab/kabc')
-rw-r--r-- | kresources/kolab/kabc/Makefile.am | 27 | ||||
-rw-r--r-- | kresources/kolab/kabc/contact.cpp | 1314 | ||||
-rw-r--r-- | kresources/kolab/kabc/contact.h | 287 | ||||
-rw-r--r-- | kresources/kolab/kabc/kolab.desktop | 53 | ||||
-rw-r--r-- | kresources/kolab/kabc/resourcekolab.cpp | 670 | ||||
-rw-r--r-- | kresources/kolab/kabc/resourcekolab.h | 175 | ||||
-rw-r--r-- | kresources/kolab/kabc/resourcekolab_plugin.cpp | 52 |
7 files changed, 2578 insertions, 0 deletions
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) + |