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
#include <qsocketdevice.h>
00024
#include <qsocketnotifier.h>
00025
#include <qtextstream.h>
00026
00027
#include <kurl.h>
00028
#include <kdebug.h>
00029
#include <krfcdate.h>
00030
#include <kextsock.h>
00031
00032
#include <kio/job.h>
00033
#include <kio/slave.h>
00034
#include <kio/scheduler.h>
00035
#include <kio/slavebase.h>
00036
#include <kio/davjob.h>
00037
#include <kio/http.h>
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 "exchangemonitor.h"
00047
#include "exchangeclient.h"
00048
#include "exchangeaccount.h"
00049
#include "utils.h"
00050
00051
extern "C" {
00052
#include <unistd.h>
00053 }
00054
00055
using namespace KPIM;
00056
00057
QString makeIDString(
const ExchangeMonitor::IDList& IDs )
00058 {
00059
QString result;
00060 ExchangeMonitor::IDList::ConstIterator it;
00061
for ( it = IDs.begin(); it != IDs.end(); ++it ) {
00062
if ( it == IDs.begin() )
00063 result += QString::number( (*it) );
00064
else
00065 result +=
"," + QString::number( (*it) );
00066 }
00067
return result;
00068 }
00069
00070
ExchangeMonitor::IDList makeIDList(
const QString& input )
00071 {
00072
ExchangeMonitor::IDList IDs;
00073
QStringList numbers = QStringList::split(
",", input );
00074 QStringList::iterator j;
00075
for ( j = numbers.begin(); j != numbers.end(); ++j ) {
00076 ExchangeMonitor::ID
id = (*j).toLong();
00077 IDs.append(
id );
00078 }
00079
return IDs;
00080 }
00081
00082 ExchangeMonitor::ExchangeMonitor( ExchangeAccount* account,
int pollMode,
const QHostAddress& ownInterface )
00083 {
00084 kdDebug() <<
"Called ExchangeMonitor" << endl;
00085
00086 mAccount = account;
00087 mSubscriptionLifetime = 3600;
00088 mPollMode = pollMode;
00089 mPollTimer = 0;
00090
00091
if ( pollMode == CallBack ) {
00092 mSocket =
new QSocketDevice( QSocketDevice::Datagram );
00093
if ( ! mSocket->bind( ownInterface, 0 ) )
00094 kdDebug() <<
"bind() returned false" << endl;
00095 mSocket->setBlocking(
false );
00096 mNotifier =
new QSocketNotifier( mSocket->socket(), QSocketNotifier::Read );
00097 connect( mNotifier, SIGNAL(activated(
int )),
this, SLOT( slotActivated(
int)));
00098
00099
00100
00101
00102
00103
00104
00105
00106 kdDebug() <<
"Port: " << mSocket->port() << endl;
00107 kdDebug() <<
"Host: " << mSocket->address().toString() << endl;
00108
00109 }
00110
00111
if ( mPollMode == Poll ) {
00112 mPollTimer =
new QTimer(
this );
00113 connect( mPollTimer, SIGNAL(timeout()),
this, SLOT(slotPollTimer()) );
00114 mPollTimer->start( 60000 );
00115 }
00116
00117 mRenewTimer =
new QTimer(
this );
00118 connect( mRenewTimer, SIGNAL(timeout()),
this, SLOT(slotRenewTimer()) );
00119 mRenewTimer->start( mSubscriptionLifetime * 900 );
00120 }
00121
00122 ExchangeMonitor::~ExchangeMonitor()
00123 {
00124 kdDebug() <<
"Entering ExchangeMonitor destructor" << endl;
00125
delete mNotifier;
00126
delete mSocket;
00127
if ( mPollTimer )
delete mPollTimer;
00128
if ( mRenewTimer )
delete mRenewTimer;
00129
if ( ! mSubscriptionMap.isEmpty() ) {
00130
QString headers =
"Subscription-ID: " + makeIDString( mSubscriptionMap.keys() );
00131 kdDebug() <<
"Subsubscribing all watches, headers:" << endl << headers << endl;
00132 KIO::DavJob *job =
new KIO::DavJob( mAccount->calendarURL(), (
int) KIO::DAV_UNSUBSCRIBE, QString::null,
false );
00133 job->addMetaData(
"customHTTPHeader", headers );
00134
00135
00136
00137 }
00138 kdDebug() <<
"Finished ExchangeMonitor destructor" << endl;
00139
00140 }
00141
00142
void ExchangeMonitor::addWatch(
const KURL &url,
int mode,
int depth )
00143 {
00144
QString headers =
"Notification-type: ";
00145
switch( mode ) {
00146
case Delete: headers +=
"delete\r\n";
break;
00147
case Move: headers +=
"move\r\n";
break;
00148
case Newmail: headers +=
"pragma/<http://schemas.microsoft.com/exchange/newmail>\r\n";
break;
00149
case Update: headers +=
"update\r\n";
break;
00150
case UpdateNewMember: headers +=
"update/newmember\r\n";
break;
00151 }
00152
00153 headers +=
"Depth: " + QString::number( depth );
00154
00155
if (mPollMode == CallBack )
00156 headers +=
"\r\nCall-Back: httpu://" + mSocket->address().toString() +
":" + QString::number(mSocket->port());
00157
00158 kdDebug() <<
"Headers: " << headers << endl;
00159
00160 KURL myURL = toDAV( url );
00161 KIO::DavJob *job =
new KIO::DavJob( myURL, (
int) KIO::DAV_SUBSCRIBE, QString::null,
false );
00162 job->addMetaData(
"customHTTPHeader", headers );
00163 job->addMetaData(
"PropagateHttpHeader",
"true" );
00164 connect(job, SIGNAL(result( KIO::Job * )),
this, SLOT(slotSubscribeResult(KIO::Job *)));
00165 }
00166
00167
void ExchangeMonitor::removeWatch(
const KURL &url )
00168 {
00169 KURL myURL = toDAV( url );
00170
QMap<ID,KURL>::Iterator it;
00171
for ( it = mSubscriptionMap.begin(); it != mSubscriptionMap.end(); ++it ) {
00172
if ( it.data() == myURL ) {
00173 removeWatch( it.key() );
00174
return;
00175 }
00176 }
00177 kdWarning() <<
"Trying to remove unknown watch " << myURL.prettyURL() <<
", failed." << endl;
00178 }
00179
00180
void ExchangeMonitor::removeWatch( ID
id )
00181 {
00182 KIO::DavJob *job =
new KIO::DavJob( mAccount->calendarURL(), (
int) KIO::DAV_UNSUBSCRIBE, QString::null,
false );
00183 job->addMetaData(
"customHTTPHeader",
"Subscription-id: " + QString::number(
id ));
00184 job->addMetaData(
"PropagateHttpHeader",
"true" );
00185 connect(job, SIGNAL(result( KIO::Job * )),
this, SLOT(slotUnsubscribeResult(KIO::Job *)));
00186 }
00187
00188
void ExchangeMonitor::slotSubscribeResult( KIO::Job * job )
00189 {
00190
if ( job->error() ) {
00191 job->showErrorDialog( 0L );
00192 emit error( ExchangeClient::CommunicationError,
"IO Error: " + QString::number(job->error()) +
":" + job->errorString() );
00193
return;
00194 }
00195
00196 ID
id;
00197 KURL url;
00198
bool gotID =
false;
00199
bool gotURL =
false;
00200
00201
QStringList headers = QStringList::split(
"\n", job->queryMetaData(
"HTTP-Headers" ) );
00202
for ( QStringList::Iterator it = headers.begin(); it != headers.end(); ++it ) {
00203
int colon = (*it).find(
": " );
00204
if ( colon<0 )
continue;
00205
QString tag = (*it).left( colon ).stripWhiteSpace().lower();
00206
QString value = (*it).mid( colon+1 ).stripWhiteSpace();
00207
if ( tag ==
"subscription-lifetime" ) {
00208
int lifetime = value.toInt();
00209
if ( lifetime < mSubscriptionLifetime ) {
00210 mSubscriptionLifetime = lifetime;
00211 mRenewTimer->changeInterval( lifetime * 900 );
00212 slotRenewTimer();
00213 }
00214 }
else if ( tag ==
"subscription-id" ) {
00215
id = value.toLong();
00216 gotID =
true;
00217 }
else if ( tag ==
"content-location" ) {
00218 url = toDAV( KURL( value ) );
00219 gotURL =
true;
00220 }
00221 }
00222
00223
if ( mSubscriptionLifetime < 60 ) {
00224 kdWarning() <<
"Exchange server gave subscription a lifetime of " << mSubscriptionLifetime <<
", changing to 60 seconds." << endl;
00225 mSubscriptionLifetime = 60;
00226
return;
00227 }
00228
00229
if ( ! gotID ) {
00230 kdError() <<
"Error: Exchange server didn't give a subscription ID" << endl;
00231 emit error( ExchangeClient::ServerResponseError,
"No subscription ID in SUBSCRIBE response headers: " + headers.join(
", ") );
00232
return;
00233 }
00234
00235
if ( ! gotURL ) {
00236 kdError() <<
"Error: Exchange server didn't return content-location" << endl;
00237 emit error( ExchangeClient::ServerResponseError,
"No content-location in SUBSCRIBE response headers: " + headers.join(
", ") );
00238
return;
00239 }
00240
00241 kdDebug() <<
"Lifetime: " << mSubscriptionLifetime << endl;
00242 kdDebug() <<
"ID: " <<
id << endl;
00243 kdDebug() <<
"URL: " << url.prettyURL() << endl;
00244
00245 mSubscriptionMap.insert(
id, url );
00246 }
00247
00248
void ExchangeMonitor::slotUnsubscribeResult( KIO::Job * job )
00249 {
00250
if ( job->error() ) {
00251 job->showErrorDialog( 0L );
00252 emit error( ExchangeClient::CommunicationError,
"IO Error: " + QString::number(job->error()) +
":" + job->errorString() );
00253
return;
00254 }
00255
00256
QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
00257 kdDebug() <<
"UNSUBSCRIBE result: " << endl << response.toString() << endl;
00258
00259
QDomElement status = response.documentElement().namedItem(
"response" ).namedItem(
"status" ).toElement();
00260
QDomElement subscriptionID = response.documentElement().namedItem(
"response" ).namedItem(
"subscriptionID" ).toElement();
00261 kdDebug() <<
"Subscription ID.text(): " << subscriptionID.text() << endl;
00262
bool ok;
00263 ID
id = subscriptionID.text().toLong( &ok );
00264
if ( ! status.text().contains(
"200" ) || !ok) {
00265 kdError() <<
"UNSUBSCRIBE result is not 200 or no subscription ID found" << endl;
00266 emit error( ExchangeClient::ServerResponseError,
"UNSUBSCRIBE yields an error response: \n" + response.toString() );
00267 }
00268
00269 mSubscriptionMap.remove(
id );
00270 }
00271
00272
void ExchangeMonitor::slotPollTimer()
00273 {
00274 kdDebug() <<
"ExchangeMonitor::slotPollTimer()" << endl;
00275 poll( mSubscriptionMap.keys() );
00276 }
00277
00278
void ExchangeMonitor::slotActivated(
int )
00279 {
00280 kdDebug() <<
"ExchangeMonitor::slotActivated()" << endl;
00281
00282 kdDebug() <<
"Bytes available: " << mSocket->bytesAvailable() << endl;
00283
int maxLen = mSocket->bytesAvailable();
00284
if ( maxLen == 0 )
00285
return;
00286
00287
QCString response( maxLen+2 );
00288 Q_LONG len = mSocket->readBlock ( response.data(), maxLen+1 );
00289
00290
if ( len <= 0 ) {
00291 kdDebug() <<
"Error: len<=0" << endl;
00292 kdDebug() <<
"Error: " << mSocket->error() << endl;
00293
return;
00294 }
00295 kdDebug() <<
"Got data of " << len <<
" bytes." << endl;
00296 kdDebug() << response << endl;
00297
00298
QString s(response);
00299 IDList IDs;
00300
00301
QStringList lines = QStringList::split(
"\n", s );
00302 QStringList::iterator it;
00303
for ( it = lines.begin(); it != lines.end(); ++it ) {
00304
QString line = (*it).stripWhiteSpace().lower();
00305
if ( line.startsWith(
"subscription-id: " ) )
00306 IDs = makeIDList( line.section(
":",1).stripWhiteSpace() );
00307 }
00308
00309
if ( IDs.isEmpty() ) {
00310 kdWarning() <<
"Did not find any subscriptions in NOTIFY!" << response << endl;
00311 }
else {
00312 poll( IDs );
00313 }
00314
00315 }
00316
00317
void ExchangeMonitor::poll(
const IDList& IDs ) {
00318
00319
00320
00321
00322
00323
00324 KIO::DavJob *job =
new KIO::DavJob( mAccount->calendarURL(), (
int) KIO::DAV_POLL, QString::null,
false );
00325 job->addMetaData(
"customHTTPHeader",
"Subscription-ID: " + makeIDString( IDs ) );
00326 connect(job, SIGNAL(result( KIO::Job * )),
this, SLOT(slotPollResult(KIO::Job *)));
00327 }
00328
00329
void ExchangeMonitor::slotPollResult( KIO::Job * job )
00330 {
00331
if ( job->error() ) {
00332 job->showErrorDialog( 0L );
00333 emit error( ExchangeClient::CommunicationError,
"IO Error: " + QString::number(job->error()) +
":" + job->errorString() );
00334
return;
00335 }
00336
QDomDocument& response = static_cast<KIO::DavJob *>( job )->response();
00337 kdDebug() <<
"POLL result: " << endl << response.toString() << endl;
00338
00339
00340
QDomNodeList responses = response.documentElement().elementsByTagName(
"response" );
00341
if ( responses.count() == 0 ) {
00342 emit error( ExchangeClient::ServerResponseError,
"Poll result is wrong: \n" + response.toString() );
00343
return;
00344 }
00345
for( uint i=0; i<responses.count(); i++ ) {
00346
QDomElement item = responses.item( i ).toElement();
00347
QDomElement status = item.namedItem(
"status" ).toElement();
00348
QDomElement subscriptionID = item.namedItem(
"subscriptionID" ).toElement();
00349
if ( status.text().contains(
"200" ) ) {
00350 kdDebug() <<
"subscriptionID: " << subscriptionID.text() << endl;
00351 IDList IDs = makeIDList( subscriptionID.text() );
00352
QValueList<KURL> urls;
00353 IDList::ConstIterator it;
00354
for ( it = IDs.begin(); it != IDs.end(); ++it ) {
00355 urls += mSubscriptionMap[ *it ];
00356 }
00357 emit notify( IDs, urls );
00358 }
else if ( ! status.text().contains(
"204" ) ) {
00359 kdWarning() <<
"POLL result is not 200 or 204, what's up?" << endl;
00360 emit error( ExchangeClient::ServerResponseError,
"Poll result is wrong: \n" + response.toString() );
00361 }
00362 }
00363 }
00364
00365
void ExchangeMonitor::slotRenewTimer()
00366 {
00367 kdDebug() <<
"ExchangeMonitor::slotRenewTimer()" << endl;
00368
00369 KIO::DavJob *job =
new KIO::DavJob( mAccount->calendarURL(), (
int) KIO::DAV_SUBSCRIBE, QString::null,
false );
00370 job->addMetaData(
"customHTTPHeader",
"Subscription-id: " + makeIDString( mSubscriptionMap.keys() ) );
00371 connect(job, SIGNAL(result( KIO::Job * )),
this, SLOT(slotRenewResult(KIO::Job *)));
00372 }
00373
00374
void ExchangeMonitor::slotRenewResult( KIO::Job* job )
00375 {
00376
if ( job->error() ) {
00377 job->showErrorDialog( 0L );
00378 emit error( ExchangeClient::CommunicationError,
"IO Error: " + QString::number(job->error()) +
":" + job->errorString() );
00379
return;
00380 }
00381 kdDebug() <<
"ExchangeMonitor::slotRenewResult()" << endl;
00382
00383
00384 }
00385
00386
#include "exchangemonitor.moc"