00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
#include <qfile.h>
00022
#include <qtextstream.h>
00023
#include <qdatastream.h>
00024
#include <qcstring.h>
00025
#include <qregexp.h>
00026
00027
#include <kapplication.h>
00028
#include <kconfig.h>
00029
#include <kstandarddirs.h>
00030
#include <kmessagebox.h>
00031
#include <klocale.h>
00032
#include <kaction.h>
00033
#include <kurl.h>
00034
#include <kdebug.h>
00035
#include <krfcdate.h>
00036
00037
#include <kio/slave.h>
00038
#include <kio/scheduler.h>
00039
#include <kio/slavebase.h>
00040
#include <kio/davjob.h>
00041
#include <kio/http.h>
00042
#include <kio/job.h>
00043
00044
#include <libkcal/incidence.h>
00045
#include <libkcal/event.h>
00046
#include <libkcal/recurrence.h>
00047
#include <libkcal/icalformat.h>
00048
#include <libkcal/icalformatimpl.h>
00049
#include <libkcal/calendarlocal.h>
00050
00051
extern "C" {
00052
#include <ical.h>
00053 }
00054
00055
#include "exchangeclient.h"
00056
#include "exchangeaccount.h"
00057
#include "exchangeprogress.h"
00058
#include "utils.h"
00059
00060
#include "exchangedownload.h"
00061
00062
using namespace KPIM;
00063
00064 ExchangeDownload::ExchangeDownload( ExchangeAccount *account,
QWidget *window )
00065 : mWindow( window )
00066 {
00067 kdDebug() <<
"ExchangeDownload()" << endl;
00068
00069 mAccount = account;
00070 mDownloadsBusy = 0;
00071 mProgress = 0;
00072 mCalendar = 0;
00073 mFormat =
new KCal::ICalFormat();
00074 }
00075
00076 ExchangeDownload::~ExchangeDownload()
00077 {
00078 kdDebug() <<
"ExchangeDownload destructor" << endl;
00079
delete mFormat;
00080
if ( mEvents )
delete mEvents;
00081 }
00082
00083
void ExchangeDownload::download( KCal::Calendar *calendar,
const QDate &start,
00084
const QDate &end,
bool showProgress )
00085 {
00086 mCalendar = calendar;
00087 mEvents = 0;
00088
00089
#if 0
00090
if( showProgress ) {
00091
00092 mProgress =
new ExchangeProgress();
00093 mProgress->show();
00094
00095 connect(
this, SIGNAL( startDownload() ), mProgress,
00096 SLOT( slotTransferStarted() ) );
00097 connect(
this, SIGNAL(finishDownload() ), mProgress,
00098 SLOT( slotTransferFinished() ) );
00099 }
00100
#endif
00101
00102
QString sql = dateSelectQuery( start, end.addDays( 1 ) );
00103
00104 kdDebug() <<
"Exchange download query: " << endl << sql << endl;
00105
00106 increaseDownloads();
00107
00108 kdDebug() <<
"ExchangeDownload::download() davSearch URL: "
00109 << mAccount->calendarURL() << endl;
00110
00111 KIO::DavJob *job = KIO::davSearch( mAccount->calendarURL(),
"DAV:",
"sql",
00112 sql,
false );
00113 KIO::Scheduler::scheduleJob( job );
00114 job->setWindow( mWindow );
00115 connect( job, SIGNAL( result( KIO::Job * ) ),
00116 SLOT( slotSearchResult( KIO::Job *) ) );
00117 }
00118
00119
void ExchangeDownload::download(
const QDate& start,
const QDate& end,
bool showProgress )
00120 {
00121 mCalendar = 0;
00122 mEvents =
new QPtrList<KCal::Event>;
00123
00124
if( showProgress ) {
00125
00126 mProgress =
new ExchangeProgress();
00127 mProgress->show();
00128
00129 connect(
this, SIGNAL(startDownload()), mProgress, SLOT(slotTransferStarted()) );
00130 connect(
this, SIGNAL(finishDownload()), mProgress, SLOT(slotTransferFinished()) );
00131 }
00132
00133
QString sql = dateSelectQuery( start, end.addDays( 1 ) );
00134
00135 increaseDownloads();
00136
00137 KIO::DavJob *job = KIO::davSearch( mAccount->calendarURL(),
"DAV:",
"sql", sql,
false );
00138 KIO::Scheduler::scheduleJob(job);
00139 job->setWindow( mWindow );
00140 connect( job, SIGNAL( result( KIO::Job * ) ),
00141 SLOT( slotSearchResult( KIO::Job * ) ) );
00142 }
00143
00144
00145
#if 0
00146
QString ExchangeDownload::dateSelectQuery(
const QDate& start,
const QDate& end )
00147 {
00148
QString startString;
00149 startString.sprintf(
"%04i/%02i/%02i",start.year(),start.month(),start.day());
00150
QString endString;
00151 endString.sprintf(
"%04i/%02i/%02i",end.year(),end.month(),end.day());
00152
QString sql =
00153
"SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\", \"urn:schemas:calendar:uid\"\r\n"
00154
"FROM Scope('shallow traversal of \"\"')\r\n"
00155
"WHERE \"urn:schemas:calendar:dtend\" > '" + startString +
"'\r\n"
00156
"AND \"urn:schemas:calendar:dtstart\" < '" + endString +
"'";
00157
return sql;
00158 }
00159
#endif
00160
00161
QString ExchangeDownload::dateSelectQuery(
const QDate& start,
const QDate& end )
00162 {
00163
QString startString;
00164 startString.sprintf(
"%04i-%02i-%02iT00:00:00Z", start.year(),
00165 start.month(), start.day() );
00166
QString endString;
00167 endString.sprintf(
"%04i-%02i-%02iT23:59:59Z", end.year(), end.month(),
00168 end.day() );
00169
QString sql =
00170
"SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\", "
00171
"\"urn:schemas:calendar:uid\"\r\n"
00172
"FROM Scope('shallow traversal of \"\"')\r\n"
00173
"WHERE \"urn:schemas:calendar:dtend\" > '" + startString +
"'\r\n"
00174
"AND \"urn:schemas:calendar:dtstart\" < '" + endString +
"'";
00175
return sql;
00176 }
00177
00178
00179
void ExchangeDownload::slotSearchResult( KIO::Job *job )
00180 {
00181
if ( job->error() ) {
00182 kdError() <<
"ExchangeDownload::slotSearchResult() error: "
00183 << job->error() << endl;
00184
QString text = i18n(
"ExchangeDownload\nError accessing '%1': %2")
00185 .arg( mAccount->calendarURL().prettyURL() )
00186 .arg( job->errorString() );
00187 KMessageBox::error( 0, text );
00188 finishUp( ExchangeClient::CommunicationError, job );
00189
return;
00190 }
00191
QDomDocument &response = static_cast<KIO::DavJob *>( job )->response();
00192
00193 kdDebug() <<
"Search result: " << endl << response.toString() << endl;
00194
00195 handleAppointments( response,
true );
00196
00197 decreaseDownloads();
00198 }
00199
00200
void ExchangeDownload::slotMasterResult( KIO::Job *job )
00201 {
00202
if ( job->error() ) {
00203 kdError() <<
"Error result for Master search: " << job->error() << endl;
00204 job->showErrorDialog( 0 );
00205 finishUp( ExchangeClient::CommunicationError, job );
00206
return;
00207 }
00208
QDomDocument &response = static_cast<KIO::DavJob *>( job )->response();
00209
00210 kdDebug() <<
"Search (master) result: " << endl << response.toString() << endl;
00211
00212 handleAppointments( response,
false );
00213
00214 decreaseDownloads();
00215 }
00216
00217
void ExchangeDownload::handleAppointments(
const QDomDocument &response,
00218
bool recurrence )
00219 {
00220 kdDebug() <<
"Entering handleAppointments" << endl;
00221
int successCount = 0;
00222
00223
if ( response.documentElement().firstChild().toElement().isNull() ) {
00224
00225
00226
return;
00227 }
00228
00229
for(
QDomElement item = response.documentElement().firstChild().toElement();
00230 !item.isNull();
00231 item = item.nextSibling().toElement() ) {
00232
00233
QDomNodeList propstats = item.elementsByTagNameNS(
"DAV:",
"propstat" );
00234
00235
for( uint i=0; i < propstats.count(); i++ ) {
00236
QDomElement propstat = propstats.item(i).toElement();
00237
QDomElement prop = propstat.namedItem(
"prop" ).toElement();
00238
if ( prop.isNull() ) {
00239 kdError() <<
"Error: no <prop> in response" << endl;
00240
continue;
00241 }
00242
00243
QDomElement instancetypeElement = prop.namedItem(
"instancetype" ).toElement();
00244
if ( instancetypeElement.isNull() ) {
00245 kdError() <<
"Error: no instance type in Exchange server reply" << endl;
00246
continue;
00247 }
00248
int instanceType = instancetypeElement.text().toInt();
00249
00250
00251
if ( recurrence && instanceType > 0 ) {
00252
QDomElement uidElement = prop.namedItem(
"uid" ).toElement();
00253
if ( uidElement.isNull() ) {
00254 kdError() <<
"Error: no uid in Exchange server reply" << endl;
00255
continue;
00256 }
00257
QString uid = uidElement.text();
00258
if ( ! m_uids.contains( uid ) ) {
00259 m_uids[uid] = 1;
00260 handleRecurrence(uid);
00261 successCount++;
00262 }
00263
continue;
00264 }
00265
00266
QDomElement hrefElement = prop.namedItem(
"href" ).toElement();
00267
if ( hrefElement.isNull() ) {
00268 kdError() <<
"Error: no href in Exchange server reply" << endl;
00269
continue;
00270 }
00271
QString href = hrefElement.text();
00272 KURL url(href);
00273
00274 kdDebug() <<
"Getting appointment from url: " << url.prettyURL() << endl;
00275
00276 readAppointment( toDAV( url ) );
00277 successCount++;
00278 }
00279 }
00280
if ( !successCount ) {
00281 finishUp( ExchangeClient::ServerResponseError,
00282
"WebDAV SEARCH response:\n" + response.toString() );
00283 }
00284 }
00285
00286
void ExchangeDownload::handleRecurrence(
QString uid )
00287 {
00288
00289
QString query =
00290
"SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\"\r\n"
00291
"FROM Scope('shallow traversal of \"\"')\r\n"
00292
"WHERE \"urn:schemas:calendar:uid\" = '" + uid +
"'\r\n"
00293
" AND (\"urn:schemas:calendar:instancetype\" = 1)\r\n";
00294
00295
00296
00297
00298 increaseDownloads();
00299
00300 KIO::DavJob* job = KIO::davSearch( mAccount->calendarURL(),
"DAV:",
"sql",
00301 query,
false );
00302 KIO::Scheduler::scheduleJob( job );
00303 job->setWindow( mWindow );
00304 connect( job, SIGNAL( result( KIO::Job * ) ),
00305 SLOT( slotMasterResult( KIO::Job * ) ) );
00306 }
00307
00308
void ExchangeDownload::readAppointment(
const KURL& url )
00309 {
00310
QDomDocument doc;
00311
QDomElement root = addElement( doc, doc,
"DAV:",
"propfind" );
00312
QDomElement prop = addElement( doc, root,
"DAV:",
"prop" );
00313 addElement( doc, prop,
"urn:schemas:calendar:",
"uid" );
00314 addElement( doc, prop,
"urn:schemas:calendar:",
"timezoneid" );
00315 addElement( doc, prop,
"urn:schemas:calendar:",
"timezone" );
00316 addElement( doc, prop,
"urn:schemas:calendar:",
"lastmodified" );
00317 addElement( doc, prop,
"urn:schemas:calendar:",
"organizer" );
00318 addElement( doc, prop,
"urn:schemas:calendar:",
"contact" );
00319 addElement( doc, prop,
"urn:schemas:httpmail:",
"to" );
00320 addElement( doc, prop,
"urn:schemas:calendar:",
"attendeestatus" );
00321 addElement( doc, prop,
"urn:schemas:calendar:",
"attendeerole" );
00322 addElement( doc, prop,
"DAV:",
"isreadonly" );
00323 addElement( doc, prop,
"urn:schemas:calendar:",
"instancetype" );
00324 addElement( doc, prop,
"urn:schemas:calendar:",
"created" );
00325 addElement( doc, prop,
"urn:schemas:calendar:",
"dtstart" );
00326 addElement( doc, prop,
"urn:schemas:calendar:",
"dtend" );
00327 addElement( doc, prop,
"urn:schemas:calendar:",
"alldayevent" );
00328 addElement( doc, prop,
"urn:schemas:calendar:",
"transparent" );
00329 addElement( doc, prop,
"urn:schemas:httpmail:",
"textdescription" );
00330 addElement( doc, prop,
"urn:schemas:httpmail:",
"subject" );
00331 addElement( doc, prop,
"urn:schemas:calendar:",
"location" );
00332 addElement( doc, prop,
"urn:schemas:calendar:",
"rrule" );
00333 addElement( doc, prop,
"urn:schemas:calendar:",
"exdate" );
00334 addElement( doc, prop,
"urn:schemas:mailheader:",
"sensitivity" );
00335 addElement( doc, prop,
"urn:schemas:calendar:",
"reminderoffset" );
00336
00337 addElement( doc, prop,
"urn:schemas-microsoft-com:office:office",
00338
"Keywords" );
00339
00340
00341
00342
00343
00344
00345
00346 increaseDownloads();
00347
00348 KIO::DavJob* job = KIO::davPropFind( url, doc,
"0",
false );
00349 KIO::Scheduler::scheduleJob( job );
00350 job->setWindow( mWindow );
00351 job->addMetaData(
"errorPage",
"false" );
00352 connect( job, SIGNAL( result( KIO::Job * ) ),
00353 SLOT( slotPropFindResult( KIO::Job * ) ) );
00354 }
00355
00356
void ExchangeDownload::slotPropFindResult( KIO::Job *job )
00357 {
00358 kdDebug() <<
"slotPropFindResult" << endl;
00359
00360
int error = job->error();
00361
if ( error ) {
00362 job->showErrorDialog( 0 );
00363 finishUp( ExchangeClient::CommunicationError, job );
00364
return;
00365 }
00366
00367
QDomDocument response = static_cast<KIO::DavJob *>( job )->response();
00368 kdDebug() <<
"Response: " << endl;
00369 kdDebug() << response.toString() << endl;
00370
00371
QDomElement prop = response.documentElement().namedItem(
"response" )
00372 .namedItem(
"propstat" ).namedItem(
"prop" ).toElement();
00373
00374 KCal::Event* event =
new KCal::Event();
00375
00376
QDomElement uidElement = prop.namedItem(
"uid" ).toElement();
00377
if ( uidElement.isNull() ) {
00378 kdError() <<
"Error: no uid in Exchange server reply" << endl;
00379 finishUp( ExchangeClient::IllegalAppointmentError,
00380
"WebDAV server response:\n" + response.toString() );
00381
return;
00382 }
00383 event->setUid( uidElement.text() );
00384
00385
00386
QString timezoneid = prop.namedItem(
"timezoneid" ).toElement().text();
00387
00388
00389
QString timezone = prop.namedItem(
"timezone" ).toElement().text();
00390
00391
00392
00393
QString localTimeZoneId;
00394
if ( mCalendar ) {
00395 mFormat->setTimeZone( mCalendar->timeZoneId(), !mCalendar->isLocalTime() );
00396 localTimeZoneId = mCalendar->timeZoneId();
00397 }
else {
00398 localTimeZoneId =
"UTC";
00399
00400 }
00401
00402
QString lastModified = prop.namedItem(
"lastmodified" ).toElement().text();
00403
if ( !lastModified.isEmpty() ) {
00404
QDateTime dt = utcAsZone( QDateTime::fromString( lastModified, Qt::ISODate ), localTimeZoneId );
00405 event->setLastModified( dt );
00406 kdDebug() <<
"Got lastModified:" << lastModified <<
", " << dt.toString() << endl;
00407 }
00408
00409
QString organizer = prop.namedItem(
"organizer" ).toElement().text();
00410 event->setOrganizer( organizer );
00411
00412
00413
00414
QString contact = prop.namedItem(
"contact" ).toElement().text();
00415
00416
00417
00418
00419
00420
QString to = prop.namedItem(
"to" ).toElement().text();
00421
00422
QStringList attn = QStringList::split(
",", to );
00423 QStringList::iterator it;
00424
for ( it = attn.begin(); it != attn.end(); ++it ) {
00425
00426
QString name =
"";
00427
00428
00429
00430 }
00431
00432
QString readonly = prop.namedItem(
"isreadonly" ).toElement().text();
00433 event->setReadOnly( readonly ==
"1" );
00434 kdDebug() <<
"Got readonly: " << readonly <<
":" << (readonly !=
"0") << endl;
00435
00436
QString created = prop.namedItem(
"created" ).toElement().text();
00437
if ( !created.isEmpty() ) {
00438
QDateTime dt = utcAsZone( QDateTime::fromString( created, Qt::ISODate ),
00439 localTimeZoneId );
00440 event->setCreated( dt );
00441 kdDebug() <<
"got created: " << dt.toString() << endl;
00442 }
00443
00444
QString dtstart = prop.namedItem(
"dtstart" ).toElement().text();
00445
if ( !dtstart.isEmpty() ) {
00446
QDateTime dt = utcAsZone( QDateTime::fromString( dtstart, Qt::ISODate ),
00447 localTimeZoneId );
00448 event->setDtStart( dt );
00449 kdDebug() <<
"got dtstart: " << dtstart <<
" becomes in timezone " << dt.toString() << endl;
00450 }
00451
00452
QString alldayevent = prop.namedItem(
"alldayevent" ).toElement().text();
00453
bool floats = alldayevent.toInt() != 0;
00454 event->setFloats( floats );
00455 kdDebug() <<
"Got alldayevent: \"" << alldayevent <<
"\":" << floats << endl;
00456
00457
QString dtend = prop.namedItem(
"dtend" ).toElement().text();
00458
if ( !dtend.isEmpty() ) {
00459
QDateTime dt = utcAsZone( QDateTime::fromString( dtend, Qt::ISODate ),
00460 localTimeZoneId );
00461
00462
if ( floats ) dt = dt.addDays( -1 );
00463 event->setDtEnd( dt );
00464 kdDebug() <<
"got dtend: " << dtend <<
" becomes in timezone " << dt.toString() << endl;
00465 }
00466
00467
QString transparent = prop.namedItem(
"transparent" ).toElement().text();
00468 event->setTransparency( transparent.toInt() > 0 ? KCal::Event::Transparent
00469 : KCal::Event::Opaque );
00470
00471
00472
QString description = prop.namedItem(
"textdescription" ).toElement().text();
00473 event->setDescription( description );
00474 kdDebug() <<
"Got description: " << description << endl;
00475
00476
QString subject = prop.namedItem(
"subject" ).toElement().text();
00477 event->setSummary( subject );
00478 kdDebug() <<
"Got summary: " << subject << endl;
00479
00480
QString location = prop.namedItem(
"location" ).toElement().text();
00481 event->setLocation( location );
00482
00483
00484
QString rrule = prop.namedItem(
"rrule" ).toElement().text();
00485 kdDebug() <<
"Got rrule: " << rrule << endl;
00486
if ( !rrule.isEmpty() ) {
00487
00488
00489
if ( ! mFormat->fromString( event->recurrence(), rrule ) ) {
00490 kdError() <<
"ERROR parsing rrule " << rrule << endl;
00491 }
00492 }
00493
00494
QDomElement keywords = prop.namedItem(
"Keywords" ).toElement();
00495
QStringList categories;
00496
QDomNodeList list = keywords.elementsByTagNameNS(
"xml:",
"v" );
00497
for( uint i=0; i < list.count(); i++ ) {
00498
QDomElement item = list.item(i).toElement();
00499 categories.append( item.text() );
00500 }
00501 event->setCategories( categories );
00502
00503
00504
00505
QDomElement exdate = prop.namedItem(
"exdate" ).toElement();
00506 KCal::DateList exdates;
00507 list = exdate.elementsByTagNameNS(
"xml:",
"v" );
00508
for( uint i=0; i < list.count(); i++ ) {
00509
QDomElement item = list.item(i).toElement();
00510
QDate date = utcAsZone( QDateTime::fromString( item.text(), Qt::ISODate ), localTimeZoneId ).date();
00511 exdates.append( date );
00512
00513 }
00514 event->setExDates( exdates );
00515
00516
00517
00518
00519
00520
00521
QString sensitivity = prop.namedItem(
"sensitivity" ).toElement().text();
00522
if ( ! sensitivity.isNull() )
00523
switch( sensitivity.toInt() ) {
00524
case 0: event->setSecrecy( KCal::Incidence::SecrecyPublic );
break;
00525
case 1: event->setSecrecy( KCal::Incidence::SecrecyPrivate );
break;
00526
case 2: event->setSecrecy( KCal::Incidence::SecrecyPrivate );
break;
00527
case 3: event->setSecrecy( KCal::Incidence::SecrecyConfidential );
break;
00528
default: kdWarning() <<
"Unknown sensitivity: " << sensitivity << endl;
00529 }
00530
00531
00532
00533
QString reminder = prop.namedItem(
"reminderoffset" ).toElement().text();
00534
00535
if ( !reminder.isEmpty() ) {
00536
00537 KCal::Duration offset( - reminder.toInt() );
00538 KCal::Alarm *alarm = event->newAlarm();
00539 alarm->setStartOffset( offset );
00540 alarm->setEnabled(
true );
00541
00542 }
00544
00546
00547
00549
00551
00552
00554
00555
00557
00558
00560
00561
00567
00568
00569
00570
00571
00573
00574
00575
00576
00577
00578
if ( mCalendar ) {
00579 KCal::Event *oldEvent = mCalendar->event( event->uid() );
00580
if ( oldEvent ) {
00581 kdWarning() <<
"Already got his event, replace it..." << endl;
00582 mCalendar->deleteEvent( oldEvent );
00583 }
00584 kdDebug() <<
"ADD EVENT" << endl;
00585 mCalendar->addEvent( event );
00586 }
else {
00587 kdDebug() <<
"EMIT gotEvent" << endl;
00588 emit gotEvent( event, static_cast<KIO::DavJob *>( job )->url() );
00589
00590 }
00591
00592 decreaseDownloads();
00593 }
00594
00595
void ExchangeDownload::increaseDownloads()
00596 {
00597 mDownloadsBusy++;
00598 emit startDownload();
00599 }
00600
00601
void ExchangeDownload::decreaseDownloads()
00602 {
00603 mDownloadsBusy--;
00604
00605 emit finishDownload();
00606
if ( mDownloadsBusy == 0 ) {
00607 kdDebug() <<
"All downloads finished" << endl;
00608 finishUp( ExchangeClient::ResultOK );
00609 }
00610 }
00611
00612
void ExchangeDownload::finishUp(
int result,
const QString &moreInfo )
00613 {
00614 kdDebug() <<
"ExchangeDownload::finishUp() " << result <<
" "
00615 << moreInfo << endl;
00616
00617
if ( mCalendar ) mCalendar->setModified(
true );
00618
00619
if ( mProgress ) {
00620 disconnect(
this, 0, mProgress, 0 );
00621 disconnect( mProgress, 0,
this, 0 );
00622 mProgress->delayedDestruct();
00623 }
00624
00625
00626
00627
00628 emit finished(
this, result, moreInfo );
00629
00630 }
00631
00632
void ExchangeDownload::finishUp(
int result, KIO::Job *job )
00633 {
00634 finishUp( result,
QString(
"WebDAV job error code = ") +
00635 QString::number( job->error() ) +
";\n" +
"\"" +
00636 job->errorString() +
"\"" );
00637 }
00638
00639
#include "exchangedownload.moc"