00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "freebusymanager.h"
00039 #include "koprefs.h"
00040 #include "mailscheduler.h"
00041
00042 #include <kabc/stdaddressbook.h>
00043 #include <kabc/addressee.h>
00044
00045 #include <kcal/incidencebase.h>
00046 #include <kcal/attendee.h>
00047 #include <kcal/freebusy.h>
00048 #include <kcal/journal.h>
00049 #include <kcal/calendarlocal.h>
00050 #include <kcal/icalformat.h>
00051
00052 #include <kio/job.h>
00053 #include <kio/netaccess.h>
00054 #include <kdebug.h>
00055 #include <kmessagebox.h>
00056 #include <ktemporaryfile.h>
00057 #include <kapplication.h>
00058 #include <kconfig.h>
00059 #include <klocale.h>
00060 #include <kstandarddirs.h>
00061
00062 #include <QFile>
00063 #include <QBuffer>
00064 #include <QRegExp>
00065 #include <QDir>
00066 #include <QTimerEvent>
00067 #include <QTextStream>
00068 #include <QByteArray>
00069
00070 using namespace KCal;
00071
00072 FreeBusyDownloadJob::FreeBusyDownloadJob( const QString &email, const KUrl &url,
00073 FreeBusyManager *manager )
00074 : QObject( manager ), mManager( manager ), mEmail( email )
00075 {
00076 KIO::Job *job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00077 connect( job, SIGNAL(result(KJob *)), SLOT(slotResult(KJob *)) );
00078 connect( job, SIGNAL(data(KIO::Job *,const QByteArray &)),
00079 SLOT(slotData(KIO::Job *,const QByteArray &)) );
00080 }
00081
00082 FreeBusyDownloadJob::~FreeBusyDownloadJob()
00083 {
00084 }
00085
00086 void FreeBusyDownloadJob::slotData( KIO::Job *, const QByteArray &data )
00087 {
00088 QByteArray tmp = data;
00089 tmp.resize( tmp.size() + 1 );
00090 tmp[tmp.size()-1] = 0;
00091 mFreeBusyData += tmp;
00092 }
00093
00094 void FreeBusyDownloadJob::slotResult( KJob *job )
00095 {
00096 kDebug() << mEmail;
00097
00098 if ( job->error() ) {
00099 kDebug() << "job error :-(";
00100 }
00101
00102 FreeBusy *fb = mManager->iCalToFreeBusy( mFreeBusyData );
00103 if ( fb ) {
00104 Person p = fb->organizer();
00105 p.setEmail( mEmail );
00106 mManager->saveFreeBusy( fb, p );
00107 }
00108 emit freeBusyDownloaded( fb, mEmail );
00109 deleteLater();
00110 }
00111
00113
00114 FreeBusyManager::FreeBusyManager( QObject *parent ) :
00115 QObject( parent ),
00116 mCalendar( 0 ), mTimerID( 0 ), mUploadingFreeBusy( false ),
00117 mBrokenUrl( false )
00118 {
00119 }
00120
00121 void FreeBusyManager::setCalendar( KCal::Calendar *c )
00122 {
00123 mCalendar = c;
00124 if ( mCalendar ) {
00125 mFormat.setTimeSpec( mCalendar->timeSpec() );
00126 }
00127 }
00128
00129 KCal::FreeBusy *FreeBusyManager::ownerFreeBusy()
00130 {
00131 KDateTime start = KDateTime::currentUtcDateTime();
00132 KDateTime end = start.addDays( KOPrefs::instance()->mFreeBusyPublishDays );
00133
00134 FreeBusy *freebusy = new FreeBusy( mCalendar, start, end );
00135 freebusy->setOrganizer( Person( KOPrefs::instance()->fullName(),
00136 KOPrefs::instance()->email() ) );
00137
00138 return freebusy;
00139 }
00140
00141 QString FreeBusyManager::ownerFreeBusyAsString()
00142 {
00143 FreeBusy *freebusy = ownerFreeBusy();
00144
00145 QString result = freeBusyToIcal( freebusy );
00146
00147 delete freebusy;
00148
00149 return result;
00150 }
00151
00152 QString FreeBusyManager::freeBusyToIcal( KCal::FreeBusy *freebusy )
00153 {
00154 return mFormat.createScheduleMessage( freebusy, iTIPPublish );
00155 }
00156
00157 void FreeBusyManager::slotPerhapsUploadFB()
00158 {
00159
00160 if ( !KOPrefs::instance()->freeBusyPublishAuto() ||
00161 KOPrefs::instance()->freeBusyPublishUrl().isEmpty() ) {
00162 return;
00163 }
00164
00165 if( mTimerID != 0 ) {
00166
00167 return;
00168 }
00169
00170 int now = static_cast<int>( QDateTime::currentDateTime().toTime_t() );
00171 int eta = static_cast<int>( mNextUploadTime.toTime_t() ) - now;
00172
00173 if ( !mUploadingFreeBusy ) {
00174
00175 if ( mNextUploadTime.isNull() ||
00176 QDateTime::currentDateTime() > mNextUploadTime ) {
00177
00178 publishFreeBusy();
00179 return;
00180 }
00181
00182
00183 if ( eta <= 0 ) {
00184
00185 publishFreeBusy();
00186 return;
00187 }
00188 } else {
00189
00190 if ( eta <= 0 ) {
00191 kDebug() << "This shouldn't happen! eta <= 0";
00192 eta = 10;
00193 }
00194 }
00195
00196
00197 mTimerID = startTimer( eta * 1000 );
00198
00199 if ( mTimerID == 0 ) {
00200
00201 publishFreeBusy();
00202 }
00203 }
00204
00205
00206 void FreeBusyManager::timerEvent( QTimerEvent * )
00207 {
00208 publishFreeBusy();
00209 }
00210
00211 void FreeBusyManager::setBrokenUrl( bool isBroken )
00212 {
00213 mBrokenUrl = isBroken;
00214 }
00215
00220 void FreeBusyManager::publishFreeBusy()
00221 {
00222
00223 if ( mUploadingFreeBusy ) {
00224 return;
00225 }
00226 KUrl targetURL ( KOPrefs::instance()->freeBusyPublishUrl() );
00227 if ( targetURL.isEmpty() ) {
00228 KMessageBox::sorry(
00229 0,
00230 i18n( "<qt><p>No URL configured for uploading your free/busy list. "
00231 "Please set it in KOrganizer's configuration dialog, on the "
00232 "\"Free/Busy\" page.</p>"
00233 "<p>Contact your system administrator for the exact URL and the "
00234 "account details.</p></qt>" ),
00235 i18n( "No Free/Busy Upload URL" ) );
00236 return;
00237 }
00238
00239 if ( mBrokenUrl ) {
00240
00241 return;
00242 }
00243 if ( !targetURL.isValid() ) {
00244 KMessageBox::sorry(
00245 0,
00246 i18n( "<qt>The target URL '%1' provided is invalid.</qt>", targetURL.prettyUrl() ),
00247 i18n( "Invalid URL" ) );
00248 mBrokenUrl = true;
00249 return;
00250 }
00251 targetURL.setUser( KOPrefs::instance()->mFreeBusyPublishUser );
00252 targetURL.setPass( KOPrefs::instance()->mFreeBusyPublishPassword );
00253
00254 mUploadingFreeBusy = true;
00255
00256
00257 if ( mTimerID != 0 ) {
00258 killTimer( mTimerID );
00259 mTimerID = 0;
00260 }
00261
00262
00263 mNextUploadTime = QDateTime::currentDateTime();
00264 if ( KOPrefs::instance()->mFreeBusyPublishDelay > 0 ) {
00265 mNextUploadTime = mNextUploadTime.addSecs( KOPrefs::instance()->mFreeBusyPublishDelay * 60 );
00266 }
00267
00268 QString messageText = ownerFreeBusyAsString();
00269
00270
00271
00272 messageText = messageText.replace( QRegExp( "ORGANIZER\\s*:MAILTO:" ), "ORGANIZER:" );
00273
00274
00275 KTemporaryFile tempFile;
00276 tempFile.setAutoRemove( false );
00277 if ( tempFile.open() ) {
00278 QTextStream textStream ( &tempFile );
00279 textStream << messageText;
00280 textStream.flush();
00281
00282 #if 0
00283 QString defaultEmail = KOCore()::self()->email();
00284 QString emailHost = defaultEmail.mid( defaultEmail.indexOf( '@' ) + 1 );
00285
00286
00287 KUrl targetURL;
00288 if( KOPrefs::instance()->mPublishKolab ) {
00289
00290 QString server;
00291 if ( KOPrefs::instance()->mPublishKolabServer == QLatin1String( "%SERVER%" ) ||
00292 KOPrefs::instance()->mPublishKolabServer.isEmpty() ) {
00293 server = emailHost;
00294 } else {
00295 server = KOPrefs::instance()->mPublishKolabServer;
00296 }
00297
00298 targetURL.setProtocol( "webdavs" );
00299 targetURL.setHost( server );
00300
00301 QString fbname = KOPrefs::instance()->mPublishUserName;
00302 int at = fbname.indexOf( '@' );
00303 if ( at > 1 && fbname.length() > (uint)at ) {
00304 fbname = fbname.left(at);
00305 }
00306 targetURL.setPath( "/freebusy/" + fbname + ".ifb" );
00307 targetURL.setUser( KOPrefs::instance()->mPublishUserName );
00308 targetURL.setPass( KOPrefs::instance()->mPublishPassword );
00309 } else {
00310
00311 targetURL = KOPrefs::instance()->mPublishAnyURL.replace( "%SERVER%", emailHost );
00312 targetURL.setUser( KOPrefs::instance()->mPublishUserName );
00313 targetURL.setPass( KOPrefs::instance()->mPublishPassword );
00314 }
00315 #endif
00316
00317 KUrl src;
00318 src.setPath( tempFile.fileName() );
00319
00320 kDebug() << targetURL;
00321
00322 KIO::Job *job = KIO::file_copy( src, targetURL, -1, KIO::Overwrite | KIO::HideProgressInfo );
00323 connect( job, SIGNAL(result(KJob *)), SLOT(slotUploadFreeBusyResult(KJob *)) );
00324 }
00325 }
00326
00327 void FreeBusyManager::slotUploadFreeBusyResult( KJob *_job )
00328 {
00329 KIO::FileCopyJob *job = static_cast<KIO::FileCopyJob *>( _job );
00330 if ( job->error() ) {
00331 KMessageBox::sorry(
00332 0,
00333 i18n( "<qt><p>The software could not upload your free/busy list to "
00334 "the URL '%1'. There might be a problem with the access "
00335 "rights, or you specified an incorrect URL. The system said: "
00336 "<em>%2</em>.</p>"
00337 "<p>Please check the URL or contact your system administrator."
00338 "</p></qt>", job->destUrl().prettyUrl(),
00339 job->errorString() ) );
00340 }
00341
00342 KUrl src = job->srcUrl();
00343 Q_ASSERT( src.isLocalFile() );
00344 if ( src.isLocalFile() ) {
00345 QFile::remove( src.path() );
00346 }
00347 mUploadingFreeBusy = false;
00348 }
00349
00350 bool FreeBusyManager::retrieveFreeBusy( const QString &email, bool forceDownload )
00351 {
00352 kDebug() << email;
00353 if ( email.isEmpty() ) {
00354 return false;
00355 }
00356
00357 if ( KOPrefs::instance()->thatIsMe( email ) ) {
00358
00359 kDebug() << "freebusy of owner";
00360 emit freeBusyRetrieved( ownerFreeBusy(), email );
00361 return true;
00362 }
00363
00364
00365 KCal::FreeBusy *fb = loadFreeBusy( email );
00366 if ( fb ) {
00367 emit freeBusyRetrieved( fb, email );
00368 }
00369
00370
00371 if ( !KOPrefs::instance()->mFreeBusyRetrieveAuto && !forceDownload ) {
00372 return false;
00373 }
00374
00375 mRetrieveQueue.append( email );
00376
00377 if ( mRetrieveQueue.count() > 1 ) {
00378 return true;
00379 }
00380
00381 return processRetrieveQueue();
00382 }
00383
00384 bool FreeBusyManager::processRetrieveQueue()
00385 {
00386 if ( mRetrieveQueue.isEmpty() ) {
00387 return true;
00388 }
00389
00390 QString email = mRetrieveQueue.first();
00391 mRetrieveQueue.pop_front();
00392
00393 KUrl sourceURL = freeBusyUrl( email );
00394
00395 kDebug() << "url:" << sourceURL;
00396
00397 if ( !sourceURL.isValid() ) {
00398 kDebug() << "Invalid FB URL";
00399 return false;
00400 }
00401
00402 FreeBusyDownloadJob *job = new FreeBusyDownloadJob( email, sourceURL, this );
00403 job->setObjectName( "freebusy_download_job" );
00404 connect( job, SIGNAL(freeBusyDownloaded(KCal::FreeBusy *,const QString &)),
00405 SIGNAL(freeBusyRetrieved(KCal::FreeBusy *,const QString &)) );
00406 connect( job, SIGNAL(freeBusyDownloaded(KCal::FreeBusy *,const QString &)),
00407 SLOT(processRetrieveQueue()) );
00408
00409 return true;
00410 }
00411
00412 void FreeBusyManager::cancelRetrieval()
00413 {
00414 mRetrieveQueue.clear();
00415 }
00416
00417 KUrl FreeBusyManager::freeBusyUrl( const QString &email ) const
00418 {
00419 kDebug() << email;
00420
00421
00422 QString configFile = KStandardDirs::locateLocal( "data", "korganizer/freebusyurls" );
00423 KConfig cfg( configFile );
00424 KConfigGroup group = cfg.group(email);
00425 QString url = group.readEntry( "url" );
00426 if ( !url.isEmpty() ) {
00427 return KUrl( url );
00428 }
00429
00430 KABC::Addressee::List list= KABC::StdAddressBook::self( true )->findByEmail( email );
00431 KABC::Addressee::List::Iterator it;
00432 QString pref;
00433 for ( it = list.begin(); it != list.end(); ++it ) {
00434 pref = (*it).preferredEmail();
00435 if ( !pref.isEmpty() && pref != email ) {
00436 kDebug() << "Preferred email of" << email << "is" << pref;
00437 group = cfg.group( pref );
00438 url = group.readEntry ( "url" );
00439 if ( !url.isEmpty() ) {
00440 kDebug() << "Taken url from preferred email:" << url;
00441 return KUrl( url );
00442 }
00443 }
00444 }
00445
00446 if ( !KOPrefs::instance()->mFreeBusyRetrieveAuto ) {
00447
00448 return KUrl();
00449 }
00450
00451
00452
00453 int emailpos = email.indexOf( '@' );
00454 if( emailpos == -1 ) {
00455 kDebug() << "No '@' found in" << email;
00456 return KUrl();
00457 }
00458
00459
00460 const QString emailName = email.left( emailpos );
00461 const QString emailHost = email.mid( emailpos + 1 );
00462
00463
00464 KUrl sourceURL;
00465 sourceURL = KOPrefs::instance()->mFreeBusyRetrieveUrl;
00466
00467 if ( KOPrefs::instance()->mFreeBusyCheckHostname ) {
00468
00469
00470 const QString hostDomain = sourceURL.host();
00471 if ( hostDomain != emailHost &&
00472 !hostDomain.endsWith( '.' + emailHost ) &&
00473 !emailHost.endsWith( '.' + hostDomain ) ) {
00474
00475 kDebug() << "Host '" << sourceURL.host()
00476 << "' doesn't match email '" << email << '\'';
00477 return KUrl();
00478 }
00479 }
00480
00481 if ( KOPrefs::instance()->mFreeBusyFullDomainRetrieval ) {
00482 sourceURL.setFileName( email + ".ifb" );
00483 } else {
00484 sourceURL.setFileName( emailName + ".ifb" );
00485 }
00486 sourceURL.setUser( KOPrefs::instance()->mFreeBusyRetrieveUser );
00487 sourceURL.setPass( KOPrefs::instance()->mFreeBusyRetrievePassword );
00488
00489 return sourceURL;
00490 }
00491
00492 KCal::FreeBusy *FreeBusyManager::iCalToFreeBusy( const QByteArray &data )
00493 {
00494 kDebug() << data;
00495
00496 QString freeBusyVCal = QString::fromUtf8( data );
00497 KCal::FreeBusy *fb = mFormat.parseFreeBusy( freeBusyVCal );
00498 if ( !fb ) {
00499 kDebug() << "Error parsing free/busy";
00500 kDebug() << freeBusyVCal;
00501 }
00502 return fb;
00503 }
00504
00505 QString FreeBusyManager::freeBusyDir()
00506 {
00507 return KStandardDirs::locateLocal( "data", "korganizer/freebusy" );
00508 }
00509
00510 FreeBusy *FreeBusyManager::loadFreeBusy( const QString &email )
00511 {
00512 kDebug() << email;
00513
00514 QString fbd = freeBusyDir();
00515
00516 QFile f( fbd + '/' + email + ".ifb" );
00517 if ( !f.exists() ) {
00518 kDebug() << f.fileName() << "doesn't exist.";
00519 return 0;
00520 }
00521
00522 if ( !f.open( QIODevice::ReadOnly ) ) {
00523 kDebug() << "Unable to open file" << f.fileName();
00524 return 0;
00525 }
00526
00527 QTextStream ts( &f );
00528 QString str = ts.readAll();
00529
00530 return iCalToFreeBusy( str.toUtf8() );
00531 }
00532
00533 bool FreeBusyManager::saveFreeBusy( FreeBusy *freebusy, const Person &person )
00534 {
00535 kDebug() << person.fullName();
00536
00537 QString fbd = freeBusyDir();
00538
00539 QDir freeBusyDirectory( fbd );
00540 if ( !freeBusyDirectory.exists() ) {
00541 kDebug() << "Directory" << fbd <<" does not exist!";
00542 kDebug() << "Creating directory:" << fbd;
00543
00544 if( !freeBusyDirectory.mkpath( fbd ) ) {
00545 kDebug() << "Could not create directory:" << fbd;
00546 return false;
00547 }
00548 }
00549
00550 QString filename( fbd );
00551 filename += '/';
00552 filename += person.email();
00553 filename += ".ifb";
00554 QFile f( filename );
00555
00556 kDebug() << "filename:" << filename;
00557
00558 freebusy->clearAttendees();
00559 freebusy->setOrganizer( person );
00560
00561 QString messageText = mFormat.createScheduleMessage( freebusy, iTIPPublish );
00562
00563 if ( !f.open( QIODevice::ReadWrite ) ) {
00564 kDebug() << "acceptFreeBusy: Can't open:" << filename << "for writing";
00565 return false;
00566 }
00567 QTextStream t( &f );
00568 t << messageText;
00569 f.close();
00570
00571 return true;
00572 }
00573
00574 #include "freebusymanager.moc"