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, 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
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
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
00101
00102 QDomElement item = response.documentElement().firstChild().toElement();
00103 QDomElement hrefElement = item.namedItem( "href" ).toElement();
00104 if ( item.isNull() || hrefElement.isNull() ) {
00105
00106
00107 tryExist();
00108 return;
00109 }
00110
00111
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
00122
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
00158 m_currentUploadNumber++;
00159 tryExist();
00160 return;
00161 }
00162
00163
00164
00165
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";
00184 case -270: return "48";
00185 case -300: return "47";
00186 case -330: return "23";
00187 case -360: return "46";
00188 case -420: return "22";
00189 case -480: return "45";
00190 case -540: return "20";
00191 case -570: return "44";
00192 case -600: return "18";
00193 case -660: return "41";
00194 case -720: return "17";
00195 case 60: return "29";
00196 case 120: return "30";
00197 case 180: return "8";
00198 case 210: return "28";
00199 case 240: return "9";
00200 case 300: return "10";
00201 case 360: return "11";
00202 case 420: return "12";
00203 case 480: return "13";
00204 case 540: return "14";
00205 case 600: return "15";
00206 case 660: return "16";
00207 case 720: return "39";
00208 default: return "52";
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
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
00234
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
00250
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
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
00260 addElement( doc, prop, "urn:schemas:calendar:", "uid", event->uid() );
00261
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
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
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
00295
00296
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
00321 QDomDocument response = static_cast<KIO::DavJob *>( job )->response();
00322
00323
00324
00325
00326
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"