libkpimexchange Library API Documentation

exchangemonitor.cpp

00001 /* 00002 This file is part of libkpimexchange 00003 Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, USA. 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; // by default, renew subscription every 3600 seconds or one hour 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 //mSocket.setSocketFlags( KExtendedSocket::inetSocket | KExtendedSocket::passiveSocket | KExtendedSocket::datagramSocket | KExtendedSocket::bufferedSocket ); 00100 //mSocket.setHost( "jupiter.tbm.tudelft.nl" ); // Does this work? 00101 //mSocket.setPort( 0 ); // setting port to 0 will make us bind to a random, free port 00102 // UDP server socket: no listen 00103 //if ( int code = mSocket.listen() ) 00104 // kdError() << "Error in socket listen: " << code << endl; 00105 //mSocket.enableRead( true ); 00106 kdDebug() << "Port: " << mSocket->port() << endl; 00107 kdDebug() << "Host: " << mSocket->address().toString() << endl; 00108 // mStream = new QTextStream( mSocket ); 00109 } 00110 00111 if ( mPollMode == Poll ) { 00112 mPollTimer = new QTimer( this ); 00113 connect( mPollTimer, SIGNAL(timeout()), this, SLOT(slotPollTimer()) ); 00114 mPollTimer->start( 60000 ); // 1 minute timer 00115 } 00116 00117 mRenewTimer = new QTimer( this ); 00118 connect( mRenewTimer, SIGNAL(timeout()), this, SLOT(slotRenewTimer()) ); 00119 mRenewTimer->start( mSubscriptionLifetime * 900 ); // 10% early so as to be in time 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 // Can't do, this is a destructor! 00135 // job->addMetaData( "PropagateHttpHeader", "true" ); 00136 // connect(job, SIGNAL(result( KIO::Job * )), this, SLOT(slotUnsubscribeResult(KIO::Job *))); 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 // FIXME: Check what did subscription means 00319 // if ( id != mSubscriptionId ) { 00320 // kdDebug() << "Don't know subscription id " << id << endl; 00321 // } 00322 00323 // confirm it 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 // Multiple results! 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 // FIXME: check for new subscription lifetime 00384 } 00385 00386 #include "exchangemonitor.moc"
KDE Logo
This file is part of the documentation for libkpimexchange Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Oct 1 15:19:07 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003