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
00040 #include "koprefs.h"
00041 #include "mailscheduler.h"
00042 #include "actionmanager.h"
00043 #include "korganizer.h"
00044
00045 #include <libkcal/incidencebase.h>
00046 #include <libkcal/attendee.h>
00047 #include <libkcal/freebusy.h>
00048 #include <libkcal/journal.h>
00049 #include <libkcal/calendarlocal.h>
00050 #include <libkcal/icalformat.h>
00051
00052 #include <kio/job.h>
00053 #include <kdebug.h>
00054 #include <kmessagebox.h>
00055 #include <ktempfile.h>
00056 #include <kio/jobclasses.h>
00057 #include <kio/netaccess.h>
00058 #include <kio/scheduler.h>
00059 #include <kapplication.h>
00060 #include <kconfig.h>
00061 #include <klocale.h>
00062 #include <kstandarddirs.h>
00063 #include <kabc/stdaddressbook.h>
00064 #include <kabc/addressee.h>
00065
00066 #include <qfile.h>
00067 #include <qbuffer.h>
00068 #include <qregexp.h>
00069 #include <qdir.h>
00070
00071 using namespace KCal;
00072
00073 FreeBusyDownloadJob::FreeBusyDownloadJob( const QString &email, const KURL &url,
00074 FreeBusyManager *manager,
00075 const char *name )
00076 : QObject( manager, name ), mManager( manager ), mEmail( email )
00077 {
00078 KIO::TransferJob *job = KIO::get( url, false, false );
00079
00080 KOrg::MainWindow *korg = ActionManager::findInstance( KURL() );
00081 job->setWindow( korg->topLevelWidget() );
00082
00083 connect( job, SIGNAL( result( KIO::Job * ) ),
00084 SLOT( slotResult( KIO::Job * ) ) );
00085 connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
00086 SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
00087 KIO::Scheduler::scheduleJob( job );
00088 }
00089
00090 FreeBusyDownloadJob::~FreeBusyDownloadJob()
00091 {
00092 }
00093
00094
00095 void FreeBusyDownloadJob::slotData( KIO::Job *, const QByteArray &data )
00096 {
00097 QByteArray tmp = data;
00098 tmp.resize( tmp.size() + 1 );
00099 tmp[tmp.size()-1] = 0;
00100 mFreeBusyData += tmp;
00101 }
00102
00103 void FreeBusyDownloadJob::slotResult( KIO::Job *job )
00104 {
00105 kdDebug(5850) << "FreeBusyDownloadJob::slotResult() " << mEmail << endl;
00106
00107 if( job->error() ) {
00108 kdDebug(5850) << "FreeBusyDownloadJob::slotResult() job error for " << mEmail << endl;
00109 emit freeBusyDownloadError( mEmail );
00110 } else {
00111 FreeBusy *fb = mManager->iCalToFreeBusy( mFreeBusyData );
00112 if ( fb ) {
00113 Person p = fb->organizer();
00114 p.setEmail( mEmail );
00115 mManager->saveFreeBusy( fb, p );
00116 }
00117 emit freeBusyDownloaded( fb, mEmail );
00118 }
00119 deleteLater();
00120 }
00121
00123
00124 FreeBusyManager::FreeBusyManager( QObject *parent, const char *name )
00125 : QObject( parent, name ),
00126 mCalendar( 0 ), mTimerID( 0 ), mUploadingFreeBusy( false ),
00127 mBrokenUrl( false )
00128 {
00129 }
00130
00131 void FreeBusyManager::setCalendar( KCal::Calendar *c )
00132 {
00133 mCalendar = c;
00134 if ( mCalendar ) {
00135 mFormat.setTimeZone( mCalendar->timeZoneId(), true );
00136 }
00137 }
00138
00139 KCal::FreeBusy *FreeBusyManager::ownerFreeBusy()
00140 {
00141 QDateTime start = QDateTime::currentDateTime();
00142 QDateTime end = start.addDays( KOPrefs::instance()->mFreeBusyPublishDays );
00143
00144 FreeBusy *freebusy = new FreeBusy( mCalendar, start, end );
00145 freebusy->setOrganizer( Person( KOPrefs::instance()->fullName(),
00146 KOPrefs::instance()->email() ) );
00147
00148 return freebusy;
00149 }
00150
00151 QString FreeBusyManager::ownerFreeBusyAsString()
00152 {
00153 FreeBusy *freebusy = ownerFreeBusy();
00154
00155 QString result = freeBusyToIcal( freebusy );
00156
00157 delete freebusy;
00158
00159 return result;
00160 }
00161
00162 QString FreeBusyManager::freeBusyToIcal( KCal::FreeBusy *freebusy )
00163 {
00164 return mFormat.createScheduleMessage( freebusy, Scheduler::Publish );
00165 }
00166
00167 void FreeBusyManager::slotPerhapsUploadFB()
00168 {
00169
00170 if ( !KOPrefs::instance()->freeBusyPublishAuto() ||
00171 KOPrefs::instance()->freeBusyPublishUrl().isEmpty() )
00172 return;
00173 if( mTimerID != 0 )
00174
00175 return;
00176
00177 int now = static_cast<int>( QDateTime::currentDateTime().toTime_t() );
00178 int eta = static_cast<int>( mNextUploadTime.toTime_t() ) - now;
00179
00180 if( !mUploadingFreeBusy ) {
00181
00182 if( mNextUploadTime.isNull() ||
00183 QDateTime::currentDateTime() > mNextUploadTime ) {
00184
00185 publishFreeBusy();
00186 return;
00187 }
00188
00189
00190 if( eta <= 0 ) {
00191
00192 publishFreeBusy();
00193 return;
00194 }
00195 } else {
00196
00197 if( eta <= 0 ) {
00198 kdDebug(5850) << "This shouldn't happen! eta <= 0\n";
00199 eta = 10;
00200 }
00201 }
00202
00203
00204 mTimerID = startTimer( eta * 1000 );
00205
00206 if( mTimerID == 0 )
00207
00208 publishFreeBusy();
00209 }
00210
00211
00212 void FreeBusyManager::timerEvent( QTimerEvent* )
00213 {
00214 publishFreeBusy();
00215 }
00216
00217 void FreeBusyManager::setBrokenUrl( bool isBroken )
00218 {
00219 mBrokenUrl = isBroken;
00220 }
00221
00226 void FreeBusyManager::publishFreeBusy()
00227 {
00228
00229 if ( mUploadingFreeBusy )
00230 return;
00231 KURL targetURL( KOPrefs::instance()->freeBusyPublishUrl() );
00232 if ( targetURL.isEmpty() ) {
00233 KMessageBox::sorry( 0,
00234 i18n( "<qt>No URL configured for uploading your free/busy list. Please "
00235 "set it in KOrganizer's configuration dialog, on the \"Free/Busy\" page. "
00236 "<br>Contact your system administrator for the exact URL and the "
00237 "account details."
00238 "</qt>" ), i18n("No Free/Busy Upload URL") );
00239 return;
00240 }
00241 if ( mBrokenUrl )
00242 return;
00243 if ( !targetURL.isValid() ) {
00244 KMessageBox::sorry( 0,
00245 i18n( "<qt>The target URL '%1' provided is invalid."
00246 "</qt>" ).arg( targetURL.prettyURL() ), i18n("Invalid URL") );
00247 mBrokenUrl = true;
00248 return;
00249 }
00250 targetURL.setUser( KOPrefs::instance()->mFreeBusyPublishUser );
00251 targetURL.setPass( KOPrefs::instance()->mFreeBusyPublishPassword );
00252
00253 mUploadingFreeBusy = true;
00254
00255
00256 if( mTimerID != 0 ) {
00257 killTimer( mTimerID );
00258 mTimerID = 0;
00259 }
00260
00261
00262 mNextUploadTime = QDateTime::currentDateTime();
00263 if( KOPrefs::instance()->mFreeBusyPublishDelay > 0 )
00264 mNextUploadTime = mNextUploadTime.addSecs(
00265 KOPrefs::instance()->mFreeBusyPublishDelay * 60 );
00266
00267 QString messageText = ownerFreeBusyAsString();
00268
00269
00270
00271 messageText = messageText.replace( QRegExp( "ORGANIZER\\s*:MAILTO:" ),
00272 "ORGANIZER:" );
00273
00274
00275 KTempFile tempFile;
00276 QTextStream *textStream = tempFile.textStream();
00277 if( textStream ) {
00278 *textStream << messageText;
00279 tempFile.close();
00280
00281 #if 0
00282 QString defaultEmail = KOCore()::self()->email();
00283 QString emailHost = defaultEmail.mid( defaultEmail.find( '@' ) + 1 );
00284
00285
00286 KURL targetURL;
00287 if( KOPrefs::instance()->mPublishKolab ) {
00288
00289 QString server;
00290 if( KOPrefs::instance()->mPublishKolabServer == "%SERVER%" ||
00291 KOPrefs::instance()->mPublishKolabServer.isEmpty() )
00292 server = emailHost;
00293 else
00294 server = KOPrefs::instance()->mPublishKolabServer;
00295
00296 targetURL.setProtocol( "webdavs" );
00297 targetURL.setHost( server );
00298
00299 QString fbname = KOPrefs::instance()->mPublishUserName;
00300 int at = fbname.find('@');
00301 if( at > 1 && fbname.length() > (uint)at ) {
00302 fbname = fbname.left(at);
00303 }
00304 targetURL.setPath( "/freebusy/" + fbname + ".ifb" );
00305 targetURL.setUser( KOPrefs::instance()->mPublishUserName );
00306 targetURL.setPass( KOPrefs::instance()->mPublishPassword );
00307 } else {
00308
00309 targetURL = KOPrefs::instance()->mPublishAnyURL.replace( "%SERVER%",
00310 emailHost );
00311 targetURL.setUser( KOPrefs::instance()->mPublishUserName );
00312 targetURL.setPass( KOPrefs::instance()->mPublishPassword );
00313 }
00314 #endif
00315
00316
00317 KURL src;
00318 src.setPath( tempFile.name() );
00319
00320 kdDebug(5850) << "FreeBusyManager::publishFreeBusy(): " << targetURL << endl;
00321
00322 KIO::Job * job = KIO::file_copy( src, targetURL, -1,
00323 true ,
00324 false ,
00325 false );
00326
00327 KOrg::MainWindow *korg = ActionManager::findInstance( KURL() );
00328 job->setWindow( korg->topLevelWidget() );
00329
00330 connect( job, SIGNAL( result( KIO::Job * ) ),
00331 SLOT( slotUploadFreeBusyResult( KIO::Job * ) ) );
00332 }
00333 }
00334
00335 void FreeBusyManager::slotUploadFreeBusyResult(KIO::Job *_job)
00336 {
00337 KIO::FileCopyJob* job = static_cast<KIO::FileCopyJob *>(_job);
00338 if ( job->error() )
00339 KMessageBox::sorry( 0,
00340 i18n( "<qt>The software could not upload your free/busy list to the "
00341 "URL '%1'. There might be a problem with the access rights, or "
00342 "you specified an incorrect URL. The system said: <em>%2</em>."
00343 "<br>Please check the URL or contact your system administrator."
00344 "</qt>" ).arg( job->destURL().prettyURL() )
00345 .arg( job->errorString() ) );
00346
00347 KURL src = job->srcURL();
00348 Q_ASSERT( src.isLocalFile() );
00349 if( src.isLocalFile() )
00350 QFile::remove(src.path());
00351 mUploadingFreeBusy = false;
00352 }
00353
00354 bool FreeBusyManager::retrieveFreeBusy( const QString &email, bool forceDownload )
00355 {
00356 kdDebug(5850) << "FreeBusyManager::retrieveFreeBusy(): " << email << endl;
00357 if ( email.isEmpty() ) return false;
00358
00359
00360 KCal::FreeBusy *fb = loadFreeBusy( email );
00361 if ( fb ) {
00362 emit freeBusyRetrieved( fb, email );
00363 }
00364
00365
00366 if( !KOPrefs::instance()->mFreeBusyRetrieveAuto && !forceDownload) {
00367 slotFreeBusyDownloadError( email );
00368 return false;
00369 }
00370
00371 mRetrieveQueue.append( email );
00372
00373 if ( mRetrieveQueue.count() > 1 ) return true;
00374
00375 return processRetrieveQueue();
00376 }
00377
00378 bool FreeBusyManager::processRetrieveQueue()
00379 {
00380 if ( mRetrieveQueue.isEmpty() ) return true;
00381
00382 QString email = mRetrieveQueue.first();
00383 mRetrieveQueue.pop_front();
00384
00385 KURL sourceURL = freeBusyUrl( email );
00386
00387 kdDebug(5850) << "FreeBusyManager::processRetrieveQueue(): url: " << sourceURL << endl;
00388
00389 if ( !sourceURL.isValid() ) {
00390 kdDebug(5850) << "Invalid FB URL\n";
00391 slotFreeBusyDownloadError( email );
00392 return false;
00393 }
00394
00395 FreeBusyDownloadJob *job = new FreeBusyDownloadJob( email, sourceURL, this,
00396 "freebusy_download_job" );
00397 connect( job, SIGNAL( freeBusyDownloaded( KCal::FreeBusy *,
00398 const QString & ) ),
00399 SIGNAL( freeBusyRetrieved( KCal::FreeBusy *, const QString & ) ) );
00400 connect( job, SIGNAL( freeBusyDownloaded( KCal::FreeBusy *,
00401 const QString & ) ),
00402 SLOT( processRetrieveQueue() ) );
00403
00404 connect( job, SIGNAL( freeBusyDownloadError( const QString& ) ),
00405 this, SLOT( slotFreeBusyDownloadError( const QString& ) ) );
00406
00407 return true;
00408 }
00409
00410 void FreeBusyManager::slotFreeBusyDownloadError( const QString& email )
00411 {
00412 if( KOPrefs::instance()->thatIsMe( email ) ) {
00413
00414
00415
00416
00417
00418 kdDebug(5850) << "freebusy of owner, falling back to local list" << endl;
00419 emit freeBusyRetrieved( ownerFreeBusy(), email );
00420 }
00421
00422 }
00423
00424 void FreeBusyManager::cancelRetrieval()
00425 {
00426 mRetrieveQueue.clear();
00427 }
00428
00429 KURL replaceVariablesURL( const KURL &url, const QString &email )
00430 {
00431 QString emailName, emailHost;
00432 int emailpos = email.find( '@' );
00433 if( emailpos >= 0 ) {
00434 emailName = email.left( emailpos );
00435 emailHost = email.mid( emailpos + 1 );
00436 }
00437
00438 QString saveStr = url.path();
00439 saveStr.replace( QRegExp( "%[Ee][Mm][Aa][Ii][Ll]%" ), email );
00440 saveStr.replace( QRegExp( "%[Nn][Aa][Mm][Ee]%" ), emailName );
00441 saveStr.replace( QRegExp( "%[Ss][Ee][Rr][Vv][Ee][Rr]%" ), emailHost );
00442
00443 KURL retUrl( url );
00444 retUrl.setPath( saveStr );
00445 return retUrl;
00446 }
00447
00448 bool fbExists( const KURL &url )
00449 {
00450
00451
00452
00453
00454
00455 KIO::Job *job = KIO::get( url, false, false );
00456 QByteArray data;
00457 if ( KIO::NetAccess::synchronousRun( job, 0, &data ) ) {
00458 QString dataStr ( data );
00459 if ( dataStr.contains( "BEGIN:VCALENDAR" ) ) {
00460 return true;
00461 }
00462 }
00463 return false;
00464 }
00465
00466 KURL FreeBusyManager::freeBusyUrl( const QString &email )
00467 {
00468 kdDebug(5850) << "FreeBusyManager::freeBusyUrl(): " << email << endl;
00469
00470
00471 QString configFile = locateLocal( "data", "korganizer/freebusyurls" );
00472 KConfig cfg( configFile );
00473
00474 cfg.setGroup( email );
00475 QString url = cfg.readEntry( "url" );
00476 if ( !url.isEmpty() ) {
00477 kdDebug(5850) << "found cached url: " << url << endl;
00478 KURL cachedURL( url );
00479 if ( KOPrefs::instance()->thatIsMe( email ) ) {
00480 cachedURL.setUser( KOPrefs::instance()->mFreeBusyRetrieveUser );
00481 cachedURL.setPass( KOPrefs::instance()->mFreeBusyRetrievePassword );
00482 }
00483 return replaceVariablesURL( cachedURL, email );
00484 }
00485
00486
00487 KABC::Addressee::List list= KABC::StdAddressBook::self( true )->findByEmail( email );
00488 KABC::Addressee::List::Iterator it;
00489 QString pref;
00490 for ( it = list.begin(); it != list.end(); ++it ) {
00491 pref = (*it).preferredEmail();
00492 if ( !pref.isEmpty() && pref != email ) {
00493 kdDebug(5850) << "FreeBusyManager::freeBusyUrl():"
00494 << "Preferred email of " << email << " is " << pref << endl;
00495 cfg.setGroup( pref );
00496 url = cfg.readEntry ( "url" );
00497 if ( !url.isEmpty() ) {
00498 kdDebug(5850) << "FreeBusyManager::freeBusyUrl():"
00499 << "Taken url from preferred email:" << url << endl;
00500 return replaceVariablesURL( KURL( url ), email );
00501 }
00502 }
00503 }
00504
00505 if ( !KOPrefs::instance()->mFreeBusyRetrieveAuto ) {
00506 kdDebug( 5850 ) << "no auto retrieving" << endl;
00507
00508 return KURL();
00509 }
00510
00511
00512
00513 int emailpos = email.find( '@' );
00514 if( emailpos == -1 ) {
00515 return KURL();
00516 }
00517
00518
00519 const QString emailName = email.left( emailpos );
00520 const QString emailHost = email.mid( emailpos + 1 );
00521
00522
00523 KURL sourceURL;
00524 sourceURL = KOPrefs::instance()->mFreeBusyRetrieveUrl;
00525
00526 if ( KOPrefs::instance()->mFreeBusyCheckHostname ) {
00527
00528
00529 const QString hostDomain = sourceURL.host();
00530 if ( hostDomain != emailHost &&
00531 !hostDomain.endsWith( '.' + emailHost ) &&
00532 !emailHost.endsWith( '.' + hostDomain ) ) {
00533
00534 kdDebug(5850) << "Host '" << sourceURL.host() << "' doesn't match email '" << email << endl;
00535 return KURL();
00536 }
00537 }
00538
00539 if ( sourceURL.url().contains( QRegExp( "\\.[xiv]fb$" ) ) ) {
00540
00541 KURL fullpathURL = replaceVariablesURL( sourceURL, email );
00542
00543
00544 fullpathURL.setUser( KOPrefs::instance()->mFreeBusyRetrieveUser );
00545 fullpathURL.setPass( KOPrefs::instance()->mFreeBusyRetrievePassword );
00546
00547
00548
00549
00550 return fullpathURL;
00551 }
00552
00553
00554
00555 QStringList extensions;
00556 extensions << "xfb" << "ifb" << "vfb";
00557 QStringList::ConstIterator ext;
00558 for ( ext = extensions.constBegin(); ext != extensions.constEnd(); ++ext ) {
00559
00560 sourceURL = KOPrefs::instance()->mFreeBusyRetrieveUrl;
00561 KURL dirURL = replaceVariablesURL( sourceURL, email );
00562 if ( KOPrefs::instance()->mFreeBusyFullDomainRetrieval ) {
00563 dirURL.addPath( email + '.' + (*ext) );
00564 } else {
00565 dirURL.addPath( emailName + '.' + (*ext ) );
00566 }
00567 dirURL.setUser( KOPrefs::instance()->mFreeBusyRetrieveUser );
00568 dirURL.setPass( KOPrefs::instance()->mFreeBusyRetrievePassword );
00569 if ( fbExists( dirURL ) ) {
00570
00571 cfg.setGroup( email );
00572 cfg.writeEntry( "url", dirURL.prettyURL() );
00573 return dirURL;
00574 }
00575 }
00576
00577 return KURL();
00578 }
00579
00580 KCal::FreeBusy *FreeBusyManager::iCalToFreeBusy( const QCString &data )
00581 {
00582 kdDebug(5850) << "FreeBusyManager::iCalToFreeBusy()" << endl;
00583
00584
00585 QString freeBusyVCal = QString::fromUtf8( data );
00586 KCal::FreeBusy *fb = mFormat.parseFreeBusy( freeBusyVCal );
00587 if ( !fb ) {
00588 kdDebug(5850) << "FreeBusyManager::iCalToFreeBusy(): Error parsing free/busy"
00589 << endl;
00590 kdDebug(5850) << freeBusyVCal << endl;
00591 }
00592 return fb;
00593 }
00594
00595 QString FreeBusyManager::freeBusyDir()
00596 {
00597 return locateLocal( "data", "korganizer/freebusy" );
00598 }
00599
00600 FreeBusy *FreeBusyManager::loadFreeBusy( const QString &email )
00601 {
00602 kdDebug(5850) << "FreeBusyManager::loadFreeBusy(): " << email << endl;
00603
00604 QString fbd = freeBusyDir();
00605
00606 QFile f( fbd + "/" + email + ".ifb" );
00607 if ( !f.exists() ) {
00608 kdDebug(5850) << "FreeBusyManager::loadFreeBusy() " << f.name()
00609 << " doesn't exist." << endl;
00610 return 0;
00611 }
00612
00613 if ( !f.open( IO_ReadOnly ) ) {
00614 kdDebug(5850) << "FreeBusyManager::loadFreeBusy() Unable to open file "
00615 << f.name() << endl;
00616 return 0;
00617 }
00618
00619 QTextStream ts( &f );
00620 QString str = ts.read();
00621
00622 return iCalToFreeBusy( str.utf8() );
00623 }
00624
00625 bool FreeBusyManager::saveFreeBusy( FreeBusy *freebusy, const Person &person )
00626 {
00627 kdDebug(5850) << "FreeBusyManager::saveFreeBusy(): " << person.fullName() << endl;
00628
00629 QString fbd = freeBusyDir();
00630
00631 QDir freeBusyDirectory( fbd );
00632 if ( !freeBusyDirectory.exists() ) {
00633 kdDebug(5850) << "Directory " << fbd << " does not exist!" << endl;
00634 kdDebug(5850) << "Creating directory: " << fbd << endl;
00635
00636 if( !freeBusyDirectory.mkdir( fbd, true ) ) {
00637 kdDebug(5850) << "Could not create directory: " << fbd << endl;
00638 return false;
00639 }
00640 }
00641
00642 QString filename( fbd );
00643 filename += "/";
00644 filename += person.email();
00645 filename += ".ifb";
00646 QFile f( filename );
00647
00648 kdDebug(5850) << "FreeBusyManager::saveFreeBusy(): filename: " << filename
00649 << endl;
00650
00651 freebusy->clearAttendees();
00652 freebusy->setOrganizer( person );
00653
00654 QString messageText = mFormat.createScheduleMessage( freebusy,
00655 Scheduler::Publish );
00656
00657 if ( !f.open( IO_ReadWrite ) ) {
00658 kdDebug(5850) << "acceptFreeBusy: Can't open:" << filename << " for writing"
00659 << endl;
00660 return false;
00661 }
00662 QTextStream t( &f );
00663 t << messageText;
00664 f.close();
00665
00666 return true;
00667 }
00668
00669 #include "freebusymanager.moc"