summaryrefslogtreecommitdiffstats
path: root/kresources/kolab/kabc
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch)
tree67208f7c145782a7e90b123b982ca78d88cc2c87 /kresources/kolab/kabc
downloadtdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz
tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kresources/kolab/kabc')
-rw-r--r--kresources/kolab/kabc/Makefile.am27
-rw-r--r--kresources/kolab/kabc/contact.cpp1314
-rw-r--r--kresources/kolab/kabc/contact.h287
-rw-r--r--kresources/kolab/kabc/kolab.desktop53
-rw-r--r--kresources/kolab/kabc/resourcekolab.cpp670
-rw-r--r--kresources/kolab/kabc/resourcekolab.h175
-rw-r--r--kresources/kolab/kabc/resourcekolab_plugin.cpp52
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)
+