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
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
00265
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
00272
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
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
00283 addElement( doc, prop,
"urn:schemas:calendar:",
"uid", event->uid() );
00284
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
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
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
00318
00319
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
00352
00353
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"