00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00065
00066
00067
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
00115
00116 tryExist();
00117 return;
00118 }
00119
00120
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
00132
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
00169 m_currentUploadNumber++;
00170 tryExist();
00171 return;
00172 }
00173
00174
00175
00176
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";
00197 case -270: return "48";
00198 case -300: return "47";
00199 case -330: return "23";
00200 case -360: return "46";
00201 case -420: return "22";
00202 case -480: return "45";
00203 case -540: return "20";
00204 case -570: return "44";
00205 case -600: return "18";
00206 case -660: return "41";
00207 case -720: return "17";
00208 case 60: return "29";
00209 case 120: return "30";
00210 case 180: return "8";
00211 case 210: return "28";
00212 case 240: return "9";
00213 case 300: return "10";
00214 case 360: return "11";
00215 case 420: return "12";
00216 case 480: return "13";
00217 case 540: return "14";
00218 case 600: return "15";
00219 case 660: return "16";
00220 case 720: return "39";
00221 default: return "52";
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
00241 addElement( doc, prop, "http://schemas.microsoft.com/exchange/",
00242 "outlookmessageclass", "IPM.Appointment" );
00243
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
00249
00250
00251 int tzOffset = - KRFCDate::localUTCOffset();
00252 QString offsetString;
00253 if ( tzOffset == 0 )
00254 offsetString = "Z";
00255 else if ( tzOffset > 0 )
00256 offsetString = QString( "+%1:%2" ).arg(tzOffset/60, 2).arg( tzOffset%60, 2 );
00257 else
00258 offsetString = QString( "-%1:%2" ).arg((-tzOffset)/60, 2).arg( (-tzOffset)%60, 2 );
00259 offsetString = offsetString.replace( QRegExp(" "), "0" );
00260
00261 kdDebug() << "Timezone offset: " << tzOffset << " : " << offsetString << endl;
00262 kdDebug() << "ExchangeUpload::mTimeZoneId=" << mTimeZoneId << endl;
00263
00264 addElement( doc, prop, "urn:schemas:calendar:", "dtstart",
00265 zoneAsUtc( event->dtStart(), mTimeZoneId ).toString( Qt::ISODate ) + "Z" );
00266
00267
00268 addElement( doc, prop, "urn:schemas:calendar:", "dtend",
00269 zoneAsUtc( event->dtEnd(), mTimeZoneId ).toString( Qt::ISODate ) + "Z" );
00270 #if 0
00271 addElement( doc, prop, "urn:schemas:calendar:", "dtstart",
00272 event->dtStart().toString( "yyyy-MM-ddThh:mm:ss.zzz" )+ offsetString );
00273
00274
00275 addElement( doc, prop, "urn:schemas:calendar:", "dtend",
00276 event->dtEnd().toString( "yyyy-MM-ddThh:mm:ss.zzz" ) + offsetString );
00277 #endif
00278 addElement( doc, prop, "urn:schemas:calendar:", "lastmodified", zoneAsUtc( event->lastModified(), mTimeZoneId ).toString( Qt::ISODate )+"Z" );
00279
00280
00281 addElement( doc, prop, "urn:schemas:httpmail:", "textdescription", event->description() );
00282 addElement( doc, prop, "urn:schemas:httpmail:", "subject", event->summary() );
00283 addElement( doc, prop, "urn:schemas:calendar:", "location", event->location() );
00284
00285 addElement( doc, prop, "urn:schemas:calendar:", "uid", event->uid() );
00286
00287
00288 KCal::Recurrence *recurrence = event->recurrence();
00289 kdDebug() << "Recurrence->doesRecur(): " << recurrence->doesRecur() << endl;
00290 if ( recurrence->doesRecur() != KCal::Recurrence::rNone ) {
00291 addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "1" );
00292 KCal::ICalFormat *format = new KCal::ICalFormat();
00293 QString recurstr = format->toString( recurrence );
00294
00295 recurstr = recurstr.replace( QRegExp("^[A-Z]*[\\s]*:"), "").stripWhiteSpace();
00296 kdDebug() << "Recurrence rule after replace: \"" << recurstr << "\"" << endl;
00297 delete format;
00298 QDomElement rrule = addElement( doc, prop, "urn:schemas:calendar:", "rrule" );
00299 addElement( doc, rrule, "xml:", "v", recurstr );
00300 addElement( doc, prop, "urn:schemas:calendar:", "timezoneid", timezoneid( tzOffset ) );
00301 } else {
00302 addElement( doc, prop, "urn:schemas:calendar:", "instancetype", "0" );
00303 }
00304
00305 KCal::DateList exdates = event->exDates();
00306 if ( !exdates.isEmpty() ) {
00307 QDomElement exdate = addElement( doc, prop, "urn:schemas:calendar:", "exdate" );
00308 KCal::DateList::iterator it;
00309 for ( it = exdates.begin(); it != exdates.end(); ++it ) {
00310 QString date = (*it).toString( "yyyy-MM-ddT00:00:00.000" )+ offsetString;
00311
00312 addElement( doc, exdate, "xml:", "v", date );
00313 }
00314 }
00315
00316 KCal::Alarm::List alarms = event->alarms();
00317 if ( alarms.count() > 0 ) {
00318 KCal::Alarm* alarm = alarms.first();
00319
00320
00321
00322 if ( alarm->hasStartOffset() ) {
00323 int offset = - alarm->startOffset().asSeconds();
00324 addElement( doc, prop, "urn:schemas:calendar:", "reminderoffset", QString::number( offset ) );
00325 }
00326 }
00327
00328 kdDebug() << "Uploading event: " << endl;
00329 kdDebug() << doc.toString() << endl;
00330
00331 kdDebug() << "Upload url: " << url << endl;
00332
00333 KIO::DavJob *job = KIO::davPropPatch( url, doc, false );
00334 job->setWindow( mWindow );
00335 connect( job, SIGNAL( result( KIO::Job * ) ),
00336 SLOT( slotPatchResult( KIO::Job * ) ) );
00337 }
00338
00339 void ExchangeUpload::slotPatchResult( KIO::Job *job )
00340 {
00341 kdDebug() << "slotPropPatchResult()" << endl;
00342 if ( job->error() ) {
00343 job->showErrorDialog( 0 );
00344 kdDebug() << "Error: " << job->error() << endl;
00345 emit finished( this, ExchangeClient::CommunicationError,
00346 "IO Error: " + QString::number(job->error()) + ":" +
00347 job->errorString() );
00348 return;
00349 }
00350 QDomDocument response = static_cast<KIO::DavJob *>( job )->response();
00351 kdDebug() << "Patch result: " << response.toString() << endl;
00352
00353
00354
00355
00356 QDomElement status = response.documentElement().namedItem( "response" )
00357 .namedItem( "status" ).toElement();
00358 QDomElement propstat = response.documentElement().namedItem( "response" )
00359 .namedItem( "propstat" ).namedItem( "status" )
00360 .toElement();
00361 kdDebug() << "status: " << status.text() << endl;
00362 kdDebug() << "propstat: " << propstat.text() << endl;
00363 if ( ! ( status.text().contains( "201" ) ||
00364 propstat.text().contains( "200" ) ) )
00365 emit finished( this, ExchangeClient::EventWriteError,
00366 "Upload error response: \n" + response.toString() );
00367 else
00368 emit finished( this, ExchangeClient::ResultOK, QString::null );
00369 }
00370
00371 #include "exchangeupload.moc"