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 Wed Jul 28 23:58:09 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003