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 mAccount = account;
00068 mDownloadsBusy = 0;
00069 mProgress = 0L;
00070 mCalendar = 0L;
00071 mFormat =
new KCal::ICalFormat();
00072 }
00073
00074 ExchangeDownload::~ExchangeDownload()
00075 {
00076 kdDebug() <<
"ExchangeDownload destructor" << endl;
00077
delete mFormat;
00078
if ( mEvents )
delete mEvents;
00079 }
00080
00081
void ExchangeDownload::download(KCal::Calendar* calendar,
const QDate& start,
const QDate& end,
bool showProgress)
00082 {
00083 mCalendar = calendar;
00084 mEvents = 0;
00085
00086
if( showProgress ) {
00087
00088 mProgress =
new ExchangeProgress();
00089 mProgress->show();
00090
00091 connect(
this, SIGNAL(startDownload()), mProgress, SLOT(slotTransferStarted()) );
00092 connect(
this, SIGNAL(finishDownload()), mProgress, SLOT(slotTransferFinished()) );
00093 }
00094
00095 QString sql = dateSelectQuery( start, end.addDays( 1 ) );
00096
00097
00098
00099 increaseDownloads();
00100
00101 KIO::DavJob *job = KIO::davSearch( mAccount->calendarURL(),
"DAV:",
"sql", sql,
false );
00102 KIO::Scheduler::scheduleJob(job);
00103 job->setWindow( mWindow );
00104 connect(job, SIGNAL(result( KIO::Job * )),
this, SLOT(slotSearchResult(KIO::Job *)));
00105 }
00106
00107
void ExchangeDownload::download(
const QDate& start,
const QDate& end,
bool showProgress )
00108 {
00109 mCalendar = 0;
00110 mEvents =
new QPtrList<KCal::Event>;
00111
00112
if( showProgress ) {
00113
00114 mProgress =
new ExchangeProgress();
00115 mProgress->show();
00116
00117 connect(
this, SIGNAL(startDownload()), mProgress, SLOT(slotTransferStarted()) );
00118 connect(
this, SIGNAL(finishDownload()), mProgress, SLOT(slotTransferFinished()) );
00119 }
00120
00121 QString sql = dateSelectQuery( start, end.addDays( 1 ) );
00122
00123 increaseDownloads();
00124
00125 KIO::DavJob *job = KIO::davSearch( mAccount->calendarURL(),
"DAV:",
"sql", sql,
false );
00126 KIO::Scheduler::scheduleJob(job);
00127 job->setWindow( mWindow );
00128 connect(job, SIGNAL(result( KIO::Job * )),
this, SLOT(slotSearchResult(KIO::Job *)));
00129 }
00130
00131 QString ExchangeDownload::dateSelectQuery(
const QDate& start,
const QDate& end )
00132 {
00133 QString startString;
00134 startString.sprintf(
"%04i/%02i/%02i",start.year(),start.month(),start.day());
00135 QString endString;
00136 endString.sprintf(
"%04i/%02i/%02i",end.year(),end.month(),end.day());
00137 QString sql =
00138
"SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\", \"urn:schemas:calendar:uid\"\r\n"
00139
"FROM Scope('shallow traversal of \"\"')\r\n"
00140
"WHERE \"urn:schemas:calendar:dtend\" > '" + startString +
"'\r\n"
00141
"AND \"urn:schemas:calendar:dtstart\" < '" + endString +
"'";
00142
return sql;
00143 }
00144
00145
00146
void ExchangeDownload::slotSearchResult( KIO::Job *job )
00147 {
00148
if ( job->error() ) {
00149 kdError() <<
"Error result for search: " << job->error() << endl;
00150 job->showErrorDialog( 0L );
00151 finishUp( ExchangeClient::CommunicationError, job );
00152
return;
00153 }
00154 QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
00155
00156
00157
00158 handleAppointments( response,
true );
00159
00160 decreaseDownloads();
00161 }
00162
00163
void ExchangeDownload::slotMasterResult( KIO::Job *job )
00164 {
00165
if ( job->error() ) {
00166 kdError() <<
"Error result for Master search: " << job->error() << endl;
00167 job->showErrorDialog( 0L );
00168 finishUp( ExchangeClient::CommunicationError, job );
00169
return;
00170 }
00171 QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
00172
00173 kdDebug() <<
"Search (master) result: " << endl << response.toString() << endl;
00174
00175 handleAppointments( response,
false );
00176
00177 decreaseDownloads();
00178 }
00179
00180
void ExchangeDownload::handleAppointments(
const QDomDocument& response,
bool recurrence ) {
00181
00182
int successCount = 0;
00183
00184
if ( response.documentElement().firstChild().toElement().isNull() ) {
00185
00186
00187
return;
00188 }
00189
00190
for( QDomElement item = response.documentElement().firstChild().toElement();
00191 !item.isNull();
00192 item = item.nextSibling().toElement() )
00193 {
00194
00195 QDomNodeList propstats = item.elementsByTagNameNS(
"DAV:",
"propstat" );
00196
00197
for( uint i=0; i < propstats.count(); i++ )
00198 {
00199 QDomElement propstat = propstats.item(i).toElement();
00200 QDomElement prop = propstat.namedItem(
"prop" ).toElement();
00201
if ( prop.isNull() )
00202 {
00203 kdError() <<
"Error: no <prop> in response" << endl;
00204
continue;
00205 }
00206
00207 QDomElement instancetypeElement = prop.namedItem(
"instancetype" ).toElement();
00208
if ( instancetypeElement.isNull() ) {
00209 kdError() <<
"Error: no instance type in Exchange server reply" << endl;
00210
continue;
00211 }
00212
int instanceType = instancetypeElement.text().toInt();
00213
00214
00215
if ( recurrence && instanceType > 0 ) {
00216 QDomElement uidElement = prop.namedItem(
"uid" ).toElement();
00217
if ( uidElement.isNull() ) {
00218 kdError() <<
"Error: no uid in Exchange server reply" << endl;
00219
continue;
00220 }
00221 QString uid = uidElement.text();
00222
if ( ! m_uids.contains( uid ) ) {
00223 m_uids[uid] = 1;
00224 handleRecurrence(uid);
00225 successCount++;
00226 }
00227
continue;
00228 }
00229
00230 QDomElement hrefElement = prop.namedItem(
"href" ).toElement();
00231
if ( hrefElement.isNull() ) {
00232 kdError() <<
"Error: no href in Exchange server reply" << endl;
00233
continue;
00234 }
00235 QString href = hrefElement.text();
00236 KURL url(href);
00237
00238 kdDebug() <<
"Getting appointment from url: " << url.prettyURL() << endl;
00239
00240 readAppointment( toDAV( url ) );
00241 successCount++;
00242 }
00243 }
00244
if ( !successCount ) {
00245 finishUp( ExchangeClient::ServerResponseError,
"WebDAV SEARCH response:\n" + response.toString() );
00246 }
00247 }
00248
00249
void ExchangeDownload::handleRecurrence(QString uid) {
00250
00251 QString query =
00252
"SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\"\r\n"
00253
"FROM Scope('shallow traversal of \"\"')\r\n"
00254
"WHERE \"urn:schemas:calendar:uid\" = '" + uid +
"'\r\n"
00255
" AND (\"urn:schemas:calendar:instancetype\" = 1)\r\n";
00256
00257
00258
00259
00260 increaseDownloads();
00261
00262 KIO::DavJob* job = KIO::davSearch( mAccount->calendarURL(),
"DAV:",
"sql", query,
false );
00263 KIO::Scheduler::scheduleJob(job);
00264 job->setWindow( mWindow );
00265 connect(job, SIGNAL(result( KIO::Job * )),
this, SLOT(slotMasterResult(KIO::Job *)));
00266 }
00267
00268
void ExchangeDownload::readAppointment(
const KURL& url )
00269 {
00270 QDomDocument doc;
00271 QDomElement root = addElement( doc, doc,
"DAV:",
"propfind" );
00272 QDomElement prop = addElement( doc, root,
"DAV:",
"prop" );
00273 addElement( doc, prop,
"urn:schemas:calendar:",
"uid" );
00274 addElement( doc, prop,
"urn:schemas:calendar:",
"timezoneid" );
00275 addElement( doc, prop,
"urn:schemas:calendar:",
"timezone" );
00276 addElement( doc, prop,
"urn:schemas:calendar:",
"lastmodified" );
00277 addElement( doc, prop,
"urn:schemas:calendar:",
"organizer" );
00278 addElement( doc, prop,
"urn:schemas:calendar:",
"contact" );
00279 addElement( doc, prop,
"urn:schemas:httpmail:",
"to" );
00280 addElement( doc, prop,
"urn:schemas:calendar:",
"attendeestatus" );
00281 addElement( doc, prop,
"urn:schemas:calendar:",
"attendeerole" );
00282 addElement( doc, prop,
"DAV:",
"isreadonly" );
00283 addElement( doc, prop,
"urn:schemas:calendar:",
"instancetype" );
00284 addElement( doc, prop,
"urn:schemas:calendar:",
"created" );
00285 addElement( doc, prop,
"urn:schemas:calendar:",
"dtstart" );
00286 addElement( doc, prop,
"urn:schemas:calendar:",
"dtend" );
00287 addElement( doc, prop,
"urn:schemas:calendar:",
"alldayevent" );
00288 addElement( doc, prop,
"urn:schemas:calendar:",
"transparent" );
00289 addElement( doc, prop,
"urn:schemas:httpmail:",
"textdescription" );
00290 addElement( doc, prop,
"urn:schemas:httpmail:",
"subject" );
00291 addElement( doc, prop,
"urn:schemas:calendar:",
"location" );
00292 addElement( doc, prop,
"urn:schemas:calendar:",
"rrule" );
00293 addElement( doc, prop,
"urn:schemas:calendar:",
"exdate" );
00294 addElement( doc, prop,
"urn:schemas:mailheader:",
"sensitivity" );
00295 addElement( doc, prop,
"urn:schemas:calendar:",
"reminderoffset" );
00296
00297 addElement( doc, prop,
"urn:schemas-microsoft-com:office:office",
"Keywords" );
00298
00299
00300
00301
00302
00303
00304
00305 increaseDownloads();
00306
00307 KIO::DavJob* job = KIO::davPropFind( url, doc,
"0",
false );
00308 KIO::Scheduler::scheduleJob(job);
00309 job->setWindow( mWindow );
00310 job->addMetaData(
"errorPage",
"false" );
00311 connect( job, SIGNAL( result( KIO::Job * ) ),
this, SLOT( slotPropFindResult( KIO::Job * ) ) );
00312 }
00313
00314
void ExchangeDownload::slotPropFindResult( KIO::Job * job )
00315 {
00316
00317
00318
int error = job->error();
00319
if ( error )
00320 {
00321 job->showErrorDialog( 0L );
00322 finishUp( ExchangeClient::CommunicationError, job );
00323
return;
00324 }
00325
00326 QDomDocument response = static_cast<KIO::DavJob *>( job )->response();
00327
00328
00329
00330 QDomElement prop = response.documentElement().namedItem(
"response" ).namedItem(
"propstat" ).namedItem(
"prop" ).toElement();
00331
00332 KCal::Event*
event =
new KCal::Event();
00333
00334 QDomElement uidElement = prop.namedItem(
"uid" ).toElement();
00335
if ( uidElement.isNull() ) {
00336 kdError() <<
"Error: no uid in Exchange server reply" << endl;
00337 finishUp( ExchangeClient::IllegalAppointmentError,
"WebDAV server response:\n" + response.toString() );
00338
return;
00339 }
00340 event->setUid( uidElement.text() );
00341
00342
00343 QString timezoneid = prop.namedItem(
"timezoneid" ).toElement().text();
00344
00345
00346 QString timezone = prop.namedItem(
"timezone" ).toElement().text();
00347
00348
00349
00350 QString localTimeZoneId;
00351
if ( mCalendar ) {
00352 mFormat->setTimeZone( mCalendar->timeZoneId(), !mCalendar->isLocalTime() );
00353 localTimeZoneId = mCalendar->timeZoneId();
00354 }
else {
00355 localTimeZoneId =
"UTC";
00356
00357 }
00358
00359 QString lastModified = prop.namedItem(
"lastmodified" ).toElement().text();
00360 QDateTime dt = utcAsZone( QDateTime::fromString( lastModified, Qt::ISODate ), localTimeZoneId );
00361 event->setLastModified( dt );
00362
00363
00364 QString organizer = prop.namedItem(
"organizer" ).toElement().text();
00365 event->setOrganizer( organizer );
00366
00367
00368
00369 QString contact = prop.namedItem(
"contact" ).toElement().text();
00370
00371
00372
00373
00374
00375 QString to = prop.namedItem(
"to" ).toElement().text();
00376
00377 QStringList attn = QStringList::split(
",", to );
00378 QStringList::iterator it;
00379
for ( it = attn.begin(); it != attn.end(); ++it ) {
00380
00381 QString name =
"";
00382
00383
00384
00385 }
00386
00387 QString readonly = prop.namedItem(
"isreadonly" ).toElement().text();
00388 event->setReadOnly( readonly !=
"0" );
00389
00390
00391 QString created = prop.namedItem(
"created" ).toElement().text();
00392 dt = utcAsZone( QDateTime::fromString( created, Qt::ISODate ), localTimeZoneId );
00393 event->setCreated( dt );
00394
00395
00396 QString dtstart = prop.namedItem(
"dtstart" ).toElement().text();
00397 dt = utcAsZone( QDateTime::fromString( dtstart, Qt::ISODate ), localTimeZoneId );
00398 event->setDtStart( dt );
00399
00400
00401 QString alldayevent = prop.namedItem(
"alldayevent" ).toElement().text();
00402
bool floats = alldayevent.toInt() != 0;
00403 event->setFloats( floats );
00404
00405
00406 QString dtend = prop.namedItem(
"dtend" ).toElement().text();
00407 dt = utcAsZone( QDateTime::fromString( dtend, Qt::ISODate ), localTimeZoneId );
00408
00409
if ( floats ) dt = dt.addDays( -1 );
00410 event->setDtEnd( dt );
00411
00412
00413 QString transparent = prop.namedItem(
"transparent" ).toElement().text();
00414 event->setTransparency( transparent.toInt() > 0 ? KCal::Event::Transparent
00415 : KCal::Event::Opaque );
00416
00417
00418 QString description = prop.namedItem(
"textdescription" ).toElement().text();
00419 event->setDescription( description );
00420
00421
00422 QString subject = prop.namedItem(
"subject" ).toElement().text();
00423 event->setSummary( subject );
00424
00425
00426 QString location = prop.namedItem(
"location" ).toElement().text();
00427 event->setLocation( location );
00428
00429
00430 QString rrule = prop.namedItem(
"rrule" ).toElement().text();
00431
00432
if ( ! rrule.isNull() ) {
00433
00434
00435
if ( ! mFormat->fromString( event->recurrence(), rrule ) ) {
00436 kdError() <<
"ERROR parsing rrule " << rrule << endl;
00437 }
00438 }
00439
00440 QDomElement keywords = prop.namedItem(
"Keywords" ).toElement();
00441 QStringList categories;
00442 QDomNodeList list = keywords.elementsByTagNameNS(
"xml:",
"v" );
00443
for( uint i=0; i < list.count(); i++ ) {
00444 QDomElement item = list.item(i).toElement();
00445 categories.append( item.text() );
00446 }
00447 event->setCategories( categories );
00448
00449
00450
00451 QDomElement exdate = prop.namedItem(
"exdate" ).toElement();
00452 KCal::DateList exdates;
00453 list = exdate.elementsByTagNameNS(
"xml:",
"v" );
00454
for( uint i=0; i < list.count(); i++ ) {
00455 QDomElement item = list.item(i).toElement();
00456 QDate date = utcAsZone( QDateTime::fromString( item.text(), Qt::ISODate ), localTimeZoneId ).date();
00457 exdates.append( date );
00458
00459 }
00460 event->setExDates( exdates );
00461
00462
00463
00464
00465
00466
00467 QString sensitivity = prop.namedItem(
"sensitivity" ).toElement().text();
00468
if ( ! sensitivity.isNull() )
00469
switch( sensitivity.toInt() ) {
00470
case 0: event->setSecrecy( KCal::Incidence::SecrecyPublic );
break;
00471
case 1: event->setSecrecy( KCal::Incidence::SecrecyPrivate );
break;
00472
case 2: event->setSecrecy( KCal::Incidence::SecrecyPrivate );
break;
00473
case 3: event->setSecrecy( KCal::Incidence::SecrecyConfidential );
break;
00474
default: kdWarning() <<
"Unknown sensitivity: " << sensitivity << endl;
00475 }
00476
00477
00478
00479 QString reminder = prop.namedItem(
"reminderoffset" ).toElement().text();
00480
00481
if ( ! reminder.isNull() ) {
00482
00483 KCal::Duration offset( - reminder.toInt() );
00484 KCal::Alarm* alarm = event->newAlarm();
00485 alarm->setStartOffset( offset );
00486 alarm->setEnabled(
true );
00487
00488 }
00490
00492
00493
00495
00497
00498
00500
00501
00503
00504
00506
00507
00513
00514
00515
00516
00517
00519
00520
00521
00522
00523
00524
if ( mCalendar ) {
00525 KCal::Event* oldEvent = mCalendar->event( event->uid() );
00526
if ( oldEvent ) {
00527 kdWarning() <<
"Already got his event, keeping old version..." << endl;
00528
00529
00530 }
else {
00531 mCalendar->addEvent( event );
00532 }
00533 }
else {
00534 emit gotEvent( event, static_cast<KIO::DavJob *>( job )->url() );
00535
00536 }
00537
00538 decreaseDownloads();
00539 }
00540
00541
void ExchangeDownload::increaseDownloads()
00542 {
00543 mDownloadsBusy++;
00544 emit startDownload();
00545 }
00546
00547
void ExchangeDownload::decreaseDownloads()
00548 {
00549 mDownloadsBusy--;
00550
00551 emit finishDownload();
00552
if ( mDownloadsBusy == 0 ) {
00553 kdDebug() <<
"All downloads finished" << endl;
00554 finishUp( ExchangeClient::ResultOK );
00555 }
00556 }
00557
00558
void ExchangeDownload::finishUp(
int result,
const QString& moreInfo )
00559 {
00560
if ( mCalendar ) mCalendar->setModified(
true );
00561
00562
if ( mProgress ) {
00563 disconnect(
this, 0, mProgress, 0 );
00564 disconnect( mProgress, 0,
this, 0 );
00565 mProgress->delayedDestruct();
00566 }
00567
00568
00569
00570
00571 emit finished(
this, result, moreInfo );
00572
00573 }
00574
00575
void ExchangeDownload::finishUp(
int result, KIO::Job* job )
00576 {
00577 finishUp( result, QString(
"WebDAV job error code = ") + QString::number( job->error() ) +
";\n" +
"\"" + job->errorString() +
"\"" );
00578 }
00579
00580
#include "exchangedownload.moc"