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, 00055 const QString &timeZoneId, QWidget *window ) 00056 : mTimeZoneId( timeZoneId ), mWindow( window ) 00057 { 00058 kdDebug() << "Called ExchangeUpload" << endl; 00059 00060 mAccount = account; 00061 m_currentUpload = event; 00062 m_currentUploadNumber = 0; 00063 00064 // kdDebug() << "Trying to add appointment " << m_currentUpload->summary() << endl; 00065 00066 // TODO: For exisiting events the URL for the uid should already be known. 00067 // Store it after downloading and keep the mapping 00068 00069 findUid( m_currentUpload->uid() ); 00070 } 00071 00072 ExchangeUpload::~ExchangeUpload() 00073 { 00074 kdDebug() << "Entering ExchangeUpload destructor" << endl; 00075 kdDebug() << "Finished ExchangeUpload destructor" << endl; 00076 } 00077 00078 void ExchangeUpload::findUid( QString const &uid ) 00079 { 00080 QString query = 00081 "SELECT \"DAV:href\", \"urn:schemas:calendar:uid\"\r\n" 00082 "FROM Scope('shallow traversal of \"\"')\r\n" 00083 "WHERE \"urn:schemas:calendar:uid\" = '" + uid + "'\r\n"; 00084 00085 kdDebug() << "Find uid query: " << endl << query << endl; 00086 kdDebug() << "Looking for uid " << uid << endl; 00087 00088 KIO::DavJob* job = KIO::davSearch( mAccount->calendarURL(), "DAV:", "sql", 00089 query, false ); 00090 job->setWindow( mWindow ); 00091 connect( job, SIGNAL( result( KIO::Job * ) ), 00092 SLOT( slotFindUidResult( KIO::Job * ) ) ); 00093 } 00094 00095 void ExchangeUpload::slotFindUidResult( KIO::Job * job ) 00096 { 00097 kdDebug() << "slotFindUidResult()" << endl; 00098 00099 if ( job->error() ) { 00100 kdDebug() << "Error: " << job->error() << endl; 00101 job->showErrorDialog( 0 ); 00102 emit finished( this, ExchangeClient::CommunicationError, 00103 "IO Error: " + QString::number(job->error()) + ":" + 00104 job->errorString() ); 00105 return; 00106 } 00107 QDomDocument &response = static_cast<KIO::DavJob *>( job )->response(); 00108 00109 kdDebug() << "Search uid result: " << endl << response.toString() << endl; 00110 00111 QDomElement item = response.documentElement().firstChild().toElement(); 00112 QDomElement hrefElement = item.namedItem( "href" ).toElement(); 00113 if ( item.isNull() || hrefElement.isNull() ) { 00114 // No appointment with this UID in exchange database 00115 // Create a new filename for this appointment and store it there 00116 tryExist(); 00117 return; 00118 } 00119 // The appointment is already in the exchange database 00120 // Overwrite it with the new data 00121 QString href = hrefElement.text(); 00122 KURL url( href ); 00123 kdDebug() << "Found URL with identical uid: " << url.prettyURL() 00124 << ", overwriting that one" << endl; 00125 00126 startUpload( toDAV( url ) ); 00127 } 00128 00129 void ExchangeUpload::tryExist() 00130 { 00131 // FIXME: we should first check if current's uid is already in the Exchange database 00132 // Maybe use locking? 00133 KURL url = mAccount->calendarURL(); 00134 if ( m_currentUploadNumber == 0 ) 00135 url.addPath( m_currentUpload->summary() + ".EML" ); 00136 else 00137 url.addPath( m_currentUpload->summary() + "-" + QString::number( m_currentUploadNumber ) + ".EML" ); 00138 00139 kdDebug() << "Trying to see whether " << url.prettyURL() << " exists" << endl; 00140 00141 QDomDocument doc; 00142 QDomElement root = addElement( doc, doc, "DAV:", "propfind" ); 00143 QDomElement prop = addElement( doc, root, "DAV:", "prop" ); 00144 addElement( doc, prop, "DAV:", "displayname" ); 00145 addElement( doc, prop, "urn:schemas:calendar", "uid" ); 00146 00147 KIO::DavJob *job = KIO::davPropFind( url, doc, "0", false ); 00148 job->setWindow( mWindow ); 00149 job->addMetaData( "errorPage", "false" ); 00150 connect( job, SIGNAL( result( KIO::Job * ) ), 00151 SLOT( slotPropFindResult( KIO::Job * ) ) ); 00152 } 00153 00154 void ExchangeUpload::slotPropFindResult( KIO::Job *job ) 00155 { 00156 kdDebug() << "slotPropFindResult()" << endl; 00157 int error = job->error(); 00158 kdDebug() << "PROPFIND error: " << error << ":" << job->errorString() << endl; 00159 if ( error && error != KIO::ERR_DOES_NOT_EXIST ) { 00160 job->showErrorDialog( 0 ); 00161 emit finished( this, ExchangeClient::CommunicationError, 00162 "IO Error: " + QString::number(error) + ":" + 00163 job->errorString() ); 00164 return; 00165 } 00166 00167 if ( !error ) { 00168 // File exist, try another one 00169 m_currentUploadNumber++; 00170 tryExist(); 00171 return; 00172 } 00173 00174 // We got a 404 error, resource doesn't exist yet, create it 00175 // FIXME: race condition possible if resource is created under 00176 // our nose. 00177 00178 KURL url = mAccount->calendarURL(); 00179 if ( m_currentUploadNumber == 0 ) 00180 url.addPath( m_currentUpload->summary() + ".EML" ); 00181 else 00182 url.addPath( m_currentUpload->summary() + "-" + 00183 QString::number( m_currentUploadNumber ) + ".EML" ); 00184 00185 startUpload( url ); 00186 } 00187 00188 QString timezoneid( int offset ) 00189 { 00190 switch ( offset ) { 00191 case 0: return "0"; 00192 case -60: return "3"; 00193 case -120: return "5"; 00194 case -180: return "51"; 00195 case -210: return "25"; 00196 case -240: return "24"; // Abu Dhabi 00197 case -270: return "48"; // Kabul 00198 case -300: return "47"; // Islamabad 00199 case -330: return "23"; // Bombay 00200 case -360: return "46"; // Dhaka 00201 case -420: return "22"; // Bangkok 00202 case -480: return "45"; // Beijing 00203 case -540: return "20"; // Tokyo 00204 case -570: return "44"; // Darwin 00205 case -600: return "18"; // Brisbane 00206 case -660: return "41"; // Solomon Islands 00207 case -720: return "17"; // Auckland 00208 case 60: return "29"; // Azores 00209 case 120: return "30"; // Mid Atlantic 00210 case 180: return "8"; // Brasilia 00211 case 210: return "28"; // Newfoundland 00212 case 240: return "9"; // Atlantic time Canada 00213 case 300: return "10"; // Eastern 00214 case 360: return "11"; // Central time 00215 case 420: return "12"; // Mountain time 00216 case 480: return "13"; // Pacific time 00217 case 540: return "14"; // Alaska time 00218 case 600: return "15"; // Hawaii 00219 case 660: return "16"; // Midway Island 00220 case 720: return "39"; // Eniwetok 00221 default: return "52"; // Invalid time zone 00222 } 00223 } 00224 00225 00226 void ExchangeUpload::startUpload( const KURL &url ) 00227 { 00228 KCal::Event *event = static_cast<KCal::Event *>( m_currentUpload ); 00229 if ( ! event ) { 00230 kdDebug() << "ERROR: trying to upload a non-Event Incidence" << endl; 00231 emit finished( this, ExchangeClient::NonEventError, "The incidence that is to be uploaded to the exchange server is not of type KCal::Event" ); 00232 return; 00233 } 00234 00235 QDomDocument doc; 00236 QDomElement root = addElement( doc, doc, "DAV:", "propertyupdate" ); 00237 QDomElement set = addElement( doc, root, "DAV:", "set" ); 00238 QDomElement prop = addElement( doc, set, "DAV:", "prop" ); 00239 addElement( doc, prop, "DAV:", "contentclass", "urn:content-classes:appointment" ); 00240 // addElement( doc, prop, "http://schemas.microsoft.com/exchange/", "outlookmessageclass", "IPM.appointment" ); 00241 addElement( doc, prop, "http://schemas.microsoft.com/exchange/", 00242 "outlookmessageclass", "IPM.Appointment" ); 00243 // addElement( doc, prop, "urn:schemas:calendar:", "method", "Add" ); 00244 addElement( doc, prop, "urn:schemas:calendar:", "alldayevent", 00245 event->doesFloat() ? "1" : "0" ); 00246 addElement( doc, prop, "urn:schemas:calendar:", "busystatus", 00247 event->transparency() ? "Free" : "Busy" ); 00248 // KLUDGE: somehow we need to take the opposite of the 00249 // value that localUTCOffset() supplies... 00250 int tzOffset = - KRFCDate::localUTCOffset(); 00251 QString offsetString; 00252 if ( tzOffset == 0 ) 00253 offsetString = "Z"; 00254 else if ( tzOffset > 0 ) 00255 offsetString = QString( "+%1:%2" ).arg(tzOffset/60, 2).arg( tzOffset%60, 2 ); 00256 else 00257 offsetString = QString( "-%1:%2" ).arg((-tzOffset)/60, 2).arg( (-tzOffset)%60, 2 ); 00258 offsetString = offsetString.replace( QRegExp(" "), "0" ); 00259 00260 kdDebug() << "Timezone offset: " << tzOffset << " : " << offsetString << endl; 00261 00262 addElement( doc, prop, "urn:schemas:calendar:", "dtstart", 00263 event->dtStart().toString( "yyyy-MM-ddThh:mm:ssZ" ) ); 00264 // event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzzZ" ) ); 00265 // 2002-06-04T08:00:00.000Z" ); 00266 addElement( doc, prop, "urn:schemas:calendar:", "dtend", 00267 event->dtEnd().toString( "yyyy-MM-ddThh:mm:ssZ" ) ); 00268 #if 0 00269 addElement( doc, prop, "urn:schemas:calendar:", "dtstart", 00270 event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzz" )+ offsetString ); 00271 // event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzzZ" ) ); 00272 // 2002-06-04T08:00:00.000Z" ); 00273 addElement( doc, prop, "urn:schemas:calendar:", "dtend", 00274 event->dtEnd().toString( "yyyy-MM-ddThh:mm:ss.zzz" ) + offsetString ); 00275 #endif 00276 addElement( doc, prop, "urn:schemas:calendar:", "lastmodified", zoneAsUtc( event->lastModified(), mTimeZoneId ).toString( Qt::ISODate )+"Z" ); 00277 00278 // addElement( doc, prop, "urn:schemas:calendar:", "meetingstatus", "confirmed" ); 00279 addElement( doc, prop, "urn:schemas:httpmail:", "textdescription", event->description() ); 00280 addElement( doc, prop, "urn:schemas:httpmail:", "subject", event->summary() ); 00281 addElement( doc, prop, "urn:schemas:calendar:", "location", event->location() ); 00282 // addElement( doc, prop, "urn:schemas:mailheader:", "subject", event->summary() ); 00283 addElement( doc, prop, "urn:schemas:calendar:", "uid", event->uid() ); 00284 // addElement( doc, prop, "urn:schemas:calendar:", "organizer", event->organizer() ); 00285 00286 KCal::Recurrence *recurrence = event->recurrence(); 00287 kdDebug() << "Recurrence->doesRecur(): " << recurrence->doesRecur() << endl; 00288 if ( recurrence->doesRecur() != KCal::Recurrence::rNone ) { 00289 addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "1" ); 00290 KCal::ICalFormat *format = new KCal::ICalFormat(); 00291 QString recurstr = format->toString( recurrence ); 00292 // Strip leading "RRULE\n :" and whitespace 00293 recurstr = recurstr.replace( QRegExp("^[A-Z]*[\\s]*:"), "").stripWhiteSpace(); 00294 kdDebug() << "Recurrence rule after replace: \"" << recurstr << "\"" << endl; 00295 delete format; 00296 QDomElement rrule = addElement( doc, prop, "urn:schemas:calendar:", "rrule" ); 00297 addElement( doc, rrule, "xml:", "v", recurstr ); 00298 addElement( doc, prop, "urn:schemas:calendar:", "timezoneid", timezoneid( tzOffset ) ); 00299 } else { 00300 addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "0" ); 00301 } 00302 00303 KCal::DateList exdates = event->exDates(); 00304 if ( !exdates.isEmpty() ) { 00305 QDomElement exdate = addElement( doc, prop, "urn:schemas:calendar:", "exdate" ); 00306 KCal::DateList::iterator it; 00307 for ( it = exdates.begin(); it != exdates.end(); ++it ) { 00308 QString date = (*it).toString( "yyyy-MM-ddT00:00:00.000" )+ offsetString; 00309 // QString date = zoneAsUtc( (*it), mTimeZoneId ).toString( Qt::ISODate ); 00310 addElement( doc, exdate, "xml:", "v", date ); 00311 } 00312 } 00313 00314 KCal::Alarm::List alarms = event->alarms(); 00315 if ( alarms.count() > 0 ) { 00316 KCal::Alarm* alarm = alarms.first(); 00317 // TODO: handle multiple alarms 00318 // TODO: handle end offsets and general alarm times 00319 // TODO: handle alarm types 00320 if ( alarm->hasStartOffset() ) { 00321 int offset = - alarm->startOffset().asSeconds(); 00322 addElement( doc, prop, "urn:schemas:calendar:", "reminderoffset", QString::number( offset ) ); 00323 } 00324 } 00325 00326 kdDebug() << "Uploading event: " << endl; 00327 kdDebug() << doc.toString() << endl; 00328 00329 kdDebug() << "Upload url: " << url << endl; 00330 00331 KIO::DavJob *job = KIO::davPropPatch( url, doc, false ); 00332 job->setWindow( mWindow ); 00333 connect( job, SIGNAL( result( KIO::Job * ) ), 00334 SLOT( slotPatchResult( KIO::Job * ) ) ); 00335 } 00336 00337 void ExchangeUpload::slotPatchResult( KIO::Job *job ) 00338 { 00339 kdDebug() << "slotPropPatchResult()" << endl; 00340 if ( job->error() ) { 00341 job->showErrorDialog( 0 ); 00342 kdDebug() << "Error: " << job->error() << endl; 00343 emit finished( this, ExchangeClient::CommunicationError, 00344 "IO Error: " + QString::number(job->error()) + ":" + 00345 job->errorString() ); 00346 return; 00347 } 00348 QDomDocument response = static_cast<KIO::DavJob *>( job )->response(); 00349 kdDebug() << "Patch result: " << response.toString() << endl; 00350 00351 // Either we have a "201 Created" (if a new event has been created) or 00352 // we have a "200 OK" (if an existing event has been altered), 00353 // or else an error has occurred ;) 00354 QDomElement status = response.documentElement().namedItem( "response" ) 00355 .namedItem( "status" ).toElement(); 00356 QDomElement propstat = response.documentElement().namedItem( "response" ) 00357 .namedItem( "propstat" ).namedItem( "status" ) 00358 .toElement(); 00359 kdDebug() << "status: " << status.text() << endl; 00360 kdDebug() << "propstat: " << propstat.text() << endl; 00361 if ( ! ( status.text().contains( "201" ) || 00362 propstat.text().contains( "200" ) ) ) 00363 emit finished( this, ExchangeClient::EventWriteError, 00364 "Upload error response: \n" + response.toString() ); 00365 else 00366 emit finished( this, ExchangeClient::ResultOK, QString::null ); 00367 } 00368 00369 #include "exchangeupload.moc"
KDE Logo
This file is part of the documentation for libkpimexchange Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:38 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003