summaryrefslogtreecommitdiffstats
path: root/libkpimexchange
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 /libkpimexchange
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 'libkpimexchange')
-rw-r--r--libkpimexchange/Makefile.am21
-rw-r--r--libkpimexchange/configure.in.in35
-rw-r--r--libkpimexchange/core/Makefile.am27
-rw-r--r--libkpimexchange/core/README.download63
-rw-r--r--libkpimexchange/core/exchangeaccount.cpp339
-rw-r--r--libkpimexchange/core/exchangeaccount.h99
-rw-r--r--libkpimexchange/core/exchangeclient.cpp292
-rw-r--r--libkpimexchange/core/exchangeclient.h134
-rw-r--r--libkpimexchange/core/exchangedelete.cpp121
-rw-r--r--libkpimexchange/core/exchangedelete.h59
-rw-r--r--libkpimexchange/core/exchangedownload.cpp643
-rw-r--r--libkpimexchange/core/exchangedownload.h89
-rw-r--r--libkpimexchange/core/exchangemonitor.cpp386
-rw-r--r--libkpimexchange/core/exchangemonitor.h106
-rw-r--r--libkpimexchange/core/exchangeprogress.cpp72
-rw-r--r--libkpimexchange/core/exchangeprogress.h54
-rw-r--r--libkpimexchange/core/exchangeupload.cpp371
-rw-r--r--libkpimexchange/core/exchangeupload.h66
-rw-r--r--libkpimexchange/core/utils.cpp100
-rw-r--r--libkpimexchange/core/utils.h56
20 files changed, 3133 insertions, 0 deletions
diff --git a/libkpimexchange/Makefile.am b/libkpimexchange/Makefile.am
new file mode 100644
index 000000000..01224c05d
--- /dev/null
+++ b/libkpimexchange/Makefile.am
@@ -0,0 +1,21 @@
+# $Id$
+
+SUBDIRS=core
+
+METASOURCES = AUTO
+
+INCLUDES = $(all_includes)
+
+lib_LTLIBRARIES = libkpimexchange.la
+
+libkpimexchange_la_SOURCES = dummy.cpp
+libkpimexchange_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 1:0:0
+libkpimexchange_la_LIBADD = core/libkpimexchangecore.la \
+ $(LIB_KIO) $(top_builddir)/libkcal/libkcal.la
+
+dummy.cpp:
+ echo >dummy.cpp
+
+messages:
+ $(XGETTEXT) core/*.cpp -o $(podir)/libkpimexchange.pot
+
diff --git a/libkpimexchange/configure.in.in b/libkpimexchange/configure.in.in
new file mode 100644
index 000000000..4c6a71603
--- /dev/null
+++ b/libkpimexchange/configure.in.in
@@ -0,0 +1,35 @@
+dnl build with exchange support
+
+AC_MSG_CHECKING([if kdelibs has recent webdav])
+AC_LANG_SAVE
+kde_safe_cppflags=$CPPFLAGS
+CPPFLAGS="$CPPFLAGS $all_includes"
+AC_LANG_CPLUSPLUS
+AC_TRY_COMPILE(
+[#include <kio/http.h>],
+[
+ int method = (int) KIO::DAV_SUBSCRIBE;
+],
+[
+ ac_has_recent_webdav="yes"
+ AC_MSG_RESULT([yes])
+],
+[
+ ac_has_recent_webdav="no"
+ AC_MSG_RESULT([no])
+])
+CPPFLAGS=$kde_safe_cppflags
+AC_LANG_RESTORE
+
+ac_use_exchange="no"
+AC_ARG_ENABLE(exchange,
+ [ --disable-exchange disable Microsoft Exchange 2000 support for korganizer [default=enable]], ac_use_exchange=$enableval, ac_use_exchange=yes)
+AM_CONDITIONAL(include_exchange_SUBDIR, test "x$ac_use_exchange" = "xyes" && test "x$ac_has_recent_webdav" = "xyes")
+
+if test "x$ac_use_exchange" != "xyes" || test "x$ac_has_recent_webdav" != "xyes"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE libkpimexchange"
+ AC_MSG_WARN([Exchange 2000 support disabled])
+else
+ AC_DEFINE(KDEPIM_ENABLE_EXCHANGE, 1, [Define if you want Microsoft Exchange 2000 support])
+fi
+
diff --git a/libkpimexchange/core/Makefile.am b/libkpimexchange/core/Makefile.am
new file mode 100644
index 000000000..cc19fd79c
--- /dev/null
+++ b/libkpimexchange/core/Makefile.am
@@ -0,0 +1,27 @@
+# $Id$
+
+SUBDIRS=
+
+METASOURCES = AUTO
+
+INCLUDES = -I$(top_srcdir)/korganizer/interfaces -I$(top_srcdir) \
+ -I$(top_srcdir)/libkcal/libical/src/libical -I$(top_srcdir)/libkcal/libical/src/libicalss \
+ -I$(top_builddir)/libkcal/libical/src/libical -I$(top_builddir)/libkcal/libical/src/libicalss \
+ $(all_includes)
+
+# -I$(top_builddir)/libkdepim/resources -I$(top_builddir)/libkdepim/resources/calendar
+
+noinst_LTLIBRARIES = libkpimexchangecore.la
+
+libkpimexchangecore_la_SOURCES = exchangeclient.cpp exchangeaccount.cpp \
+ exchangedownload.cpp exchangeupload.cpp exchangedelete.cpp \
+ utils.cpp exchangeprogress.cpp exchangemonitor.cpp
+
+#libkpimexchange_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 1:0:0 -module
+#libkpimexchange_la_LIBADD = $(LIB_KIO) $(top_builddir)/libkcal/libkcal.la
+
+kdepimincludedir = $(includedir)/kdepim
+kdepiminclude_HEADERS = exchangeclient.h exchangeaccount.h
+
+noinst_HEADERS = exchangedownload.h exchangeupload.h exchangedelete.h exchangeprogress.h utils.h
+
diff --git a/libkpimexchange/core/README.download b/libkpimexchange/core/README.download
new file mode 100644
index 000000000..07540db85
--- /dev/null
+++ b/libkpimexchange/core/README.download
@@ -0,0 +1,63 @@
+$Id$
+This document describes what happens during the download of
+appointments from an exchange server in exchangedownload.cpp.
+Error handling, user interface ignored for clarity
+
+Author: Jan-Pascal van Best, janpascal@vanbest.org
+
+NOTES:
+- You can only use an ExchangeDownload object for a single download
+ It uses internal state member variables and such.
+
+DATA STRUCTURES:
+QMap<QString,int> m_uids is in fact a set of known uids telling us
+whether we're already busy or finished reading the Master event
+for this UID. The map contains the UID as key, with a value of 1,
+if UID is either being or finished downloading.
+
+QMap<QString,DwString *> m_transferJobs maps URLs being downloaded
+to strings of data already received. A URL is removed from the map
+if all data has been received
+
+METHODS:
+download()
+- Provides authentication info to the KDE authentication service
+- Creates an SQL query using dateSelectQuery()
+- Starts a SEARCH job, connects the result() signal
+ to the slotSearchResult() slot
+
+slotSearchResult()
+- Calls handleAppointments() with recurrence enabled
+
+handleAppointments()
+- Examines all events returned by the SEARCH
+- If recurrence is enabled, for Master, Instance or Exception events,
+ and if we havent't handled this particular UID yet, call
+ handleRecurrence() with the UID of the event
+- If recurrence is disabled, or for Single events, start a TransferJob.
+ Connect the data() signal to the slotData() slot and the result()
+ signal to the slotTransferResult() slot.
+- Note that this method may start many new jobs for transferring
+ appointments and for finding recurrent events!
+
+handleRecurrence()
+- Start a new SEARCH job, looking for the Master event of the UID
+- Connect the result() signal to the slotMasterResult() slot
+
+slotMasterResult()
+- Call handleAppointment() with recurrence disabled
+
+slotData()
+- If the URL of the data we're receiving is already in m_transferJobs,
+ append the data to the string related to this URL. Else, create a new
+ string, and place a new URL,string pair in m_transferJobs
+
+slotTransferResult()
+- Parse the data received for this URL as a MIME message
+- call handlePart() for every MIME part in the message
+- Remove the URL from m_transferJobs and free the string
+
+handlePart()
+- If this is a text/calendar part, read iCalendar data from the part and
+ insert it into the calendar.
+
diff --git a/libkpimexchange/core/exchangeaccount.cpp b/libkpimexchange/core/exchangeaccount.cpp
new file mode 100644
index 000000000..9142c9db3
--- /dev/null
+++ b/libkpimexchange/core/exchangeaccount.cpp
@@ -0,0 +1,339 @@
+/*
+ This file is part of libkpimexchange
+
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+ Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qstring.h>
+#include <qtextstream.h>
+#include <qapplication.h>
+#include <qdom.h>
+#include <qwidgetlist.h>
+#include <qwidget.h>
+#include <qfile.h>
+
+#include <kurl.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <dcopclient.h>
+#include <kcursor.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+#include <kio/authinfo.h>
+#include <kio/davjob.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+
+#include "exchangeaccount.h"
+#include "utils.h"
+
+using namespace KPIM;
+
+ExchangeAccount::ExchangeAccount( const QString &host, const QString &port,
+ const QString &account,
+ const QString &password,
+ const QString &mailbox )
+ : mError( false )
+{
+ KURL url( "webdav://" + host + "/exchange/" + account );
+
+ if ( !port.isEmpty() )
+ {
+ url.setPort( port.toInt() );
+ }
+
+ mHost = host;
+ mPort = port;
+ mAccount = account;
+ mPassword = password;
+
+ if ( mailbox.isEmpty() ) {
+ mMailbox = url.url();
+ kdDebug() << "#!#!#!#!#!#!# mailbox url: " << mMailbox << endl;
+ } else
+ mMailbox = mailbox;
+
+ kdDebug() << "ExchangeAccount: mMailbox: " << mMailbox << endl;
+
+ mCalendarURL = 0;
+}
+
+ExchangeAccount::ExchangeAccount( const QString& group )
+{
+ load( group );
+}
+
+ExchangeAccount::~ExchangeAccount()
+{
+}
+
+QString endecryptStr( const QString &aStr )
+{
+ QString result;
+ for (uint i = 0; i < aStr.length(); i++)
+ result += (aStr[i].unicode() < 0x20) ? aStr[i] :
+ QChar(0x1001F - aStr[i].unicode());
+ return result;
+}
+
+void ExchangeAccount::save( QString const &group )
+{
+ kapp->config()->setGroup( group );
+ kapp->config()->writeEntry( "host", mHost );
+ kapp->config()->writeEntry( "user", mAccount );
+ kapp->config()->writeEntry( "mailbox", mMailbox );
+ kapp->config()->writeEntry( "MS-ID", endecryptStr( mPassword ) );
+ kapp->config()->sync();
+}
+
+void ExchangeAccount::load( QString const &group )
+{
+ kapp->config()->setGroup( group );
+
+ QString host = kapp->config()->readEntry( "host" );
+ if ( ! host.isNull() ) {
+ mHost = host;
+ } else {
+ mHost = "mail.company.com";
+ }
+
+ QString user = kapp->config()->readEntry( "user" );
+ if ( ! user.isNull() ) {
+ mAccount = user;
+ } else {
+ mAccount = "username";
+ }
+
+ QString mailbox = kapp->config()->readEntry( "mailbox" );
+ if ( ! mailbox.isNull() ) {
+ mMailbox = mailbox;
+ } else {
+ mMailbox = "webdav://" + host + "/exchange/" + mAccount;
+ }
+
+ QString password = endecryptStr( kapp->config()->readEntry( "MS-ID" ) );
+ if ( ! password.isNull() ) {
+ mPassword = password;
+ }
+}
+
+KURL ExchangeAccount::baseURL()
+{
+ KURL url = KURL( mMailbox );
+ return url;
+}
+
+KURL ExchangeAccount::calendarURL()
+{
+ if ( mCalendarURL ) {
+ return *mCalendarURL;
+ } else {
+ KURL url = baseURL();
+ url.addPath( "Calendar" );
+ return url;
+ }
+}
+
+bool ExchangeAccount::authenticate( QWidget *window )
+{
+ if ( window )
+ return authenticate( window->winId() );
+ else
+ return authenticate();
+}
+
+bool ExchangeAccount::authenticate()
+{
+ long windowId;
+ QWidgetList *widgets = QApplication::topLevelWidgets();
+ if ( widgets->isEmpty() )
+ windowId = 0;
+ else
+ windowId = widgets->first()->winId();
+ delete widgets;
+
+ return authenticate( windowId );
+}
+
+bool ExchangeAccount::authenticate( int windowId )
+{
+ kdDebug() << "Entering ExchangeAccount::authenticate( windowId=" << windowId << " )" << endl;
+
+ kdDebug() << "Authenticating to base URL: " << baseURL().prettyURL() << endl;
+
+ KIO::AuthInfo info;
+ info.url = baseURL();
+ info.username = mAccount;
+ info.password = mPassword;
+ info.realmValue = mHost;
+ info.digestInfo = "Basic";
+
+ DCOPClient *dcopClient = new DCOPClient();
+ dcopClient->attach();
+
+ QByteArray params;
+ QDataStream stream(params, IO_WriteOnly);
+ stream << info << windowId;
+
+ dcopClient->send( "kded", "kpasswdserver",
+ "addAuthInfo(KIO::AuthInfo, long int)", params );
+
+ dcopClient->detach();
+ delete dcopClient;
+
+ mCalendarURL = 0;
+
+ calcFolderURLs();
+
+ // TODO: Remove this busy loop
+ QApplication::setOverrideCursor( KCursor::waitCursor() );
+ do {
+ qApp->processEvents();
+ } while ( !mCalendarURL && !mError );
+ QApplication::restoreOverrideCursor();
+
+ return !mError;
+}
+
+void ExchangeAccount::calcFolderURLs()
+{
+ kdDebug() << "ExchangeAccount::calcFolderURLs" << endl;
+ QDomDocument doc;
+ QDomElement root = addElement( doc, doc, "DAV:", "propfind" );
+ QDomElement prop = addElement( doc, root, "DAV:", "prop" );
+ addElement( doc, prop, "urn:schemas:httpmail:", "calendar" );
+// For later use:
+// urn:schemas:httpmail:contacts Contacts
+// urn:schemas:httpmail:deleteditems Deleted Items
+// urn:schemas:httpmail:drafts Drafts
+// urn:schemas:httpmail:inbox Inbox
+// urn:schemas:httpmail:journal Journal
+// urn:schemas:httpmail:notes Notes
+// urn:schemas:httpmail:outbox Outbox
+// urn:schemas:httpmail:sentitems Sent Items
+// urn:schemas:httpmail:tasks Tasks
+// urn:schemas:httpmail:sendmsg Exchange Mail Submission URI
+// urn:schemas:httpmail:msgfolderroot Mailbox folder (root)
+
+ kdDebug() << "calcFolderUrls(): " << baseURL() << endl;
+
+ mError = false;
+
+ KIO::DavJob* job = KIO::davPropFind( baseURL(), doc, "1", false );
+ job->addMetaData( "errorPage", "false" );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotFolderResult( KIO::Job * ) ) );
+}
+
+void ExchangeAccount::slotFolderResult( KIO::Job *job )
+{
+ kdDebug() << "ExchangeAccount::slotFolderResult()" << endl;
+ if ( job->error() ) {
+ kdError() << "Error: Cannot get well-know folder names; " << job->error() << endl;
+ QString text = i18n("ExchangeAccount\nError accessing '%1': %2")
+ .arg( baseURL().prettyURL() ).arg( job->errorString() );
+ KMessageBox::error( 0, text );
+ mError = true;
+ return;
+ }
+ QDomDocument &response = static_cast<KIO::DavJob *>( job )->response();
+
+ QDomElement prop = response.documentElement().namedItem( "response" )
+ .namedItem( "propstat" ).namedItem( "prop" ).toElement();
+
+ QDomElement calElement = prop.namedItem( "calendar" ).toElement();
+ if ( calElement.isNull() ) {
+ kdError() << "Error: no calendar URL in Exchange server reply" << endl;
+ mError = true;
+ return;
+ }
+ QString calendar = calElement.text();
+
+ kdDebug() << "ExchangeAccount: response calendarURL: " << calendar << endl;
+
+ mCalendarURL = toDAV( new KURL( calendar ) );
+ kdDebug() << "Calendar URL: " << mCalendarURL->url() << endl;
+}
+
+QString ExchangeAccount::tryFindMailbox( const QString& host, const QString& port, const QString& user, const QString& password )
+{
+ kdDebug() << "Entering ExchangeAccount::tryFindMailbox()" << endl;
+
+ KURL url("http://" + host + "/exchange");
+ if (!port.isEmpty()) url.setPort(port.toInt());
+
+ QString result = tryMailbox( url.url(), user, password );
+ if ( result.isNull() )
+ {
+ url.setProtocol("https");
+ result = tryMailbox( url.url(), user, password );
+ }
+ return result;
+}
+
+QString ExchangeAccount::tryMailbox( const QString &_url, const QString &user,
+ const QString &password )
+{
+ KURL url = KURL( _url );
+ url.setUser( user );
+ url.setPass( password );
+
+ QString tmpFile;
+ if ( !KIO::NetAccess::download( url, tmpFile, 0 ) ) {
+ kdWarning() << "Trying to find mailbox failed: not able to download " << url.prettyURL() << endl;
+ return QString::null;
+ }
+ QFile file( tmpFile );
+ if ( !file.open( IO_ReadOnly ) ) {
+ kdWarning() << "Trying to find mailbox failed: not able to open temp file " << tmpFile << endl;
+ KIO::NetAccess::removeTempFile( tmpFile );
+ return QString::null;
+ }
+
+ QTextStream stream( &file );
+ QString line;
+ QString result;
+ while ( !stream.eof() ) {
+ line = stream.readLine(); // line of text excluding '\n'
+ int pos = line.find( "<BASE href=\"", 0, FALSE );
+ if ( pos < 0 )
+ continue;
+ int end = line.find( "\"", pos+12, FALSE );
+ if ( pos < 0 ) {
+ kdWarning() << "Strange, found no closing quote in " << line << endl;
+ continue;
+ }
+ QString mailboxString = line.mid( pos+12, end-pos-12 );
+ KURL mailbox( mailboxString );
+ if ( mailbox.isEmpty() ) {
+ kdWarning() << "Strange, could not get URL from " << mailboxString << " in line " << line << endl;
+ continue;
+ }
+ result = toDAV( mailbox ).prettyURL( -1 ); // Strip ending slash from URL, if present
+ kdDebug() << "Found mailbox: " << result << endl;
+ }
+ file.close();
+
+ KIO::NetAccess::removeTempFile( tmpFile );
+ return result;
+}
+
+#include "exchangeaccount.moc"
diff --git a/libkpimexchange/core/exchangeaccount.h b/libkpimexchange/core/exchangeaccount.h
new file mode 100644
index 000000000..505232c88
--- /dev/null
+++ b/libkpimexchange/core/exchangeaccount.h
@@ -0,0 +1,99 @@
+/*
+ This file is part of libkpimexchange.
+
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+ Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+#ifndef EXCHANGE_ACCOUNT_H
+#define EXCHANGE_ACCOUNT_H
+
+#include <qobject.h>
+#include <qstring.h>
+
+#include <kdepimmacros.h>
+#include <kurl.h>
+#include <kio/job.h>
+
+namespace KPIM {
+
+class KDE_EXPORT ExchangeAccount : public QObject
+{
+ Q_OBJECT
+ public:
+ ExchangeAccount( const QString &host, const QString &port,
+ const QString &account, const QString &password,
+ const QString &mailbox = QString::null );
+ /**
+ Create a new account object, read data from group app data
+ */
+ ExchangeAccount( const QString &group );
+ ~ExchangeAccount();
+
+ void save( QString const &group );
+ void load( QString const &group );
+
+ QString host() { return mHost; }
+ QString port() { return mPort; }
+ QString account() { return mAccount; }
+ QString mailbox() { return mMailbox; }
+ QString password() { return mPassword; }
+
+ void setHost( QString host ) { mHost = host; }
+ void setPort( QString port ) { mPort = port; }
+ void setAccount( QString account ) { mAccount = account; }
+ void setMailbox( QString mailbox ) { mMailbox = mailbox; }
+ void setPassword( QString password ) { mPassword = password; }
+
+ KURL baseURL();
+ KURL calendarURL();
+
+ // Returns the mailbox URL of this user. QString::null if unsuccessful
+ static QString tryFindMailbox( const QString &host, const QString &port,
+ const QString &user,
+ const QString &password );
+
+ // Put authentication info in KDE password store for auto-authentication
+ // with later webdav access. Also calculates the calendar URL.
+ bool authenticate();
+ bool authenticate( QWidget *window );
+
+ private:
+ bool authenticate( int windowId );
+ void calcFolderURLs();
+ static QString tryMailbox( const QString &_url, const QString &user,
+ const QString &password );
+
+ private slots:
+ void slotFolderResult( KIO::Job * );
+
+ private:
+ QString mHost;
+ QString mPort;
+ QString mAccount;
+ QString mMailbox;
+ QString mPassword;
+
+ KURL *mCalendarURL;
+ bool mError;
+};
+
+}
+
+#endif
+
diff --git a/libkpimexchange/core/exchangeclient.cpp b/libkpimexchange/core/exchangeclient.cpp
new file mode 100644
index 000000000..337e540fd
--- /dev/null
+++ b/libkpimexchange/core/exchangeclient.cpp
@@ -0,0 +1,292 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kapplication.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <kcursor.h>
+#include <klocale.h>
+
+// These for test() method
+#include <kio/http.h>
+#include <kio/davjob.h>
+// #include "libkdepim/resources/resourcemanager.h"
+// #include "libkdepim/resources/calendar/resourcecalendar.h"
+
+
+#include "exchangeclient.h"
+#include "exchangeaccount.h"
+#include "exchangeprogress.h"
+#include "exchangeupload.h"
+#include "exchangedownload.h"
+#include "exchangedelete.h"
+//#include "exchangemonitor.h"
+#include "utils.h"
+
+using namespace KPIM;
+
+ExchangeClient::ExchangeClient( ExchangeAccount *account,
+ const QString &timeZoneId )
+ : mWindow( 0 ), mTimeZoneId( timeZoneId )
+{
+ kdDebug() << "Creating ExchangeClient...\n";
+ mAccount = account;
+ if ( timeZoneId.isNull() ) {
+ setTimeZoneId( "UTC" );
+ }
+}
+
+ExchangeClient::~ExchangeClient()
+{
+ kdDebug() << "ExchangeClient destructor" << endl;
+}
+
+void ExchangeClient::setWindow(QWidget *window)
+{
+ mWindow = window;
+}
+
+QWidget *ExchangeClient::window() const
+{
+ return mWindow;
+}
+
+void ExchangeClient::setTimeZoneId( const QString& timeZoneId )
+{
+ mTimeZoneId = timeZoneId;
+}
+
+QString ExchangeClient::timeZoneId()
+{
+ return mTimeZoneId;
+}
+
+void ExchangeClient::test()
+{
+// if ( !mAccount->authenticate( mWindow ) ) return;
+ kdDebug() << "Entering test()" << endl;
+ KURL baseURL = KURL( "http://mail.tbm.tudelft.nl/janb/Calendar" );
+ KURL url( "webdav://mail.tbm.tudelft.nl/exchange/" );
+
+/*
+ KRES::Manager<KCal::ResourceCalendar>* manager = new KRES::Manager<KCal::ResourceCalendar>( "calendar" );
+ KCal::ResourceCalendar* resource = manager->standardResource();
+
+ kdDebug(5800) << "Opening resource " + resource->resourceName() << endl;
+ bool result = resource->open();
+ kdDebug() << "Result: " << result << endl;
+
+ resource->subscribeEvents( QDate( 2002, 12, 18 ), QDate( 2002, 12, 19 ) );
+*/
+// mAccount->tryFindMailbox();
+/*
+ QString query =
+ "<propfind xmlns=\"DAV:\" xmlns:h=\"urn:schemas:httpmail:\">\r\n"
+ " <allprop/>\r\n"
+ "</propfind>\r\n";
+
+ KIO::DavJob* job = new KIO::DavJob( url, (int) KIO::DAV_PROPFIND, query, false );
+ job->addMetaData( "davDepth", "0" );
+*/
+// ExchangeMonitor* monitor = new ExchangeMonitor( mAccount );
+}
+
+void ExchangeClient::test2()
+{
+ kdDebug() << "Entering test2()" << endl;
+}
+/*
+ExchangeMonitor* ExchangeClient::monitor( int pollMode, const QHostAddress& ownInterface )
+{
+ return new ExchangeMonitor( mAccount, pollMode, ownInterface );
+}
+*/
+void ExchangeClient::download( KCal::Calendar *calendar, const QDate &start,
+ const QDate &end, bool showProgress )
+{
+ kdDebug() << "ExchangeClient::download1()" << endl;
+
+ if ( !mAccount->authenticate( mWindow ) ) {
+ emit downloadFinished( 0, i18n("Authentication error") );
+ return;
+ }
+
+ ExchangeDownload *worker = new ExchangeDownload( mAccount, mWindow );
+ worker->download( calendar, start, end, showProgress );
+ connect( worker,
+ SIGNAL( finished( ExchangeDownload *, int, const QString & ) ),
+ SLOT( slotDownloadFinished( ExchangeDownload *, int,
+ const QString & ) ) );
+}
+
+void ExchangeClient::download( const QDate &start, const QDate &end,
+ bool showProgress )
+{
+ kdDebug() << "ExchangeClient::download2()" << endl;
+
+ if ( !mAccount->authenticate( mWindow ) ) {
+ emit downloadFinished( 0, i18n("Authentication error") );
+ return;
+ }
+
+ ExchangeDownload *worker = new ExchangeDownload( mAccount, mWindow );
+ worker->download( start, end, showProgress );
+ connect( worker,
+ SIGNAL( finished( ExchangeDownload *, int, const QString & ) ),
+ SLOT( slotDownloadFinished( ExchangeDownload *, int,
+ const QString & ) ) );
+ connect( worker, SIGNAL( gotEvent( KCal::Event *, const KURL & ) ),
+ SIGNAL( event( KCal::Event *, const KURL & ) ) );
+}
+
+void ExchangeClient::upload( KCal::Event *event )
+{
+ kdDebug() << "ExchangeClient::upload()" << endl;
+
+ if ( !mAccount->authenticate( mWindow ) ) {
+ emit uploadFinished( 0, i18n("Authentication error") );
+ return;
+ }
+
+ ExchangeUpload *worker = new ExchangeUpload( event, mAccount, mTimeZoneId,
+ mWindow );
+ connect( worker, SIGNAL( finished( ExchangeUpload *, int, const QString & ) ),
+ SLOT( slotUploadFinished( ExchangeUpload *, int, const QString & ) ) );
+}
+
+void ExchangeClient::remove( KCal::Event *event )
+{
+ if ( !mAccount->authenticate( mWindow ) ) {
+ emit removeFinished( 0, i18n("Authentication error") );
+ return;
+ }
+
+ ExchangeDelete *worker = new ExchangeDelete( event, mAccount, mWindow );
+ connect( worker, SIGNAL( finished( ExchangeDelete *, int, const QString & ) ),
+ SLOT( slotRemoveFinished( ExchangeDelete *, int, const QString & ) ) );
+}
+
+void ExchangeClient::slotDownloadFinished( ExchangeDownload *worker,
+ int result, const QString &moreInfo )
+{
+ emit downloadFinished( result, moreInfo );
+ worker->deleteLater();
+}
+
+void ExchangeClient::slotDownloadFinished( ExchangeDownload* worker, int result, const QString& moreInfo, QPtrList<KCal::Event>& events )
+{
+ emit downloadFinished( result, moreInfo, events );
+ worker->deleteLater();
+}
+
+void ExchangeClient::slotUploadFinished( ExchangeUpload* worker, int result, const QString& moreInfo )
+{
+ kdDebug() << "ExchangeClient::slotUploadFinished()" << endl;
+ emit uploadFinished( result, moreInfo );
+ worker->deleteLater();
+}
+
+void ExchangeClient::slotRemoveFinished( ExchangeDelete* worker, int result, const QString& moreInfo )
+{
+ kdDebug() << "ExchangeClient::slotRemoveFinished()" << endl;
+ emit removeFinished( result, moreInfo );
+ worker->deleteLater();
+}
+
+int ExchangeClient::downloadSynchronous( KCal::Calendar *calendar,
+ const QDate &start, const QDate &end,
+ bool showProgress )
+{
+ kdDebug() << "ExchangeClient::downloadSynchronous()" << endl;
+
+ mClientState = WaitingForResult;
+ connect( this, SIGNAL( downloadFinished( int, const QString & ) ),
+ SLOT( slotSyncFinished( int, const QString & ) ) );
+
+ download( calendar, start, end, showProgress );
+
+ // TODO: Remove this busy loop
+ QApplication::setOverrideCursor
+ ( KCursor::waitCursor() );
+ do {
+ qApp->processEvents();
+ } while ( mClientState == WaitingForResult );
+ QApplication::restoreOverrideCursor();
+
+ disconnect( this, SIGNAL( downloadFinished( int, const QString & ) ),
+ this, SLOT( slotSyncFinished( int, const QString & ) ) );
+
+ return mSyncResult;
+}
+
+int ExchangeClient::uploadSynchronous( KCal::Event* event )
+{
+ mClientState = WaitingForResult;
+ connect( this, SIGNAL( uploadFinished( int, const QString & ) ),
+ SLOT( slotSyncFinished( int, const QString & ) ) );
+
+ upload( event );
+
+ // TODO: Remove this busy loop
+ QApplication::setOverrideCursor( KCursor::waitCursor() );
+ do {
+ qApp->processEvents();
+ } while ( mClientState == WaitingForResult );
+ QApplication::restoreOverrideCursor();
+ disconnect( this, SIGNAL( uploadFinished( int, const QString & ) ),
+ this, SLOT( slotSyncFinished( int, const QString & ) ) );
+ return mSyncResult;
+}
+
+int ExchangeClient::removeSynchronous( KCal::Event* event )
+{
+ mClientState = WaitingForResult;
+ connect( this, SIGNAL( removeFinished( int, const QString & ) ),
+ SLOT( slotSyncFinished( int, const QString & ) ) );
+
+ remove( event );
+
+ // TODO: Remove this busy loop
+ QApplication::setOverrideCursor( KCursor::waitCursor() );
+ do {
+ qApp->processEvents();
+ } while ( mClientState == WaitingForResult );
+ QApplication::restoreOverrideCursor();
+ disconnect( this, SIGNAL( removeFinished( int, const QString & ) ),
+ this, SLOT( slotSyncFinished( int, const QString & ) ) );
+ return mSyncResult;
+}
+
+void ExchangeClient::slotSyncFinished( int result, const QString &moreInfo )
+{
+ kdDebug() << "Exchangeclient::slotSyncFinished("<<result<<","<<moreInfo<<")" << endl;
+ if ( mClientState == WaitingForResult ) {
+ mClientState = HaveResult;
+ mSyncResult = result;
+ mDetailedErrorString = moreInfo;
+ }
+}
+
+QString ExchangeClient::detailedErrorString()
+{
+ return mDetailedErrorString;
+}
+
+#include "exchangeclient.moc"
diff --git a/libkpimexchange/core/exchangeclient.h b/libkpimexchange/core/exchangeclient.h
new file mode 100644
index 000000000..b887dac50
--- /dev/null
+++ b/libkpimexchange/core/exchangeclient.h
@@ -0,0 +1,134 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef KDEPIM_EXCHANGE_CLIENT_H
+#define KDEPIM_EXCHANGE_CLIENT_H
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qobject.h>
+#include <qhostaddress.h>
+#include <qptrlist.h>
+
+#include <kdepimmacros.h>
+
+namespace KCal {
+ class Event;
+ class Calendar;
+}
+
+namespace KIO {
+ class Job;
+}
+
+namespace KPIM {
+
+class ExchangeAccount;
+class ExchangeDownload;
+class ExchangeUpload;
+class ExchangeDelete;
+//class ExchangeMonitor;
+
+class KDE_EXPORT ExchangeClient : public QObject {
+ Q_OBJECT
+ public:
+ ExchangeClient( ExchangeAccount* account, const QString& mTimeZoneId=QString::null );
+ ~ExchangeClient();
+
+ /**
+ * Associate this client with a window given by @p window.
+ */
+ void setWindow(QWidget *window);
+
+ /**
+ * Returns the window this client is associated with.
+ */
+ QWidget *window() const;
+
+ /**
+ * Set the time zone to use
+ */
+ void setTimeZoneId( const QString& timeZoneId );
+ QString timeZoneId();
+
+ // synchronous functions
+ enum {
+ ResultOK, /** No problem */
+ UnknownError, /** Something else happened */
+ CommunicationError, /** IO Error, the server could not be reached or returned an HTTP error */
+ ServerResponseError, /** Server did not give a useful response. For download, this
+ means that a SEARCH did not result in anything like an appointment */
+ IllegalAppointmentError, /** Reading appointment data from server response failed */
+ NonEventError, /** The Incidence that is to be uplaoded to the server is not an Event */
+ EventWriteError, /** When writing an event to the server, an error occurred */
+ DeleteUnknownEventError /** The event to be deleted does not exist on the server */
+ };
+
+ int downloadSynchronous( KCal::Calendar* calendar, const QDate& start, const QDate& end, bool showProgress=false);
+ int uploadSynchronous( KCal::Event* event );
+ int removeSynchronous( KCal::Event* event );
+
+ // ExchangeMonitor* monitor( int pollMode, const QHostAddress& ownInterface );
+
+ QString detailedErrorString();
+
+ public slots:
+ // Asynchronous functions, wait for "finished" signals for result
+ // Deprecated: use download() without the Calendar* argument instead
+ void download( KCal::Calendar* calendar, const QDate& start, const QDate& end, bool showProgress=false);
+ void download( const QDate& start, const QDate& end, bool showProgress=false);
+ void upload( KCal::Event* event );
+ void remove( KCal::Event* event );
+ void test();
+
+ private slots:
+ void slotDownloadFinished( ExchangeDownload* worker, int result, const QString& moreInfo );
+ void slotDownloadFinished( ExchangeDownload* worker, int result, const QString& moreInfo, QPtrList<KCal::Event>& );
+ void slotUploadFinished( ExchangeUpload* worker, int result, const QString& moreInfo );
+ void slotRemoveFinished( ExchangeDelete* worker, int result, const QString& moreInfo );
+ void slotSyncFinished( int result, const QString& moreInfo );
+
+ signals:
+ // Useful for progress dialogs, shows how much still needs to be done.
+ // Not used right now, since ExchangeDownload provides its own progress dialog
+ void startDownload();
+ void finishDownload();
+
+ void downloadFinished( int result, const QString& moreInfo );
+ void event( KCal::Event* event, const KURL& url);
+ void downloadFinished( int result, const QString& moreInfo, QPtrList<KCal::Event>& events );
+ void uploadFinished( int result, const QString& moreInfo );
+ void removeFinished( int result, const QString& moreInfo );
+
+ private:
+ void test2();
+
+ enum { WaitingForResult, HaveResult, Error };
+
+ int mClientState;
+ int mSyncResult;
+ QString mDetailedErrorString;
+ QWidget* mWindow;
+ ExchangeAccount* mAccount;
+ QString mTimeZoneId;
+};
+
+}
+
+#endif
diff --git a/libkpimexchange/core/exchangedelete.cpp b/libkpimexchange/core/exchangedelete.cpp
new file mode 100644
index 000000000..8495afa36
--- /dev/null
+++ b/libkpimexchange/core/exchangedelete.cpp
@@ -0,0 +1,121 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qstring.h>
+#include <qregexp.h>
+
+#include <kurl.h>
+#include <kdebug.h>
+#include <krfcdate.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+
+#include <kio/slave.h>
+#include <kio/scheduler.h>
+#include <kio/slavebase.h>
+#include <kio/davjob.h>
+#include <kio/http.h>
+
+#include "exchangeclient.h"
+#include "exchangeprogress.h"
+#include "exchangedelete.h"
+#include "exchangeaccount.h"
+#include "utils.h"
+
+using namespace KPIM;
+
+// Delete:
+// - Find URL for uid
+// - Delete URL
+// - Can there be multipe URLs, for instance when dealing with
+// recurrent appointments? Maybe, so we just look for Master or Single
+// instancetypes
+
+ExchangeDelete::ExchangeDelete( KCal::Event* event, ExchangeAccount* account, QWidget* window ) :
+ mWindow( window )
+{
+ kdDebug() << "Created ExchangeDelete" << endl;
+
+ mAccount = account;
+
+ findUidSingleMaster( event->uid() );
+}
+
+ExchangeDelete::~ExchangeDelete()
+{
+ kdDebug() << "ExchangeDelete destructor" << endl;
+}
+
+void ExchangeDelete::findUidSingleMaster( QString const& uid )
+{
+ QString query =
+ "SELECT \"DAV:href\", \"urn:schemas:calendar:uid\"\r\n"
+ "FROM Scope('shallow traversal of \"\"')\r\n"
+ "WHERE \"urn:schemas:calendar:uid\" = '" + uid + "'\r\n"
+ " AND (\"urn:schemas:calendar:instancetype\" = 0\r\n"
+ " OR \"urn:schemas:calendar:instancetype\" = 1)\r\n";
+
+ KIO::DavJob* job = KIO::davSearch( mAccount->calendarURL(), "DAV:", "sql", query, false );
+ job->setWindow( mWindow );
+ connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotFindUidResult(KIO::Job *)));
+}
+
+void ExchangeDelete::slotFindUidResult( KIO::Job * job )
+{
+ if ( job->error() ) {
+ job->showErrorDialog( 0L );
+ emit finished( this, ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
+ return;
+ }
+ QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
+
+ QDomElement item = response.documentElement().firstChild().toElement();
+ QDomElement hrefElement = item.namedItem( "href" ).toElement();
+ if ( item.isNull() || hrefElement.isNull() ) {
+ // Not found
+ emit finished( this, ExchangeClient::DeleteUnknownEventError, "UID of event to be deleted not found on server\n"+response.toString() );
+ return;
+ }
+ // Found the appointment's URL
+ QString href = hrefElement.text();
+ KURL url(href);
+
+ startDelete( toDAV( url ) );
+}
+
+void ExchangeDelete::startDelete( const KURL& url )
+{
+ KIO::SimpleJob* job = KIO::file_delete( url, false ); // no GUI
+ job->setWindow( mWindow );
+ connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotDeleteResult( KIO::Job * ) ) );
+}
+
+void ExchangeDelete::slotDeleteResult( KIO::Job* job )
+{
+ kdDebug() << "Finished Delete" << endl;
+ if ( job->error() ) {
+ job->showErrorDialog( 0L );
+ emit finished( this, ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
+ return;
+ }
+ emit finished( this, ExchangeClient::ResultOK, QString::null );
+}
+
+#include "exchangedelete.moc"
diff --git a/libkpimexchange/core/exchangedelete.h b/libkpimexchange/core/exchangedelete.h
new file mode 100644
index 000000000..a9d933254
--- /dev/null
+++ b/libkpimexchange/core/exchangedelete.h
@@ -0,0 +1,59 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef KDEPIM_EXCHANGE_DELETE_H
+#define KDEPIM_EXCHANGE_DELETE_H
+
+#include <qstring.h>
+#include <qwidget.h>
+
+#include <kio/job.h>
+#include <kdepimmacros.h>
+
+#include <libkcal/calendar.h>
+#include <libkcal/event.h>
+
+namespace KPIM {
+
+class ExchangeAccount;
+
+class KDE_EXPORT ExchangeDelete : public QObject {
+ Q_OBJECT
+ public:
+ ExchangeDelete( KCal::Event* event, ExchangeAccount* account, QWidget* window=0 );
+ ~ExchangeDelete();
+
+ private slots:
+ void slotDeleteResult( KIO::Job * );
+ void slotFindUidResult( KIO::Job * );
+
+ signals:
+ void finished( ExchangeDelete* worker, int result, const QString& moreInfo );
+
+ private:
+ void findUidSingleMaster( QString const& uid );
+ void startDelete( const KURL& url );
+
+ ExchangeAccount* mAccount;
+ QWidget* mWindow;
+};
+
+}
+
+#endif
diff --git a/libkpimexchange/core/exchangedownload.cpp b/libkpimexchange/core/exchangedownload.cpp
new file mode 100644
index 000000000..dcf469549
--- /dev/null
+++ b/libkpimexchange/core/exchangedownload.cpp
@@ -0,0 +1,643 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdatastream.h>
+#include <qcstring.h>
+#include <qregexp.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <krfcdate.h>
+
+#include <kio/slave.h>
+#include <kio/scheduler.h>
+#include <kio/slavebase.h>
+#include <kio/davjob.h>
+#include <kio/http.h>
+#include <kio/job.h>
+
+#include <libkcal/incidence.h>
+#include <libkcal/event.h>
+#include <libkcal/recurrence.h>
+#include <libkcal/icalformat.h>
+#include <libkcal/icalformatimpl.h>
+#include <libkcal/calendarlocal.h>
+
+extern "C" {
+ #include <ical.h>
+}
+
+#include "exchangeclient.h"
+#include "exchangeaccount.h"
+#include "exchangeprogress.h"
+#include "utils.h"
+
+#include "exchangedownload.h"
+
+using namespace KPIM;
+
+ExchangeDownload::ExchangeDownload( ExchangeAccount *account, QWidget *window )
+ : mWindow( window )
+{
+ kdDebug() << "ExchangeDownload()" << endl;
+
+ mAccount = account;
+ mDownloadsBusy = 0;
+ mProgress = 0;
+ mCalendar = 0;
+ mFormat = new KCal::ICalFormat();
+}
+
+ExchangeDownload::~ExchangeDownload()
+{
+ kdDebug() << "ExchangeDownload destructor" << endl;
+ delete mFormat;
+ if ( mEvents ) delete mEvents;
+}
+
+void ExchangeDownload::download( KCal::Calendar *calendar, const QDate &start,
+ const QDate &end, bool showProgress )
+{
+ mCalendar = calendar;
+ mEvents = 0;
+
+ if( showProgress ) {
+#if 0
+ //kdDebug() << "Creating progress dialog" << endl;
+ mProgress = new ExchangeProgress();
+ mProgress->show();
+
+ connect( this, SIGNAL( startDownload() ), mProgress,
+ SLOT( slotTransferStarted() ) );
+ connect( this, SIGNAL(finishDownload() ), mProgress,
+ SLOT( slotTransferFinished() ) );
+#endif
+ }
+
+ QString sql = dateSelectQuery( start, end.addDays( 1 ) );
+
+ kdDebug() << "Exchange download query: " << endl << sql << endl;
+
+ increaseDownloads();
+
+ kdDebug() << "ExchangeDownload::download() davSearch URL: "
+ << mAccount->calendarURL() << endl;
+
+ KIO::DavJob *job = KIO::davSearch( mAccount->calendarURL(), "DAV:", "sql",
+ sql, false );
+ KIO::Scheduler::scheduleJob( job );
+ job->setWindow( mWindow );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotSearchResult( KIO::Job *) ) );
+}
+
+void ExchangeDownload::download( const QDate& start, const QDate& end, bool showProgress )
+{
+ mCalendar = 0;
+ mEvents = new QPtrList<KCal::Event>;
+
+ if( showProgress ) {
+ //kdDebug() << "Creating progress dialog" << endl;
+ mProgress = new ExchangeProgress();
+ mProgress->show();
+
+ connect( this, SIGNAL(startDownload()), mProgress, SLOT(slotTransferStarted()) );
+ connect( this, SIGNAL(finishDownload()), mProgress, SLOT(slotTransferFinished()) );
+ }
+
+ QString sql = dateSelectQuery( start, end.addDays( 1 ) );
+
+ increaseDownloads();
+
+ KIO::DavJob *job = KIO::davSearch( mAccount->calendarURL(), "DAV:", "sql", sql, false );
+ KIO::Scheduler::scheduleJob(job);
+ job->setWindow( mWindow );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotSearchResult( KIO::Job * ) ) );
+}
+
+// Original query TODO: make query configurable
+QString ExchangeDownload::dateSelectQuery( const QDate& start, const QDate& end )
+{
+ QString startString;
+ startString.sprintf("%04i/%02i/%02i",start.year(),start.month(),start.day());
+ QString endString;
+ endString.sprintf("%04i/%02i/%02i",end.year(),end.month(),end.day());
+ QString sql =
+ "SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\", \"urn:schemas:calendar:uid\"\r\n"
+ "FROM Scope('shallow traversal of \"\"')\r\n"
+ "WHERE \"urn:schemas:calendar:dtend\" > '" + startString + "'\r\n"
+ "AND \"urn:schemas:calendar:dtstart\" < '" + endString + "'";
+ return sql;
+}
+
+#if 0
+// That's the "new" code that breaks with Exchange. It was meant for Opengroupware, but that got its own resource anyway
+QString ExchangeDownload::dateSelectQuery( const QDate& start, const QDate& end )
+{
+ QString startString;
+ startString.sprintf( "%04i-%02i-%02iT00:00:00Z", start.year(),
+ start.month(), start.day() );
+ QString endString;
+ endString.sprintf( "%04i-%02i-%02iT23:59:59Z", end.year(), end.month(),
+ end.day() );
+ QString sql =
+ "SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\", "
+ "\"urn:schemas:calendar:uid\"\r\n"
+ "FROM Scope('shallow traversal of \"\"')\r\n"
+ "WHERE \"urn:schemas:calendar:dtend\" > '" + startString + "'\r\n"
+ "AND \"urn:schemas:calendar:dtstart\" < '" + endString + "'";
+ return sql;
+}
+#endif
+
+void ExchangeDownload::slotSearchResult( KIO::Job *job )
+{
+ if ( job->error() ) {
+ kdError() << "ExchangeDownload::slotSearchResult() error: "
+ << job->error() << endl;
+ QString text = i18n("ExchangeDownload\nError accessing '%1': %2")
+ .arg( mAccount->calendarURL().prettyURL() )
+ .arg( job->errorString() );
+ KMessageBox::error( 0, text );
+ finishUp( ExchangeClient::CommunicationError, job );
+ return;
+ }
+ QDomDocument &response = static_cast<KIO::DavJob *>( job )->response();
+
+ kdDebug() << "Search result: " << endl << response.toString() << endl;
+
+ handleAppointments( response, true );
+
+ decreaseDownloads();
+}
+
+void ExchangeDownload::slotMasterResult( KIO::Job *job )
+{
+ if ( job->error() ) {
+ kdError() << "Error result for Master search: " << job->error() << endl;
+ job->showErrorDialog( 0 );
+ finishUp( ExchangeClient::CommunicationError, job );
+ return;
+ }
+ QDomDocument &response = static_cast<KIO::DavJob *>( job )->response();
+
+ kdDebug() << "Search (master) result: " << endl << response.toString() << endl;
+
+ handleAppointments( response, false );
+
+ decreaseDownloads();
+}
+
+void ExchangeDownload::handleAppointments( const QDomDocument &response,
+ bool recurrence )
+{
+ kdDebug() << "Entering handleAppointments" << endl;
+ int successCount = 0;
+
+ if ( response.documentElement().firstChild().toElement().isNull() ) {
+ // Got an empty response, but no error. This would mean there are
+ // no appointments in this time period.
+ return;
+ }
+
+ for( QDomElement item = response.documentElement().firstChild().toElement();
+ !item.isNull();
+ item = item.nextSibling().toElement() ) {
+ //kdDebug() << "Current item:" << item.tagName() << endl;
+ QDomNodeList propstats = item.elementsByTagNameNS( "DAV:", "propstat" );
+ // kdDebug() << "Item has " << propstats.count() << " propstat children" << endl;
+ for( uint i=0; i < propstats.count(); i++ ) {
+ QDomElement propstat = propstats.item(i).toElement();
+ QDomElement prop = propstat.namedItem( "prop" ).toElement();
+ if ( prop.isNull() ) {
+ kdError() << "Error: no <prop> in response" << endl;
+ continue;
+ }
+
+ QDomElement instancetypeElement = prop.namedItem( "instancetype" ).toElement();
+ if ( instancetypeElement.isNull() ) {
+ kdError() << "Error: no instance type in Exchange server reply" << endl;
+ continue;
+ }
+ int instanceType = instancetypeElement.text().toInt();
+ //kdDebug() << "Instance type: " << instanceType << endl;
+
+ if ( recurrence && instanceType > 0 ) {
+ QDomElement uidElement = prop.namedItem( "uid" ).toElement();
+ if ( uidElement.isNull() ) {
+ kdError() << "Error: no uid in Exchange server reply" << endl;
+ continue;
+ }
+ QString uid = uidElement.text();
+ if ( ! m_uids.contains( uid ) ) {
+ m_uids[uid] = 1;
+ handleRecurrence(uid);
+ successCount++;
+ }
+ continue;
+ }
+
+ QDomElement hrefElement = prop.namedItem( "href" ).toElement();
+ if ( hrefElement.isNull() ) {
+ kdError() << "Error: no href in Exchange server reply" << endl;
+ continue;
+ }
+ QString href = hrefElement.text();
+ KURL url(href);
+
+ kdDebug() << "Getting appointment from url: " << url.prettyURL() << endl;
+
+ readAppointment( toDAV( url ) );
+ successCount++;
+ }
+ }
+ if ( !successCount ) {
+ finishUp( ExchangeClient::ServerResponseError,
+ "WebDAV SEARCH response:\n" + response.toString() );
+ }
+}
+
+void ExchangeDownload::handleRecurrence( QString uid )
+{
+ // kdDebug() << "Handling recurrence info for uid=" << uid << endl;
+ QString query =
+ "SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\"\r\n"
+ "FROM Scope('shallow traversal of \"\"')\r\n"
+ "WHERE \"urn:schemas:calendar:uid\" = '" + uid + "'\r\n"
+ " AND (\"urn:schemas:calendar:instancetype\" = 1)\r\n";
+// " OR \"urn:schemas:calendar:instancetype\" = 3)\r\n" // FIXME: exception are not handled
+
+ // kdDebug() << "Exchange master query: " << endl << query << endl;
+
+ increaseDownloads();
+
+ KIO::DavJob* job = KIO::davSearch( mAccount->calendarURL(), "DAV:", "sql",
+ query, false );
+ KIO::Scheduler::scheduleJob( job );
+ job->setWindow( mWindow );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotMasterResult( KIO::Job * ) ) );
+}
+
+void ExchangeDownload::readAppointment( const KURL& url )
+{
+ QDomDocument doc;
+ QDomElement root = addElement( doc, doc, "DAV:", "propfind" );
+ QDomElement prop = addElement( doc, root, "DAV:", "prop" );
+ addElement( doc, prop, "urn:schemas:calendar:", "uid" );
+ addElement( doc, prop, "urn:schemas:calendar:", "timezoneid" );
+ addElement( doc, prop, "urn:schemas:calendar:", "timezone" );
+ addElement( doc, prop, "urn:schemas:calendar:", "lastmodified" );
+ addElement( doc, prop, "urn:schemas:calendar:", "organizer" );
+ addElement( doc, prop, "urn:schemas:calendar:", "contact" );
+ addElement( doc, prop, "urn:schemas:httpmail:", "to" );
+ addElement( doc, prop, "urn:schemas:calendar:", "attendeestatus" );
+ addElement( doc, prop, "urn:schemas:calendar:", "attendeerole" );
+ addElement( doc, prop, "DAV:", "isreadonly" );
+ addElement( doc, prop, "urn:schemas:calendar:", "instancetype" );
+ addElement( doc, prop, "urn:schemas:calendar:", "created" );
+ addElement( doc, prop, "urn:schemas:calendar:", "dtstart" );
+ addElement( doc, prop, "urn:schemas:calendar:", "dtend" );
+ addElement( doc, prop, "urn:schemas:calendar:", "alldayevent" );
+ addElement( doc, prop, "urn:schemas:calendar:", "transparent" );
+ addElement( doc, prop, "urn:schemas:httpmail:", "textdescription" );
+ addElement( doc, prop, "urn:schemas:httpmail:", "subject" );
+ addElement( doc, prop, "urn:schemas:calendar:", "location" );
+ addElement( doc, prop, "urn:schemas:calendar:", "rrule" );
+ addElement( doc, prop, "urn:schemas:calendar:", "exdate" );
+ addElement( doc, prop, "urn:schemas:mailheader:", "sensitivity" );
+ addElement( doc, prop, "urn:schemas:calendar:", "reminderoffset" );
+
+ addElement( doc, prop, "urn:schemas-microsoft-com:office:office",
+ "Keywords" );
+
+// addElement( doc, prop, "", "" );
+// addElement( doc, prop, "DAV:", "" );
+// addElement( doc, prop, "urn:schemas:calendar:", "" );
+// addElement( doc, prop, "urn:content-classes:appointment", "" );
+// addElement( doc, prop, "urn:schemas:httpmail:", "" );
+
+ increaseDownloads();
+
+ KIO::DavJob* job = KIO::davPropFind( url, doc, "0", false );
+ KIO::Scheduler::scheduleJob( job );
+ job->setWindow( mWindow );
+ job->addMetaData( "errorPage", "false" );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotPropFindResult( KIO::Job * ) ) );
+}
+
+void ExchangeDownload::slotPropFindResult( KIO::Job *job )
+{
+ kdDebug() << "slotPropFindResult" << endl;
+
+ int error = job->error();
+ if ( error ) {
+ job->showErrorDialog( 0 );
+ finishUp( ExchangeClient::CommunicationError, job );
+ return;
+ }
+
+ QDomDocument response = static_cast<KIO::DavJob *>( job )->response();
+ kdDebug() << "Response: " << endl;
+ kdDebug() << response.toString() << endl;
+
+ QDomElement prop = response.documentElement().namedItem( "response" )
+ .namedItem( "propstat" ).namedItem( "prop" ).toElement();
+
+ KCal::Event* event = new KCal::Event();
+
+ QDomElement uidElement = prop.namedItem( "uid" ).toElement();
+ if ( uidElement.isNull() ) {
+ kdError() << "Error: no uid in Exchange server reply" << endl;
+ finishUp( ExchangeClient::IllegalAppointmentError,
+ "WebDAV server response:\n" + response.toString() );
+ return;
+ }
+ event->setUid( uidElement.text() );
+ // kdDebug() << "Got UID: " << uidElement.text() << endl;
+
+ QString timezoneid = prop.namedItem( "timezoneid" ).toElement().text();
+ // kdDebug() << "DEBUG: timezoneid = " << timezoneid << endl;
+
+ QString timezone = prop.namedItem( "timezone" ).toElement().text();
+ // kdDebug() << "DEBUG: timezone = " << timezone << endl;
+
+ // mFormat is used for parsing recurrence rules.
+ QString localTimeZoneId;
+ if ( mCalendar ) {
+ mFormat->setTimeZone( mCalendar->timeZoneId(), !mCalendar->isLocalTime() );
+ localTimeZoneId = mCalendar->timeZoneId();
+ } else {
+ localTimeZoneId = "UTC";
+ // If no mCalendar, stay in UTC
+ }
+
+ QString lastModified = prop.namedItem( "lastmodified" ).toElement().text();
+ if ( !lastModified.isEmpty() ) {
+ QDateTime dt = utcAsZone( QDateTime::fromString( lastModified, Qt::ISODate ), localTimeZoneId );
+ event->setLastModified( dt );
+ kdDebug() << "Got lastModified:" << lastModified << ", " << dt.toString() << endl;
+ }
+
+ QString organizer = prop.namedItem( "organizer" ).toElement().text();
+ // TODO: Does outlook have a common name? Or does the organizer already contain both?
+ event->setOrganizer( organizer );
+ // kdDebug() << "Got organizer: " << organizer << endl;
+
+ // Trying to find attendees, not working yet
+ QString contact = prop.namedItem( "contact" ).toElement().text();
+// event->setOrganizer( organizer );
+ // kdDebug() << "DEBUG: Got contact: " << contact << endl;
+
+ // This looks promising for finding attendees
+ // FIXME: get this to work
+ QString to = prop.namedItem( "to" ).toElement().text();
+ // kdDebug() << "DEBUG: Got to: " << to << endl;
+ QStringList attn = QStringList::split( ",", to ); // This doesn't work: there can be commas between ""
+ QStringList::iterator it;
+ for ( it = attn.begin(); it != attn.end(); ++it ) {
+ // kdDebug() << " attendee: " << (*it) << endl;
+ QString name = "";
+ // KCal::Attendee* a = new KCal::Attendee( name, email );
+
+ // event->addAttendee( a );
+ }
+
+ QString readonly = prop.namedItem( "isreadonly" ).toElement().text();
+ event->setReadOnly( readonly == "1" );
+ kdDebug() << "Got readonly: " << readonly << ":" << (readonly != "0") << endl;
+
+ QString created = prop.namedItem( "created" ).toElement().text();
+ if ( !created.isEmpty() ) {
+ QDateTime dt = utcAsZone( QDateTime::fromString( created, Qt::ISODate ),
+ localTimeZoneId );
+ event->setCreated( dt );
+ kdDebug() << "got created: " << dt.toString() << endl;
+ }
+
+ QString dtstart = prop.namedItem( "dtstart" ).toElement().text();
+ if ( !dtstart.isEmpty() ) {
+ QDateTime dt = utcAsZone( QDateTime::fromString( dtstart, Qt::ISODate ),
+ localTimeZoneId );
+ event->setDtStart( dt );
+ kdDebug() << "got dtstart: " << dtstart << " becomes in timezone " << dt.toString() << endl;
+ }
+
+ QString alldayevent = prop.namedItem( "alldayevent" ).toElement().text();
+ bool floats = alldayevent.toInt() != 0;
+ event->setFloats( floats );
+ kdDebug() << "Got alldayevent: \"" << alldayevent << "\":" << floats << endl;
+
+ QString dtend = prop.namedItem( "dtend" ).toElement().text();
+ if ( !dtend.isEmpty() ) {
+ QDateTime dt = utcAsZone( QDateTime::fromString( dtend, Qt::ISODate ),
+ localTimeZoneId );
+ // Outlook thinks differently about floating event timing than libkcal
+ if ( floats ) dt = dt.addDays( -1 );
+ event->setDtEnd( dt );
+ kdDebug() << "got dtend: " << dtend << " becomes in timezone " << dt.toString() << endl;
+ }
+
+ QString transparent = prop.namedItem( "transparent" ).toElement().text();
+ event->setTransparency( transparent.toInt() > 0 ? KCal::Event::Transparent
+ : KCal::Event::Opaque );
+ // kdDebug() << "Got transparent: " << transparent << endl;
+
+ QString description = prop.namedItem( "textdescription" ).toElement().text();
+ event->setDescription( description );
+ kdDebug() << "Got description: " << description << endl;
+
+ QString subject = prop.namedItem( "subject" ).toElement().text();
+ event->setSummary( subject );
+ kdDebug() << "Got summary: " << subject << endl;
+
+ QString location = prop.namedItem( "location" ).toElement().text();
+ event->setLocation( location );
+ // kdDebug() << "Got location: " << location << endl;
+
+ QString rrule = prop.namedItem( "rrule" ).toElement().text();
+ kdDebug() << "Got rrule: " << rrule << endl;
+ if ( !rrule.isEmpty() ) {
+ // Timezone should be handled automatically
+ // because we used mFormat->setTimeZone() earlier
+ KCal::RecurrenceRule *rr = event->recurrence()->defaultRRule( true );
+
+ if ( !rr || !mFormat->fromString( rr, rrule ) ) {
+ kdError() << "ERROR parsing rrule " << rrule << endl;
+ }
+ }
+
+ QDomElement keywords = prop.namedItem( "Keywords" ).toElement();
+ QStringList categories;
+ QDomNodeList list = keywords.elementsByTagNameNS( "xml:", "v" );
+ for( uint i=0; i < list.count(); i++ ) {
+ QDomElement item = list.item(i).toElement();
+ categories.append( item.text() );
+ }
+ event->setCategories( categories );
+ // kdDebug() << "Got categories: " << categories.join( ", " ) << endl;
+
+
+ QDomElement exdate = prop.namedItem( "exdate" ).toElement();
+ KCal::DateList exdates;
+ list = exdate.elementsByTagNameNS( "xml:", "v" );
+ for( uint i=0; i < list.count(); i++ ) {
+ QDomElement item = list.item(i).toElement();
+ QDate date = utcAsZone( QDateTime::fromString( item.text(), Qt::ISODate ), localTimeZoneId ).date();
+ exdates.append( date );
+ // kdDebug() << "Got exdate: " << date.toString() << endl;
+ }
+ event->recurrence()->setExDates( exdates );
+
+ // Exchange sentitivity values:
+ // 0 None
+ // 1 Personal
+ // 2 Private
+ // 3 Company Confidential
+ QString sensitivity = prop.namedItem( "sensitivity" ).toElement().text();
+ if ( ! sensitivity.isNull() )
+ switch( sensitivity.toInt() ) {
+ case 0: event->setSecrecy( KCal::Incidence::SecrecyPublic ); break;
+ case 1: event->setSecrecy( KCal::Incidence::SecrecyPrivate ); break;
+ case 2: event->setSecrecy( KCal::Incidence::SecrecyPrivate ); break;
+ case 3: event->setSecrecy( KCal::Incidence::SecrecyConfidential ); break;
+ default: kdWarning() << "Unknown sensitivity: " << sensitivity << endl;
+ }
+ // kdDebug() << "Got sensitivity: " << sensitivity << endl;
+
+
+ QString reminder = prop.namedItem( "reminderoffset" ).toElement().text();
+ // kdDebug() << "Reminder offset: " << reminder << endl;
+ if ( !reminder.isEmpty() ) {
+ // Duration before event in seconds
+ KCal::Duration offset( - reminder.toInt() );
+ KCal::Alarm *alarm = event->newAlarm();
+ alarm->setStartOffset( offset );
+ alarm->setDisplayAlarm("");
+ alarm->setEnabled( true );
+ // TODO: multiple alarms; alarm->setType( KCal::Alarm::xxxx );
+ }
+ /** Create a new alarm which is associated with this incidence */
+ //Alarm* newAlarm();
+ /** Add an alarm which is associated with this incidence */
+ //void addAlarm(Alarm*);
+
+ /** point at some other event to which the event relates */
+ //void setRelatedTo(Incidence *relatedTo);
+ /** Add an event which is related to this event */
+ //void addRelation(Incidence *);
+
+ /** set the list of attachments/associated files for this event */
+ //void setAttachments(const QStringList &attachments);
+
+ /** set resources used, such as Office, Car, etc. */
+ //void setResources(const QStringList &resources);
+
+ /** set the event's priority, 0 is undefined, 1 highest (decreasing order) */
+ //void setPriority(int priority);
+
+ /**
+ Add Attendee to this incidence. IncidenceBase takes ownership of the
+ Attendee object.
+ */
+
+ //void addAttendee(Attendee *a, bool doupdate=true );
+
+ // THE FOLLOWING EVENT PROPERTIES ARE NOT READ
+
+ // Revision ID in webdav is a String, not an int
+ /** set the number of revisions this event has seen */
+ //void setRevision(int rev);
+
+ // Problem: When you sync Outlook to a Palm, the conduit splits up
+ // multi-day events into single-day events WITH ALL THE SAME UID
+ // Grrrrrrr.
+ if ( mCalendar ) {
+ KCal::Event *oldEvent = mCalendar->event( event->uid() );
+ if ( oldEvent ) {
+ kdWarning() << "Already got his event, replace it..." << endl;
+ mCalendar->deleteEvent( oldEvent );
+ }
+ kdDebug() << "ADD EVENT" << endl;
+ mCalendar->addEvent( event );
+ } else {
+ kdDebug() << "EMIT gotEvent" << endl;
+ emit gotEvent( event, static_cast<KIO::DavJob *>( job )->url() );
+// mEvents->append( event );
+ }
+
+ decreaseDownloads();
+}
+
+void ExchangeDownload::increaseDownloads()
+{
+ mDownloadsBusy++;
+ emit startDownload();
+}
+
+void ExchangeDownload::decreaseDownloads()
+{
+ mDownloadsBusy--;
+ // kdDebug() << "Download finished, waiting for " << mDownloadsBusy << " more" << endl;
+ emit finishDownload();
+ if ( mDownloadsBusy == 0 ) {
+ kdDebug() << "All downloads finished" << endl;
+ finishUp( ExchangeClient::ResultOK );
+ }
+}
+
+void ExchangeDownload::finishUp( int result, const QString &moreInfo )
+{
+ kdDebug() << "ExchangeDownload::finishUp() " << result << " "
+ << moreInfo << endl;
+
+ if ( mCalendar ) mCalendar->setModified( true );
+ // Disconnect from progress bar
+ if ( mProgress ) {
+ disconnect( this, 0, mProgress, 0 );
+ disconnect( mProgress, 0, this, 0 );
+ mProgress->delayedDestruct();
+ }
+
+// if ( mEvents ) {
+// emit finished( this, result, moreInfo, *mEvents );
+// } else {
+ emit finished( this, result, moreInfo );
+// }
+}
+
+void ExchangeDownload::finishUp( int result, KIO::Job *job )
+{
+ finishUp( result, QString("WebDAV job error code = ") +
+ QString::number( job->error() ) + ";\n" + "\"" +
+ job->errorString() + "\"" );
+}
+
+#include "exchangedownload.moc"
diff --git a/libkpimexchange/core/exchangedownload.h b/libkpimexchange/core/exchangedownload.h
new file mode 100644
index 000000000..a43d82692
--- /dev/null
+++ b/libkpimexchange/core/exchangedownload.h
@@ -0,0 +1,89 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef KDEPIM_EXCHANGE_DOWNLOAD_H
+#define KDEPIM_EXCHANGE_DOWNLOAD_H
+
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qdatetime.h>
+#include <qdom.h>
+#include <qmap.h>
+#include <kio/job.h>
+
+#include <libkcal/calendar.h>
+#include <libkcal/icalformat.h>
+
+namespace KPIM {
+
+class ExchangeProgress;
+class ExchangeAccount;
+
+class ExchangeDownload : public QObject {
+ Q_OBJECT
+ public:
+ ExchangeDownload( ExchangeAccount* account, QWidget* window=0 );
+ ~ExchangeDownload();
+
+ void download( KCal::Calendar* calendar,
+ const QDate& start, const QDate& end, bool showProgress );
+ void download( const QDate& start, const QDate& end, bool showProgress );
+
+ signals:
+ void startDownload();
+ void finishDownload();
+
+ void gotEvent( KCal::Event* event, const KURL& url );
+ void finished( ExchangeDownload*, int result, const QString& moreInfo );
+ void finished( ExchangeDownload*, int result, const QString& moreInfo, QPtrList<KCal::Event>& events );
+
+ private slots:
+ void slotSearchResult( KIO::Job *job );
+ void slotMasterResult( KIO::Job* job );
+ void slotPropFindResult( KIO::Job * );
+
+ private:
+ void handleAppointments( const QDomDocument &, bool recurrence );
+ void readAppointment( const KURL& url );
+ void handleRecurrence( QString uid );
+ void finishUp( int result, const QString& moreInfo=QString::null );
+ void finishUp( int result, KIO::Job* job );
+
+ void increaseDownloads();
+ void decreaseDownloads();
+
+ QString dateSelectQuery( const QDate& start, const QDate& end );
+
+ KCal::Calendar *mCalendar;
+ KCal::ICalFormat *mFormat;
+ QPtrList<KCal::Event> *mEvents;
+ ExchangeAccount *mAccount;
+ ExchangeProgress *mProgress;
+ int mDownloadsBusy;
+ QDomDocument mResponse;
+
+ QMap<QString,int> m_uids; // This keeps track of uids we already covered. Especially useful for
+ // recurring events.
+ QWidget* mWindow;
+};
+
+}
+
+#endif
+
diff --git a/libkpimexchange/core/exchangemonitor.cpp b/libkpimexchange/core/exchangemonitor.cpp
new file mode 100644
index 000000000..c659efb15
--- /dev/null
+++ b/libkpimexchange/core/exchangemonitor.cpp
@@ -0,0 +1,386 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qstring.h>
+#include <qregexp.h>
+#include <qsocketdevice.h>
+#include <qsocketnotifier.h>
+#include <qtextstream.h>
+
+#include <kurl.h>
+#include <kdebug.h>
+#include <krfcdate.h>
+#include <kextsock.h>
+
+#include <kio/job.h>
+#include <kio/slave.h>
+#include <kio/scheduler.h>
+#include <kio/slavebase.h>
+#include <kio/davjob.h>
+#include <kio/http.h>
+
+#include <libkcal/event.h>
+#include <libkcal/icalformat.h>
+#include <libkcal/icalformatimpl.h>
+#include <libkcal/recurrence.h>
+#include <libkcal/incidence.h>
+#include <libkcal/event.h>
+
+#include "exchangemonitor.h"
+#include "exchangeclient.h"
+#include "exchangeaccount.h"
+#include "utils.h"
+
+extern "C" {
+ #include <unistd.h>
+}
+
+using namespace KPIM;
+
+QString makeIDString( const ExchangeMonitor::IDList& IDs )
+{
+ QString result;
+ ExchangeMonitor::IDList::ConstIterator it;
+ for ( it = IDs.begin(); it != IDs.end(); ++it ) {
+ if ( it == IDs.begin() )
+ result += QString::number( (*it) );
+ else
+ result += "," + QString::number( (*it) );
+ }
+ return result;
+}
+
+ExchangeMonitor::IDList makeIDList( const QString& input )
+{
+ ExchangeMonitor::IDList IDs;
+ QStringList numbers = QStringList::split( ",", input );
+ QStringList::iterator j;
+ for ( j = numbers.begin(); j != numbers.end(); ++j ) {
+ ExchangeMonitor::ID id = (*j).toLong();
+ IDs.append( id );
+ }
+ return IDs;
+}
+
+ExchangeMonitor::ExchangeMonitor( ExchangeAccount* account, int pollMode, const QHostAddress& ownInterface )
+{
+ kdDebug() << "Called ExchangeMonitor" << endl;
+
+ mAccount = account;
+ mSubscriptionLifetime = 3600; // by default, renew subscription every 3600 seconds or one hour
+ mPollMode = pollMode;
+ mPollTimer = 0;
+
+ if ( pollMode == CallBack ) {
+ mSocket = new QSocketDevice( QSocketDevice::Datagram );
+ if ( ! mSocket->bind( ownInterface, 0 ) )
+ kdDebug() << "bind() returned false" << endl;
+ mSocket->setBlocking( false );
+ mNotifier = new QSocketNotifier( mSocket->socket(), QSocketNotifier::Read );
+ connect( mNotifier, SIGNAL(activated( int )), this, SLOT( slotActivated(int)));
+
+ //mSocket.setSocketFlags( KExtendedSocket::inetSocket | KExtendedSocket::passiveSocket | KExtendedSocket::datagramSocket | KExtendedSocket::bufferedSocket );
+ //mSocket.setHost( "jupiter.tbm.tudelft.nl" ); // Does this work?
+ //mSocket.setPort( 0 ); // setting port to 0 will make us bind to a random, free port
+ // UDP server socket: no listen
+ //if ( int code = mSocket.listen() )
+ // kdError() << "Error in socket listen: " << code << endl;
+ //mSocket.enableRead( true );
+ kdDebug() << "Port: " << mSocket->port() << endl;
+ kdDebug() << "Host: " << mSocket->address().toString() << endl;
+ // mStream = new QTextStream( mSocket );
+ }
+
+ if ( mPollMode == Poll ) {
+ mPollTimer = new QTimer( this, "mPollTimer" );
+ connect( mPollTimer, SIGNAL(timeout()), this, SLOT(slotPollTimer()) );
+ mPollTimer->start( 60000 ); // 1 minute timer
+ }
+
+ mRenewTimer = new QTimer( this, "mRenewTimer" );
+ connect( mRenewTimer, SIGNAL(timeout()), this, SLOT(slotRenewTimer()) );
+ mRenewTimer->start( mSubscriptionLifetime * 900 ); // 10% early so as to be in time
+}
+
+ExchangeMonitor::~ExchangeMonitor()
+{
+ kdDebug() << "Entering ExchangeMonitor destructor" << endl;
+ delete mNotifier;
+ delete mSocket;
+ if ( mPollTimer ) delete mPollTimer;
+ if ( mRenewTimer ) delete mRenewTimer;
+ if ( ! mSubscriptionMap.isEmpty() ) {
+ QString headers = "Subscription-ID: " + makeIDString( mSubscriptionMap.keys() );
+ kdDebug() << "Subsubscribing all watches, headers:" << endl << headers << endl;
+ KIO::DavJob *job = new KIO::DavJob( mAccount->calendarURL(), (int) KIO::DAV_UNSUBSCRIBE, QString::null, false );
+ job->addMetaData( "customHTTPHeader", headers );
+ // Can't do, this is a destructor!
+ // job->addMetaData( "PropagateHttpHeader", "true" );
+ // connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotUnsubscribeResult(KIO::Job *)));
+ }
+ kdDebug() << "Finished ExchangeMonitor destructor" << endl;
+
+}
+
+void ExchangeMonitor::addWatch( const KURL &url, int mode, int depth )
+{
+ QString headers = "Notification-type: ";
+ switch( mode ) {
+ case Delete: headers += "delete\r\n"; break;
+ case Move: headers += "move\r\n"; break;
+ case Newmail: headers += "pragma/<http://schemas.microsoft.com/exchange/newmail>\r\n"; break;
+ case Update: headers += "update\r\n"; break;
+ case UpdateNewMember: headers += "update/newmember\r\n"; break;
+ }
+
+ headers += "Depth: " + QString::number( depth );
+
+ if (mPollMode == CallBack )
+ headers += "\r\nCall-Back: httpu://" + mSocket->address().toString() + ":" + QString::number(mSocket->port());
+
+ kdDebug() << "Headers: " << headers << endl;
+
+ KURL myURL = toDAV( url );
+ KIO::DavJob *job = new KIO::DavJob( myURL, (int) KIO::DAV_SUBSCRIBE, QString::null, false );
+ job->addMetaData( "customHTTPHeader", headers );
+ job->addMetaData( "PropagateHttpHeader", "true" );
+ connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotSubscribeResult(KIO::Job *)));
+}
+
+void ExchangeMonitor::removeWatch( const KURL &url )
+{
+ KURL myURL = toDAV( url );
+ QMap<ID,KURL>::Iterator it;
+ for ( it = mSubscriptionMap.begin(); it != mSubscriptionMap.end(); ++it ) {
+ if ( it.data() == myURL ) {
+ removeWatch( it.key() );
+ return;
+ }
+ }
+ kdWarning() << "Trying to remove unknown watch " << myURL.prettyURL() << ", failed." << endl;
+}
+
+void ExchangeMonitor::removeWatch( ID id )
+{
+ KIO::DavJob *job = new KIO::DavJob( mAccount->calendarURL(), (int) KIO::DAV_UNSUBSCRIBE, QString::null, false );
+ job->addMetaData( "customHTTPHeader", "Subscription-id: " + QString::number( id ));
+ job->addMetaData( "PropagateHttpHeader", "true" );
+ connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotUnsubscribeResult(KIO::Job *)));
+}
+
+void ExchangeMonitor::slotSubscribeResult( KIO::Job * job )
+{
+ if ( job->error() ) {
+ job->showErrorDialog( 0L );
+ emit error( ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
+ return;
+ }
+
+ ID id;
+ KURL url;
+ bool gotID = false;
+ bool gotURL = false;
+
+ QStringList headers = QStringList::split( "\n", job->queryMetaData( "HTTP-Headers" ) );
+ for ( QStringList::Iterator it = headers.begin(); it != headers.end(); ++it ) {
+ int colon = (*it).find( ": " );
+ if ( colon<0 ) continue;
+ QString tag = (*it).left( colon ).stripWhiteSpace().lower();
+ QString value = (*it).mid( colon+1 ).stripWhiteSpace();
+ if ( tag == "subscription-lifetime" ) {
+ int lifetime = value.toInt();
+ if ( lifetime < mSubscriptionLifetime ) {
+ mSubscriptionLifetime = lifetime;
+ mRenewTimer->changeInterval( lifetime * 900 );
+ slotRenewTimer();
+ }
+ } else if ( tag == "subscription-id" ) {
+ id = value.toLong();
+ gotID = true;
+ } else if ( tag == "content-location" ) {
+ url = toDAV( KURL( value ) );
+ gotURL = true;
+ }
+ }
+
+ if ( mSubscriptionLifetime < 60 ) {
+ kdWarning() << "Exchange server gave subscription a lifetime of " << mSubscriptionLifetime << ", changing to 60 seconds." << endl;
+ mSubscriptionLifetime = 60;
+ return;
+ }
+
+ if ( ! gotID ) {
+ kdError() << "Error: Exchange server didn't give a subscription ID" << endl;
+ emit error( ExchangeClient::ServerResponseError, "No subscription ID in SUBSCRIBE response headers: " + headers.join(", ") );
+ return;
+ }
+
+ if ( ! gotURL ) {
+ kdError() << "Error: Exchange server didn't return content-location" << endl;
+ emit error( ExchangeClient::ServerResponseError, "No content-location in SUBSCRIBE response headers: " + headers.join(", ") );
+ return;
+ }
+
+ kdDebug() << "Lifetime: " << mSubscriptionLifetime << endl;
+ kdDebug() << "ID: " << id << endl;
+ kdDebug() << "URL: " << url.prettyURL() << endl;
+
+ mSubscriptionMap.insert( id, url );
+}
+
+void ExchangeMonitor::slotUnsubscribeResult( KIO::Job * job )
+{
+ if ( job->error() ) {
+ job->showErrorDialog( 0L );
+ emit error( ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
+ return;
+ }
+
+ QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
+ kdDebug() << "UNSUBSCRIBE result: " << endl << response.toString() << endl;
+
+ QDomElement status = response.documentElement().namedItem( "response" ).namedItem( "status" ).toElement();
+ QDomElement subscriptionID = response.documentElement().namedItem( "response" ).namedItem( "subscriptionID" ).toElement();
+ kdDebug() << "Subscription ID.text(): " << subscriptionID.text() << endl;
+ bool ok;
+ ID id = subscriptionID.text().toLong( &ok );
+ if ( ! status.text().contains( "200" ) || !ok) {
+ kdError() << "UNSUBSCRIBE result is not 200 or no subscription ID found" << endl;
+ emit error( ExchangeClient::ServerResponseError, "UNSUBSCRIBE yields an error response: \n" + response.toString() );
+ }
+
+ mSubscriptionMap.remove( id );
+}
+
+void ExchangeMonitor::slotPollTimer()
+{
+ kdDebug() << "ExchangeMonitor::slotPollTimer()" << endl;
+ poll( mSubscriptionMap.keys() );
+}
+
+void ExchangeMonitor::slotActivated( int )
+{
+ kdDebug() << "ExchangeMonitor::slotActivated()" << endl;
+
+ kdDebug() << "Bytes available: " << mSocket->bytesAvailable() << endl;
+ int maxLen = mSocket->bytesAvailable();
+ if ( maxLen == 0 )
+ return;
+
+ QCString response( maxLen+2 );
+ Q_LONG len = mSocket->readBlock ( response.data(), maxLen+1 );
+
+ if ( len <= 0 ) {
+ kdDebug() << "Error: len<=0" << endl;
+ kdDebug() << "Error: " << mSocket->error() << endl;
+ return;
+ }
+ kdDebug() << "Got data of " << len << " bytes." << endl;
+ kdDebug() << response << endl;
+
+ QString s(response);
+ IDList IDs;
+
+ QStringList lines = QStringList::split( "\n", s );
+ QStringList::iterator it;
+ for ( it = lines.begin(); it != lines.end(); ++it ) {
+ QString line = (*it).stripWhiteSpace().lower();
+ if ( line.startsWith( "subscription-id: " ) )
+ IDs = makeIDList( line.section(":",1).stripWhiteSpace() );
+ }
+
+ if ( IDs.isEmpty() ) {
+ kdWarning() << "Did not find any subscriptions in NOTIFY!" << response << endl;
+ } else {
+ poll( IDs );
+ }
+
+}
+
+void ExchangeMonitor::poll( const IDList& IDs ) {
+ // FIXME: Check what did subscription means
+// if ( id != mSubscriptionId ) {
+// kdDebug() << "Don't know subscription id " << id << endl;
+// }
+
+ // confirm it
+ KIO::DavJob *job = new KIO::DavJob( mAccount->calendarURL(), (int) KIO::DAV_POLL, QString::null, false );
+ job->addMetaData( "customHTTPHeader", "Subscription-ID: " + makeIDString( IDs ) );
+ connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotPollResult(KIO::Job *)));
+}
+
+void ExchangeMonitor::slotPollResult( KIO::Job * job )
+{
+ if ( job->error() ) {
+ job->showErrorDialog( 0L );
+ emit error( ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
+ return;
+ }
+ QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
+ kdDebug() << "POLL result: " << endl << response.toString() << endl;
+
+ // Multiple results!
+ QDomNodeList responses = response.documentElement().elementsByTagName( "response" );
+ if ( responses.count() == 0 ) {
+ emit error( ExchangeClient::ServerResponseError, "Poll result is wrong: \n" + response.toString() );
+ return;
+ }
+ for( uint i=0; i<responses.count(); i++ ) {
+ QDomElement item = responses.item( i ).toElement();
+ QDomElement status = item.namedItem( "status" ).toElement();
+ QDomElement subscriptionID = item.namedItem( "subscriptionID" ).toElement();
+ if ( status.text().contains( "200" ) ) {
+ kdDebug() << "subscriptionID: " << subscriptionID.text() << endl;
+ IDList IDs = makeIDList( subscriptionID.text() );
+ QValueList<KURL> urls;
+ IDList::ConstIterator it;
+ for ( it = IDs.begin(); it != IDs.end(); ++it ) {
+ urls += mSubscriptionMap[ *it ];
+ }
+ emit notify( IDs, urls );
+ } else if ( ! status.text().contains( "204" ) ) {
+ kdWarning() << "POLL result is not 200 or 204, what's up?" << endl;
+ emit error( ExchangeClient::ServerResponseError, "Poll result is wrong: \n" + response.toString() );
+ }
+ }
+}
+
+void ExchangeMonitor::slotRenewTimer()
+{
+ kdDebug() << "ExchangeMonitor::slotRenewTimer()" << endl;
+
+ KIO::DavJob *job = new KIO::DavJob( mAccount->calendarURL(), (int) KIO::DAV_SUBSCRIBE, QString::null, false );
+ job->addMetaData( "customHTTPHeader", "Subscription-id: " + makeIDString( mSubscriptionMap.keys() ) );
+ connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotRenewResult(KIO::Job *)));
+}
+
+void ExchangeMonitor::slotRenewResult( KIO::Job* job )
+{
+ if ( job->error() ) {
+ job->showErrorDialog( 0L );
+ emit error( ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
+ return;
+ }
+ kdDebug() << "ExchangeMonitor::slotRenewResult()" << endl;
+
+ // FIXME: check for new subscription lifetime
+}
+
+#include "exchangemonitor.moc"
diff --git a/libkpimexchange/core/exchangemonitor.h b/libkpimexchange/core/exchangemonitor.h
new file mode 100644
index 000000000..f1429ba4e
--- /dev/null
+++ b/libkpimexchange/core/exchangemonitor.h
@@ -0,0 +1,106 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef KDEPIM_EXCHANGE_MONITOR_H
+#define KDEPIM_EXCHANGE_MONITOR_H
+
+#include <qstring.h>
+#include <qmap.h>
+//#include <qwidget.h>
+#include <qhostaddress.h>
+
+#include <kurl.h>
+#include <kio/job.h>
+
+#include <libkcal/calendar.h>
+#include <libkcal/event.h>
+
+class QSocketDevice;
+class QSocketNotifier;
+class QTextStream;
+
+namespace KPIM {
+
+class ExchangeAccount;
+
+class ExchangeMonitor : public QObject {
+ Q_OBJECT
+ public:
+ typedef long ID;
+ typedef QValueList<ID> IDList;
+
+ enum { CallBack, Poll };
+ enum { Delete, /** Any: 0 The message or folder subscribed to was deleted.
+ Folder 1 A message or folder was deleted from the folder. */
+ Move, /** Any: 0 The message or folder was moved.
+ Folder 1 A message or folder was moved from or to the folder. */
+ Newmail, /** Mailbox or Folder Any Special new mail update.
+ Message Any Not valid - return 400 (Bad Request). */
+ Update, /** Message 0 The message was modified (either properties or body).
+ Folder 0 Properties of the folder were modified.
+ Folder 1 A message or sub-folder was created in the folder, copied to
+ the folder, moved to or from the folder, deleted from the folder,
+ modified in the folder, or the folder properties were modified. */
+ UpdateNewMember, /** Any 0 Not valid - return 400 (Bad Request).
+ Existing Folder 1 A message or sub-folder was created in the folder, copied to
+ the folder, or moved to the folder. */
+ Any /** Message 1 Treat as depth = 0. */
+ };
+
+ ExchangeMonitor( ExchangeAccount* account, int pollMode, const QHostAddress& ownInterface );
+ ~ExchangeMonitor();
+ void addWatch( const KURL &url, int mode, int depth );
+ void removeWatch( const KURL &url );
+ void removeWatch( ID id );
+
+ signals:
+ void notify( const QValueList<long>& IDs, const QValueList<KURL>& urls );
+// void added( ID id, const KURL& url );
+// void removed( ID id, const KURL& url );
+
+ void error( int result, const QString& moreInfo );
+
+ private slots:
+ void slotSubscribeResult( KIO::Job * );
+ void slotUnsubscribeResult( KIO::Job * );
+ void slotPollTimer();
+ void poll( const IDList& IDs );
+ void slotPollResult( KIO::Job * );
+ void slotRenewTimer();
+ void slotRenewResult( KIO::Job * );
+ void slotActivated(int socket);
+
+ private:
+// void init();
+
+ QMap<ID,KURL> mSubscriptionMap;
+ QSocketDevice *mSocket;
+ QSocketNotifier* mNotifier;
+ QTextStream *mStream;
+ ExchangeAccount* mAccount;
+ int mSubscriptionLifetime;
+ // QString mSubscriptionId;
+ QTimer* mPollTimer;
+ QTimer* mRenewTimer;
+ int mPollMode;
+};
+
+}
+
+#endif
diff --git a/libkpimexchange/core/exchangeprogress.cpp b/libkpimexchange/core/exchangeprogress.cpp
new file mode 100644
index 000000000..203a1a0b7
--- /dev/null
+++ b/libkpimexchange/core/exchangeprogress.cpp
@@ -0,0 +1,72 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcombobox.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <ksimpleconfig.h>
+
+#include "exchangeprogress.h"
+using namespace KPIM;
+
+ExchangeProgress::ExchangeProgress(QWidget *parent)
+ : KProgressDialog(parent, "", i18n("Exchange Download Progress"), i18n("Exchange Plugin"), "text" )
+{
+ m_finished = 0;
+ m_total = 0;
+ setAutoClose( false );
+ setLabel( i18n( "Listing appointments" ) );
+}
+
+ExchangeProgress::~ExchangeProgress()
+{
+}
+
+void ExchangeProgress::slotTransferStarted()
+{
+ m_total++;
+ progressBar()->setTotalSteps( m_total );
+ updateLabel();
+}
+
+void ExchangeProgress::slotTransferFinished()
+{
+ m_finished++;
+ updateLabel();
+ if ( m_finished == m_total ) {
+ emit complete( this );
+ }
+}
+
+void ExchangeProgress::updateLabel()
+{
+ progressBar()->setValue( m_finished );
+ QString str = i18n( "Downloading, %1 of %2" ).arg( m_finished ).arg( m_total );
+ setLabel( str );
+}
+
+#include "exchangeprogress.moc"
diff --git a/libkpimexchange/core/exchangeprogress.h b/libkpimexchange/core/exchangeprogress.h
new file mode 100644
index 000000000..c37467db1
--- /dev/null
+++ b/libkpimexchange/core/exchangeprogress.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef EXCHANGEPROGRESS_H
+#define EXCHANGEPROGRESS_H
+
+#include <kprogress.h>
+
+class QComboBox;
+
+namespace KPIM {
+
+class ExchangeProgress : public KProgressDialog
+{
+ Q_OBJECT
+
+ public:
+ ExchangeProgress(QWidget *parent=0);
+ virtual ~ExchangeProgress();
+
+ public slots:
+ void slotTransferStarted();
+ void slotTransferFinished();
+
+ signals:
+ void complete( ExchangeProgress* );
+
+ private:
+ int m_total;
+ int m_finished;
+
+ private:
+ void updateLabel();
+};
+
+}
+
+#endif
diff --git a/libkpimexchange/core/exchangeupload.cpp b/libkpimexchange/core/exchangeupload.cpp
new file mode 100644
index 000000000..8332979a0
--- /dev/null
+++ b/libkpimexchange/core/exchangeupload.cpp
@@ -0,0 +1,371 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qstring.h>
+#include <qregexp.h>
+
+#include <kurl.h>
+#include <kdebug.h>
+#include <krfcdate.h>
+#include <kio/job.h>
+
+#include <kio/slave.h>
+#include <kio/scheduler.h>
+#include <kio/slavebase.h>
+#include <kio/davjob.h>
+#include <kio/http.h>
+
+extern "C" {
+ #include <ical.h>
+}
+
+#include <libkcal/event.h>
+#include <libkcal/icalformat.h>
+#include <libkcal/icalformatimpl.h>
+#include <libkcal/recurrence.h>
+#include <libkcal/incidence.h>
+#include <libkcal/event.h>
+
+#include "exchangeclient.h"
+#include "exchangeprogress.h"
+#include "exchangeupload.h"
+#include "exchangeaccount.h"
+#include "utils.h"
+
+using namespace KPIM;
+
+ExchangeUpload::ExchangeUpload( KCal::Event *event, ExchangeAccount *account,
+ const QString &timeZoneId, QWidget *window )
+ : mTimeZoneId( timeZoneId ), mWindow( window )
+{
+ kdDebug() << "Called ExchangeUpload" << endl;
+
+ mAccount = account;
+ m_currentUpload = event;
+ m_currentUploadNumber = 0;
+
+// kdDebug() << "Trying to add appointment " << m_currentUpload->summary() << endl;
+
+ // TODO: For exisiting events the URL for the uid should already be known.
+ // Store it after downloading and keep the mapping
+
+ findUid( m_currentUpload->uid() );
+}
+
+ExchangeUpload::~ExchangeUpload()
+{
+ kdDebug() << "Entering ExchangeUpload destructor" << endl;
+ kdDebug() << "Finished ExchangeUpload destructor" << endl;
+}
+
+void ExchangeUpload::findUid( QString const &uid )
+{
+ QString query =
+ "SELECT \"DAV:href\", \"urn:schemas:calendar:uid\"\r\n"
+ "FROM Scope('shallow traversal of \"\"')\r\n"
+ "WHERE \"urn:schemas:calendar:uid\" = '" + uid + "'\r\n";
+
+ kdDebug() << "Find uid query: " << endl << query << endl;
+ kdDebug() << "Looking for uid " << uid << endl;
+
+ KIO::DavJob* job = KIO::davSearch( mAccount->calendarURL(), "DAV:", "sql",
+ query, false );
+ job->setWindow( mWindow );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotFindUidResult( KIO::Job * ) ) );
+}
+
+void ExchangeUpload::slotFindUidResult( KIO::Job * job )
+{
+ kdDebug() << "slotFindUidResult()" << endl;
+
+ if ( job->error() ) {
+ kdDebug() << "Error: " << job->error() << endl;
+ job->showErrorDialog( 0 );
+ emit finished( this, ExchangeClient::CommunicationError,
+ "IO Error: " + QString::number(job->error()) + ":" +
+ job->errorString() );
+ return;
+ }
+ QDomDocument &response = static_cast<KIO::DavJob *>( job )->response();
+
+ kdDebug() << "Search uid result: " << endl << response.toString() << endl;
+
+ QDomElement item = response.documentElement().firstChild().toElement();
+ QDomElement hrefElement = item.namedItem( "href" ).toElement();
+ if ( item.isNull() || hrefElement.isNull() ) {
+ // No appointment with this UID in exchange database
+ // Create a new filename for this appointment and store it there
+ tryExist();
+ return;
+ }
+ // The appointment is already in the exchange database
+ // Overwrite it with the new data
+ QString href = hrefElement.text();
+ KURL url( href );
+ kdDebug() << "Found URL with identical uid: " << url.prettyURL()
+ << ", overwriting that one" << endl;
+
+ startUpload( toDAV( url ) );
+}
+
+void ExchangeUpload::tryExist()
+{
+ // FIXME: we should first check if current's uid is already in the Exchange database
+ // Maybe use locking?
+ KURL url = mAccount->calendarURL();
+ if ( m_currentUploadNumber == 0 )
+ url.addPath( m_currentUpload->summary() + ".EML" );
+ else
+ url.addPath( m_currentUpload->summary() + "-" + QString::number( m_currentUploadNumber ) + ".EML" );
+
+ kdDebug() << "Trying to see whether " << url.prettyURL() << " exists" << endl;
+
+ QDomDocument doc;
+ QDomElement root = addElement( doc, doc, "DAV:", "propfind" );
+ QDomElement prop = addElement( doc, root, "DAV:", "prop" );
+ addElement( doc, prop, "DAV:", "displayname" );
+ addElement( doc, prop, "urn:schemas:calendar", "uid" );
+
+ KIO::DavJob *job = KIO::davPropFind( url, doc, "0", false );
+ job->setWindow( mWindow );
+ job->addMetaData( "errorPage", "false" );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotPropFindResult( KIO::Job * ) ) );
+}
+
+void ExchangeUpload::slotPropFindResult( KIO::Job *job )
+{
+ kdDebug() << "slotPropFindResult()" << endl;
+ int error = job->error();
+ kdDebug() << "PROPFIND error: " << error << ":" << job->errorString() << endl;
+ if ( error && error != KIO::ERR_DOES_NOT_EXIST ) {
+ job->showErrorDialog( 0 );
+ emit finished( this, ExchangeClient::CommunicationError,
+ "IO Error: " + QString::number(error) + ":" +
+ job->errorString() );
+ return;
+ }
+
+ if ( !error ) {
+ // File exist, try another one
+ m_currentUploadNumber++;
+ tryExist();
+ return;
+ }
+
+ // We got a 404 error, resource doesn't exist yet, create it
+ // FIXME: race condition possible if resource is created under
+ // our nose.
+
+ KURL url = mAccount->calendarURL();
+ if ( m_currentUploadNumber == 0 )
+ url.addPath( m_currentUpload->summary() + ".EML" );
+ else
+ url.addPath( m_currentUpload->summary() + "-" +
+ QString::number( m_currentUploadNumber ) + ".EML" );
+
+ startUpload( url );
+}
+
+QString timezoneid( int offset )
+{
+ switch ( offset ) {
+ case 0: return "0";
+ case -60: return "3";
+ case -120: return "5";
+ case -180: return "51";
+ case -210: return "25";
+ case -240: return "24"; // Abu Dhabi
+ case -270: return "48"; // Kabul
+ case -300: return "47"; // Islamabad
+ case -330: return "23"; // Bombay
+ case -360: return "46"; // Dhaka
+ case -420: return "22"; // Bangkok
+ case -480: return "45"; // Beijing
+ case -540: return "20"; // Tokyo
+ case -570: return "44"; // Darwin
+ case -600: return "18"; // Brisbane
+ case -660: return "41"; // Solomon Islands
+ case -720: return "17"; // Auckland
+ case 60: return "29"; // Azores
+ case 120: return "30"; // Mid Atlantic
+ case 180: return "8"; // Brasilia
+ case 210: return "28"; // Newfoundland
+ case 240: return "9"; // Atlantic time Canada
+ case 300: return "10"; // Eastern
+ case 360: return "11"; // Central time
+ case 420: return "12"; // Mountain time
+ case 480: return "13"; // Pacific time
+ case 540: return "14"; // Alaska time
+ case 600: return "15"; // Hawaii
+ case 660: return "16"; // Midway Island
+ case 720: return "39"; // Eniwetok
+ default: return "52"; // Invalid time zone
+ }
+}
+
+
+void ExchangeUpload::startUpload( const KURL &url )
+{
+ KCal::Event *event = static_cast<KCal::Event *>( m_currentUpload );
+ if ( ! event ) {
+ kdDebug() << "ERROR: trying to upload a non-Event Incidence" << endl;
+ emit finished( this, ExchangeClient::NonEventError, "The incidence that is to be uploaded to the exchange server is not of type KCal::Event" );
+ return;
+ }
+
+ QDomDocument doc;
+ QDomElement root = addElement( doc, doc, "DAV:", "propertyupdate" );
+ QDomElement set = addElement( doc, root, "DAV:", "set" );
+ QDomElement prop = addElement( doc, set, "DAV:", "prop" );
+ addElement( doc, prop, "DAV:", "contentclass", "urn:content-classes:appointment" );
+// addElement( doc, prop, "http://schemas.microsoft.com/exchange/", "outlookmessageclass", "IPM.appointment" );
+ addElement( doc, prop, "http://schemas.microsoft.com/exchange/",
+ "outlookmessageclass", "IPM.Appointment" );
+ // addElement( doc, prop, "urn:schemas:calendar:", "method", "Add" );
+ addElement( doc, prop, "urn:schemas:calendar:", "alldayevent",
+ event->doesFloat() ? "1" : "0" );
+ addElement( doc, prop, "urn:schemas:calendar:", "busystatus",
+ event->transparency() ? "Free" : "Busy" );
+ // KLUDGE: somehow we need to take the opposite of the
+ // value that localUTCOffset() supplies...
+ // FIXME: What do we need that offset for anyway???
+ int tzOffset = - KRFCDate::localUTCOffset();
+ QString offsetString;
+ if ( tzOffset == 0 )
+ offsetString = "Z";
+ else if ( tzOffset > 0 )
+ offsetString = QString( "+%1:%2" ).arg(tzOffset/60, 2).arg( tzOffset%60, 2 );
+ else
+ offsetString = QString( "-%1:%2" ).arg((-tzOffset)/60, 2).arg( (-tzOffset)%60, 2 );
+ offsetString = offsetString.replace( QRegExp(" "), "0" );
+
+ kdDebug() << "Timezone offset: " << tzOffset << " : " << offsetString << endl;
+ kdDebug() << "ExchangeUpload::mTimeZoneId=" << mTimeZoneId << endl;
+
+ addElement( doc, prop, "urn:schemas:calendar:", "dtstart",
+ zoneAsUtc( event->dtStart(), mTimeZoneId ).toString( Qt::ISODate ) + "Z" );
+ // event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzzZ" ) );
+ // 2002-06-04T08:00:00.000Z" );
+ addElement( doc, prop, "urn:schemas:calendar:", "dtend",
+ zoneAsUtc( event->dtEnd(), mTimeZoneId ).toString( Qt::ISODate ) + "Z" );
+#if 0
+ addElement( doc, prop, "urn:schemas:calendar:", "dtstart",
+ event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzz" )+ offsetString );
+ // event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzzZ" ) );
+ // 2002-06-04T08:00:00.000Z" );
+ addElement( doc, prop, "urn:schemas:calendar:", "dtend",
+ event->dtEnd().toString( "yyyy-MM-ddThh:mm:ss.zzz" ) + offsetString );
+#endif
+ addElement( doc, prop, "urn:schemas:calendar:", "lastmodified", zoneAsUtc( event->lastModified(), mTimeZoneId ).toString( Qt::ISODate )+"Z" );
+
+// addElement( doc, prop, "urn:schemas:calendar:", "meetingstatus", "confirmed" );
+ addElement( doc, prop, "urn:schemas:httpmail:", "textdescription", event->description() );
+ addElement( doc, prop, "urn:schemas:httpmail:", "subject", event->summary() );
+ addElement( doc, prop, "urn:schemas:calendar:", "location", event->location() );
+ // addElement( doc, prop, "urn:schemas:mailheader:", "subject", event->summary() );
+ addElement( doc, prop, "urn:schemas:calendar:", "uid", event->uid() );
+// addElement( doc, prop, "urn:schemas:calendar:", "organizer", event->organizer() );
+
+ KCal::Recurrence *recurrence = event->recurrence();
+ kdDebug() << "Recurrence->doesRecur(): " << recurrence->doesRecur() << endl;
+ if ( recurrence->recurrenceType() != KCal::Recurrence::rNone ) {
+ addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "1" );
+ KCal::ICalFormat *format = new KCal::ICalFormat();
+ QString recurstr = format->toString( recurrence->defaultRRule() );
+ // Strip leading "RRULE\n :" and whitespace
+ recurstr = recurstr.replace( QRegExp("^[A-Z]*[\\s]*:"), "").stripWhiteSpace();
+ kdDebug() << "Recurrence rule after replace: \"" << recurstr << "\"" << endl;
+ delete format;
+ QDomElement rrule = addElement( doc, prop, "urn:schemas:calendar:", "rrule" );
+ addElement( doc, rrule, "xml:", "v", recurstr );
+ addElement( doc, prop, "urn:schemas:calendar:", "timezoneid", timezoneid( tzOffset ) );
+ } else {
+ addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "0" );
+ }
+
+ KCal::DateList exdates = recurrence->exDates();
+ if ( !exdates.isEmpty() ) {
+ QDomElement exdate = addElement( doc, prop, "urn:schemas:calendar:", "exdate" );
+ KCal::DateList::iterator it;
+ for ( it = exdates.begin(); it != exdates.end(); ++it ) {
+ QString date = (*it).toString( "yyyy-MM-ddT00:00:00.000" )+ offsetString;
+// QString date = zoneAsUtc( (*it), mTimeZoneId ).toString( Qt::ISODate );
+ addElement( doc, exdate, "xml:", "v", date );
+ }
+ }
+
+ KCal::Alarm::List alarms = event->alarms();
+ if ( alarms.count() > 0 ) {
+ KCal::Alarm* alarm = alarms.first();
+ // TODO: handle multiple alarms
+ // TODO: handle end offsets and general alarm times
+ // TODO: handle alarm types
+ if ( alarm->hasStartOffset() ) {
+ int offset = - alarm->startOffset().asSeconds();
+ addElement( doc, prop, "urn:schemas:calendar:", "reminderoffset", QString::number( offset ) );
+ }
+ }
+
+ kdDebug() << "Uploading event: " << endl;
+ kdDebug() << doc.toString() << endl;
+
+ kdDebug() << "Upload url: " << url << endl;
+
+ KIO::DavJob *job = KIO::davPropPatch( url, doc, false );
+ job->setWindow( mWindow );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotPatchResult( KIO::Job * ) ) );
+}
+
+void ExchangeUpload::slotPatchResult( KIO::Job *job )
+{
+ kdDebug() << "slotPropPatchResult()" << endl;
+ if ( job->error() ) {
+ job->showErrorDialog( 0 );
+ kdDebug() << "Error: " << job->error() << endl;
+ emit finished( this, ExchangeClient::CommunicationError,
+ "IO Error: " + QString::number(job->error()) + ":" +
+ job->errorString() );
+ return;
+ }
+ QDomDocument response = static_cast<KIO::DavJob *>( job )->response();
+ kdDebug() << "Patch result: " << response.toString() << endl;
+
+ // Either we have a "201 Created" (if a new event has been created) or
+ // we have a "200 OK" (if an existing event has been altered),
+ // or else an error has occurred ;)
+ QDomElement status = response.documentElement().namedItem( "response" )
+ .namedItem( "status" ).toElement();
+ QDomElement propstat = response.documentElement().namedItem( "response" )
+ .namedItem( "propstat" ).namedItem( "status" )
+ .toElement();
+ kdDebug() << "status: " << status.text() << endl;
+ kdDebug() << "propstat: " << propstat.text() << endl;
+ if ( ! ( status.text().contains( "201" ) ||
+ propstat.text().contains( "200" ) ) )
+ emit finished( this, ExchangeClient::EventWriteError,
+ "Upload error response: \n" + response.toString() );
+ else
+ emit finished( this, ExchangeClient::ResultOK, QString::null );
+}
+
+#include "exchangeupload.moc"
diff --git a/libkpimexchange/core/exchangeupload.h b/libkpimexchange/core/exchangeupload.h
new file mode 100644
index 000000000..5ed269297
--- /dev/null
+++ b/libkpimexchange/core/exchangeupload.h
@@ -0,0 +1,66 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef KDEPIM_EXCHANGE_UPLOAD_H
+#define KDEPIM_EXCHANGE_UPLOAD_H
+
+#include <qstring.h>
+#include <qwidget.h>
+#include <kio/job.h>
+
+#include <kdepimmacros.h>
+
+#include <libkcal/calendar.h>
+#include <libkcal/event.h>
+
+namespace KPIM {
+
+class ExchangeAccount;
+
+class KDE_EXPORT ExchangeUpload : public QObject {
+ Q_OBJECT
+ public:
+ ExchangeUpload( KCal::Event* event, ExchangeAccount* account, const QString& timeZoneId, QWidget* window=0 );
+ ~ExchangeUpload();
+
+ private slots:
+ void slotPatchResult( KIO::Job * );
+ void slotPropFindResult( KIO::Job * );
+ void slotFindUidResult( KIO::Job * );
+
+ signals:
+ void startDownload();
+ void finishDownload();
+ void finished( ExchangeUpload* worker, int result, const QString& moreInfo );
+
+ private:
+ void tryExist();
+ void startUpload( const KURL& url );
+ void findUid( QString const& uid );
+
+ ExchangeAccount* mAccount;
+ KCal::Event* m_currentUpload;
+ int m_currentUploadNumber;
+ QString mTimeZoneId;
+ QWidget* mWindow;
+};
+
+}
+
+#endif
diff --git a/libkpimexchange/core/utils.cpp b/libkpimexchange/core/utils.cpp
new file mode 100644
index 000000000..1d90cfcca
--- /dev/null
+++ b/libkpimexchange/core/utils.cpp
@@ -0,0 +1,100 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qdatetime.h>
+
+extern "C" {
+ #include <ical.h>
+}
+
+#include "utils.h"
+
+/** In a document doc with node node, add an element with name ns and tagname tag. Return the new element
+ */
+QDomElement addElement( QDomDocument& doc, QDomNode& node, const QString& ns, const QString& tag )
+{
+ QDomElement el = doc.createElementNS( ns, tag );
+ node.appendChild( el );
+ return el;
+}
+
+/**
+ In a document doc with node node, add an element with namespace ns and tagname tag. Add a textnode in
+ the element with text contents text. Return the new element.
+ */
+QDomElement addElement( QDomDocument& doc, QDomNode& node, const QString& ns, const QString& tag, const QString& text )
+{
+ QDomElement el = doc.createElementNS( ns, tag );
+ QDomText textnode = doc.createTextNode( text );
+ el.appendChild( textnode );
+ node.appendChild( el );
+ return el;
+}
+
+//TODO: should not call libical functions directly -- better to make
+// a new libkcal abstraction method.
+QDateTime utcAsZone( const QDateTime& utc, const QString& timeZoneId )
+{
+ int daylight;
+ QDateTime epoch;
+ epoch.setTime_t( 0 );
+ time_t v = epoch.secsTo( utc );
+ struct icaltimetype tt = icaltime_from_timet( v, 0 ); // 0: is_date=false
+ int offset = icaltimezone_get_utc_offset(
+ icaltimezone_get_builtin_timezone( timeZoneId.latin1() ),
+ &tt, &daylight );
+
+ return utc.addSecs( offset );
+}
+
+//TODO: should not call libical functions directly -- better to make
+// a new libkcal abstraction method.
+QDateTime zoneAsUtc( const QDateTime& zone, const QString& timeZoneId )
+{
+ int daylight;
+ QDateTime epoch;
+ epoch.setTime_t( 0 );
+ time_t v = epoch.secsTo( zone );
+ struct icaltimetype tt = icaltime_from_timet( v, 0 ); // 0: is_date=false
+ int offset = icaltimezone_get_utc_offset(
+ icaltimezone_get_builtin_timezone( timeZoneId.latin1() ),
+ &tt, &daylight );
+
+ return zone.addSecs( - offset );
+}
+
+KURL toDAV( const KURL& url ) {
+ KURL result( url );
+ if ( result.protocol() == "http" )
+ result.setProtocol( "webdav" );
+ else if ( result.protocol() == "https" )
+ result.setProtocol( "webdavs" );
+ return result;
+}
+
+KURL* toDAV( const KURL* url ) {
+ KURL* result = new KURL( *url );
+ if ( result->protocol() == "http" )
+ result->setProtocol( "webdav" );
+ else if ( result->protocol() == "https" )
+ result->setProtocol( "webdavs" );
+ return result;
+}
+
diff --git a/libkpimexchange/core/utils.h b/libkpimexchange/core/utils.h
new file mode 100644
index 000000000..20b681416
--- /dev/null
+++ b/libkpimexchange/core/utils.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of libkpimexchange
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDEPIM_EXCHANGE_UTILS_H
+#define KDEPIM_EXCHANGE_UTILS_H
+
+#include <qstring.h>
+#include <qdom.h>
+
+#include <kurl.h>
+
+/** In a document doc with node node, add an element with name ns and tagname tag. Return the new element
+ */
+QDomElement addElement( QDomDocument& doc, QDomNode& node, const QString& ns, const QString& tag );
+
+/**
+ In a document doc with node node, add an element with namespace ns and tagname tag. Add a textnode in
+ the element with text contents text. Return the new element.
+ */
+QDomElement addElement( QDomDocument& doc, QDomNode& node, const QString& ns, const QString& tag, const QString& text );
+
+/**
+ * Return the representation of utc time in the time zone indicated by timeZoneId
+ */
+QDateTime utcAsZone( const QDateTime& utc, const QString& timeZoneId );
+
+/**
+ * Return the UTC representation of local time in the time zone indicated by timeZoneId
+ */
+QDateTime zoneAsUtc( const QDateTime& zone, const QString& timeZoneId );
+
+/**
+ * Convert http:// url to webdav:// and https:// to webdavs://
+ */
+KURL toDAV( const KURL& url );
+KURL* toDAV( const KURL* url );
+
+#endif
+