libkpimexchange Library API Documentation

exchangeupload.cpp

00001 /*
00002     This file is part of libkpimexchange
00003     Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018     Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include <qstring.h>
00022 #include <qregexp.h>
00023 
00024 #include <kurl.h>
00025 #include <kdebug.h>
00026 #include <krfcdate.h>
00027 #include <kio/job.h>
00028 
00029 #include <kio/slave.h>
00030 #include <kio/scheduler.h>
00031 #include <kio/slavebase.h>
00032 #include <kio/davjob.h>
00033 #include <kio/http.h>
00034 
00035 extern "C" {
00036   #include <ical.h>
00037 }
00038 
00039 #include <libkcal/event.h>
00040 #include <libkcal/icalformat.h>
00041 #include <libkcal/icalformatimpl.h>
00042 #include <libkcal/recurrence.h>
00043 #include <libkcal/incidence.h>
00044 #include <libkcal/event.h>
00045 
00046 #include "exchangeclient.h"
00047 #include "exchangeprogress.h"
00048 #include "exchangeupload.h"
00049 #include "exchangeaccount.h"
00050 #include "utils.h"
00051 
00052 using namespace KPIM;
00053 
00054 ExchangeUpload::ExchangeUpload( KCal::Event* event, ExchangeAccount* account, const QString& timeZoneId, QWidget* window ) :
00055   mTimeZoneId( timeZoneId), mWindow( window )
00056 {
00057   kdDebug() << "Called ExchangeUpload" << endl;
00058 
00059   mAccount = account;
00060   m_currentUpload = event;
00061   m_currentUploadNumber = 0;
00062 
00063 //  kdDebug() << "Trying to add appointment " << m_currentUpload->summary() << endl;
00064 
00065   findUid( m_currentUpload->uid() );
00066 }
00067 
00068 ExchangeUpload::~ExchangeUpload()
00069 {
00070   kdDebug() << "Entering ExchangeUpload destructor" << endl;
00071   kdDebug() << "Finished ExchangeUpload destructor" << endl;
00072 }
00073 
00074 void ExchangeUpload::findUid( QString const& uid )
00075 {
00076   QString query = 
00077         "SELECT \"DAV:href\", \"urn:schemas:calendar:uid\"\r\n"
00078         "FROM Scope('shallow traversal of \"\"')\r\n"
00079         "WHERE \"urn:schemas:calendar:uid\" = '" + uid + "'\r\n";
00080 
00081 //  kdDebug() << "Find uid query: " << endl << query << endl;
00082   kdDebug() << "Looking for uid " << uid << endl;
00083   
00084   KIO::DavJob* job = KIO::davSearch( mAccount->calendarURL(), "DAV:", "sql", query, false );
00085   job->setWindow( mWindow );
00086   connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotFindUidResult(KIO::Job *)));
00087 }
00088 
00089 void ExchangeUpload::slotFindUidResult( KIO::Job * job )
00090 {
00091   kdDebug() << "slotFindUidResult()" << endl;
00092   if ( job->error() ) {
00093     kdDebug() << "Error: " << job->error() << endl;
00094     job->showErrorDialog( 0L );
00095     emit finished( this, ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
00096     return;
00097   }
00098   QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
00099 
00100 //  kdDebug() << "Search uid result: " << endl << response.toString() << endl;
00101 
00102   QDomElement item = response.documentElement().firstChild().toElement();
00103   QDomElement hrefElement = item.namedItem( "href" ).toElement();
00104   if ( item.isNull() || hrefElement.isNull() ) {
00105     // No appointment with this UID in exchange database
00106     // Create a new filename for this appointment and store it there
00107     tryExist();
00108     return;
00109   }
00110   // The appointment is already in the exchange database
00111   // Overwrite it with the new data
00112   QString href = hrefElement.text();
00113   KURL url(href);
00114   kdDebug() << "Found URL with identical uid: " << url.prettyURL() << ", overwriting that one" << endl;
00115 
00116   startUpload( toDAV( url ) );  
00117 }  
00118 
00119 void ExchangeUpload::tryExist()
00120 {
00121   // FIXME: we should first check if current's uid is already in the Exchange database
00122   // Maybe use locking?
00123   KURL url = mAccount->calendarURL();
00124   if ( m_currentUploadNumber == 0 )
00125     url.addPath( m_currentUpload->summary() + ".EML" );
00126   else
00127     url.addPath( m_currentUpload->summary() + "-" + QString::number( m_currentUploadNumber ) + ".EML" );
00128 
00129   kdDebug() << "Trying to see whether " << url.prettyURL() << " exists" << endl;
00130  
00131   QDomDocument doc;
00132   QDomElement root = addElement( doc, doc, "DAV:", "propfind" );
00133   QDomElement prop = addElement( doc, root, "DAV:", "prop" );
00134   addElement( doc, prop, "DAV:", "displayname" );
00135   addElement( doc, prop, "urn:schemas:calendar", "uid" );
00136 
00137   KIO::DavJob* job = KIO::davPropFind( url, doc, "0", false );
00138   job->setWindow( mWindow );
00139   job->addMetaData( "errorPage", "false" );
00140   connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotPropFindResult( KIO::Job * ) ) );
00141 }
00142 
00143 void ExchangeUpload::slotPropFindResult( KIO::Job *job )
00144 {
00145   kdDebug() << "slotPropFindResult()" << endl;
00146   int error = job->error(); 
00147   kdDebug() << "PROPFIND error: " << error << ":" << job->errorString() << endl;
00148   if ( error && error != KIO::ERR_DOES_NOT_EXIST )
00149   {
00150     job->showErrorDialog( 0L );
00151     emit finished( this, ExchangeClient::CommunicationError, "IO Error: " + QString::number(error) + ":" + job->errorString() );
00152     return;
00153   }
00154 
00155   if ( !error ) 
00156   {
00157     // File exist, try another one
00158     m_currentUploadNumber++;
00159     tryExist();
00160     return;
00161   }
00162 
00163   // We got a 404 error, resource doesn't exist yet, create it
00164   // FIXME: race condition possible if resource is created under
00165   // our nose.
00166 
00167   KURL url = mAccount->calendarURL();
00168   if ( m_currentUploadNumber == 0 )
00169     url.addPath( m_currentUpload->summary() + ".EML" );
00170   else
00171     url.addPath( m_currentUpload->summary() + "-" + QString::number( m_currentUploadNumber ) + ".EML" );
00172 
00173   startUpload( url );
00174 }
00175 
00176 QString timezoneid( int offset ) {
00177   switch ( offset ) {
00178     case 0: return "0";
00179     case -60: return "3";
00180     case -120: return "5";
00181     case -180: return "51";
00182     case -210: return "25";
00183     case -240: return "24"; // Abu Dhabi
00184     case -270: return "48"; // Kabul
00185     case -300: return "47"; // Islamabad
00186     case -330: return "23"; // Bombay
00187     case -360: return "46"; // Dhaka
00188     case -420: return "22"; // Bangkok
00189     case -480: return "45"; // Beijing
00190     case -540: return "20"; // Tokyo
00191     case -570: return "44"; // Darwin
00192     case -600: return "18"; // Brisbane
00193     case -660: return "41"; // Solomon Islands
00194     case -720: return "17"; // Auckland
00195     case 60: return "29"; // Azores
00196     case 120: return "30"; // Mid Atlantic
00197     case 180: return "8"; // Brasilia
00198     case 210: return "28";  // Newfoundland
00199     case 240: return "9"; // Atlantic time Canada
00200     case 300: return "10"; // Eastern
00201     case 360: return "11"; // Central time
00202     case 420: return "12"; // Mountain time
00203     case 480: return "13"; // Pacific time
00204     case 540: return "14"; // Alaska time
00205     case 600: return "15"; // Hawaii
00206     case 660: return "16"; // Midway Island
00207     case 720: return "39"; // Eniwetok
00208     default: return "52"; // Invalid time zone
00209   }
00210 }
00211 
00212 
00213 void ExchangeUpload::startUpload( const KURL& url )
00214 {
00215   KCal::Event* event = static_cast<KCal::Event *>( m_currentUpload );
00216   if ( ! event ) {
00217     kdDebug() << "ERROR: trying to upload a non-Event Incidence" << endl;
00218     emit finished( this, ExchangeClient::NonEventError, "The incidence that is to be uploaded to the exchange server is not of type KCal::Event" );
00219     return;
00220   }
00221 
00222   QDomDocument doc;
00223   QDomElement root = addElement( doc, doc, "DAV:", "propertyupdate" );
00224   QDomElement set = addElement( doc, root, "DAV:", "set" );
00225   QDomElement prop = addElement( doc, set, "DAV:", "prop" );
00226   addElement( doc, prop, "DAV:", "contentclass", "urn:content-classes:appointment" );
00227   addElement( doc, prop, "http://schemas.microsoft.com/exchange/", "outlookmessageclass", "IPM.appointment" );
00228  // addElement( doc, prop, "urn:schemas:calendar:", "method", "Add" );
00229   addElement( doc, prop, "urn:schemas:calendar:", "alldayevent", 
00230       event->doesFloat() ? "1" : "0" );
00231   addElement( doc, prop, "urn:schemas:calendar:", "busystatus", 
00232       event->transparency() ? "Free" : "Busy" );
00233   // KLUDGE: somehow we need to take the opposite of the
00234   // value that localUTCOffset() supplies...
00235   int tzOffset = - KRFCDate::localUTCOffset(); 
00236   QString offsetString;
00237   if ( tzOffset==0 ) 
00238     offsetString = "Z";
00239   else if ( tzOffset > 0 ) 
00240     offsetString = QString( "+%1:%2" ).arg(tzOffset/60, 2).arg( tzOffset%60, 2 );
00241   else
00242     offsetString = QString( "-%1:%2" ).arg((-tzOffset)/60, 2).arg( (-tzOffset)%60, 2 );
00243   offsetString = offsetString.replace( QRegExp(" "), "0" );
00244 
00245   kdDebug() << "Timezone offset: " << tzOffset << " : " << offsetString << endl;
00246 
00247   addElement( doc, prop, "urn:schemas:calendar:", "dtstart", 
00248       event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzz" )+ offsetString );
00249   //    event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzzZ" ) );
00250   //    2002-06-04T08:00:00.000Z" );
00251   addElement( doc, prop, "urn:schemas:calendar:", "dtend", 
00252       event->dtEnd().toString( "yyyy-MM-ddThh:mm:ss.zzz" ) + offsetString );
00253   addElement( doc, prop, "urn:schemas:calendar:", "lastmodified", zoneAsUtc( event->lastModified(), mTimeZoneId ).toString( Qt::ISODate )+"Z" );
00254 
00255 //  addElement( doc, prop, "urn:schemas:calendar:", "meetingstatus", "confirmed" );
00256   addElement( doc, prop, "urn:schemas:httpmail:", "textdescription", event->description() );
00257   addElement( doc, prop, "urn:schemas:httpmail:", "subject", event->summary() );
00258   addElement( doc, prop, "urn:schemas:calendar:", "location", event->location() );
00259   // addElement( doc, prop, "urn:schemas:mailheader:", "subject", event->summary() );
00260   addElement( doc, prop, "urn:schemas:calendar:", "uid", event->uid() );
00261 //  addElement( doc, prop, "urn:schemas:calendar:", "organizer", event->organizer() );
00262 
00263   KCal::Recurrence *recurrence = event->recurrence();
00264   kdDebug() << "Recurrence->doesRecur(): " << recurrence->doesRecur() << endl;
00265   if ( recurrence->doesRecur() != KCal::Recurrence::rNone ) {
00266     addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "1" );
00267     KCal::ICalFormat *format = new KCal::ICalFormat();
00268     QString recurstr = format->toString( recurrence );
00269     // Strip leading "RRULE\n :" and whitespace
00270     recurstr = recurstr.replace( QRegExp("^[A-Z]*[\\s]*:"), "").stripWhiteSpace();
00271     kdDebug() << "Recurrence rule after replace: \"" << recurstr << "\"" << endl;
00272     delete format;
00273     QDomElement rrule = addElement( doc, prop, "urn:schemas:calendar:", "rrule" );
00274     addElement( doc, rrule, "xml:", "v", recurstr );
00275     addElement( doc, prop, "urn:schemas:calendar:", "timezoneid", timezoneid( tzOffset ) );
00276   } else {
00277     addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "0" );
00278   }
00279 
00280   KCal::DateList exdates = event->exDates();
00281   if ( !exdates.isEmpty() ) {
00282     QDomElement exdate = addElement( doc, prop, "urn:schemas:calendar:", "exdate" );
00283     KCal::DateList::iterator it;
00284     for ( it = exdates.begin(); it != exdates.end(); ++it ) {
00285       QString date = (*it).toString( "yyyy-MM-ddT00:00:00.000" )+ offsetString;
00286 //      QString date = zoneAsUtc( (*it), mTimeZoneId ).toString( Qt::ISODate );
00287       addElement( doc, exdate, "xml:", "v", date );
00288     }
00289   }
00290 
00291   KCal::Alarm::List alarms = event->alarms();
00292   if ( alarms.count() > 0 ) {
00293     KCal::Alarm* alarm = alarms.first();
00294     // TODO: handle multiple alarms
00295     // TODO: handle end offsets and general alarm times
00296     // TODO: handle alarm types
00297     if ( alarm->hasStartOffset() ) {
00298       int offset = - alarm->startOffset().asSeconds();
00299       addElement( doc, prop, "urn:schemas:calendar:", "reminderoffset", QString::number( offset ) );
00300     }
00301   }
00302 
00303   kdDebug() << "Uploading event: " << endl;
00304   kdDebug() << doc.toString() << endl;
00305 
00306   KIO::DavJob *job = KIO::davPropPatch( url, doc, false );
00307   job->setWindow( mWindow );
00308   connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotPatchResult( KIO::Job * ) ) );
00309 }
00310 
00311 void ExchangeUpload::slotPatchResult( KIO::Job* job )
00312 {
00313   kdDebug() << "slotPropPatchResult()" << endl;
00314   if ( job->error() ) {
00315     job->showErrorDialog( 0L );
00316     kdDebug() << "Error: " << job->error() << endl;
00317     emit finished( this, ExchangeClient::CommunicationError, "IO Error: " + QString::number(job->error()) + ":" + job->errorString() );
00318     return;
00319   }
00320 //  kdDebug() << "Patch result" << endl;
00321   QDomDocument response = static_cast<KIO::DavJob *>( job )->response();
00322 //  kdDebug() << response.toString() << endl;
00323 
00324   // Either we have a "201 Created" (if a new event has been created) or 
00325   // we have a "200 OK" (if an existing event has been altered), 
00326   // or else an error has occurred ;)
00327   QDomElement status = response.documentElement().namedItem( "response" ).namedItem( "status" ).toElement();
00328   QDomElement propstat = response.documentElement().namedItem( "response" ).namedItem( "propstat" ).namedItem( "status" ).toElement();
00329   kdDebug() << "status: " << status.text() << endl;
00330   kdDebug() << "propstat: " << propstat.text() << endl;
00331   if ( ! ( status.text().contains( "201" ) || 
00332            propstat.text().contains( "200" ) ) )
00333     emit finished( this, ExchangeClient::EventWriteError, "Upload error response: \n" + response.toString() ); 
00334   else 
00335     emit finished( this, ExchangeClient::ResultOK, QString::null );
00336 }
00337 
00338 #include "exchangeupload.moc"
KDE Logo
This file is part of the documentation for libkpimexchange Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:38:10 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003