korganizer Library API Documentation

freebusymanager.cpp

00001 /*
00002   This file is part of the Groupware/KOrganizer integration.
00003 
00004   Requires the Qt and KDE widget libraries, available at no cost at
00005   http://www.trolltech.com and http://www.kde.org respectively
00006 
00007   Copyright (c) 2002-2004 Klarälvdalens Datakonsult AB
00008         <info@klaralvdalens-datakonsult.se>
00009   Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
00010 
00011   This program is free software; you can redistribute it and/or modify
00012   it under the terms of the GNU General Public License as published by
00013   the Free Software Foundation; either version 2 of the License, or
00014   (at your option) any later version.
00015 
00016   This program is distributed in the hope that it will be useful,
00017   but WITHOUT ANY WARRANTY; without even the implied warranty of
00018   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00019   GNU General Public License for more details.
00020 
00021   You should have received a copy of the GNU General Public License
00022   along with this program; if not, write to the Free Software
00023   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00024   MA  02111-1307, USA.
00025 
00026   In addition, as a special exception, the copyright holders give
00027   permission to link the code of this program with any edition of
00028   the Qt library by Trolltech AS, Norway (or with modified versions
00029   of Qt that use the same license as Qt), and distribute linked
00030   combinations including the two.  You must obey the GNU General
00031   Public License in all respects for all of the code used other than
00032   Qt.  If you modify this file, you may extend this exception to
00033   your version of the file, but you are not obligated to do so.  If
00034   you do not wish to do so, delete this exception statement from
00035   your version.
00036 */
00037 
00038 #include "freebusymanager.h"
00039 
00040 #include "koprefs.h"
00041 #include "mailscheduler.h"
00042 
00043 #include <libkcal/incidencebase.h>
00044 #include <libkcal/attendee.h>
00045 #include <libkcal/freebusy.h>
00046 #include <libkcal/journal.h>
00047 #include <libkcal/calendarlocal.h>
00048 #include <libkcal/icalformat.h>
00049 
00050 #include <kio/job.h>
00051 #include <kdebug.h>
00052 #include <kmessagebox.h>
00053 #include <ktempfile.h>
00054 #include <kio/netaccess.h>
00055 #include <kapplication.h>
00056 #include <kconfig.h>
00057 #include <klocale.h>
00058 #include <kstandarddirs.h>
00059 
00060 #include <qfile.h>
00061 #include <qbuffer.h>
00062 #include <qregexp.h>
00063 #include <qdir.h>
00064 
00065 using namespace KCal;
00066 
00067 FreeBusyDownloadJob::FreeBusyDownloadJob( const QString &email, const KURL &url,
00068                                           FreeBusyManager *manager,
00069                                           const char *name )
00070   : QObject( manager, name ), mManager( manager ), mEmail( email )
00071 {
00072   KIO::Job *job = KIO::get( url, false, false );
00073   connect( job, SIGNAL( result( KIO::Job * ) ),
00074            SLOT( slotResult( KIO::Job * ) ) );
00075   connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
00076            SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
00077 }
00078 
00079 FreeBusyDownloadJob::~FreeBusyDownloadJob()
00080 {
00081 }
00082 
00083 
00084 void FreeBusyDownloadJob::slotData( KIO::Job *, const QByteArray &data )
00085 {
00086   QByteArray tmp = data;
00087   tmp.resize( tmp.size() + 1 );
00088   tmp[tmp.size()-1] = 0;
00089   mFreeBusyData += tmp;
00090 }
00091 
00092 void FreeBusyDownloadJob::slotResult( KIO::Job *job )
00093 {
00094   kdDebug(5850) << "FreeBusyDownloadJob::slotResult() " << mEmail << endl;
00095 
00096   if( job->error() ) {
00097     kdDebug(5850) << "FreeBusyDownloadJob::slotResult() job error :-(" << endl;
00098   }
00099 
00100   FreeBusy *fb = mManager->iCalToFreeBusy( mFreeBusyData );
00101   emit freeBusyDownloaded( fb, mEmail );
00102   // PENDING(steffen): Is this safe?
00103   //job->deleteLater();
00104   delete this;
00105 }
00106 
00107 
00108 FreeBusyManager::FreeBusyManager( QObject *parent, const char *name )
00109   : QObject( parent, name ),
00110     mCalendar( 0 ), mTimerID( 0 ), mUploadingFreeBusy( false )
00111 {
00112 }
00113 
00114 void FreeBusyManager::setCalendar( KCal::Calendar *c )
00115 {
00116   mCalendar = c;
00117   if ( mCalendar ) {
00118     mFormat.setTimeZone( mCalendar->timeZoneId(), true );
00119   }
00120 }
00121 
00122 KCal::FreeBusy *FreeBusyManager::ownerFreeBusy()
00123 {
00124   QDateTime start = QDateTime::currentDateTime();
00125   QDateTime end = start.addDays( KOPrefs::instance()->mFreeBusyPublishDays );
00126 
00127   FreeBusy *freebusy = new FreeBusy( mCalendar, start, end );
00128   freebusy->setOrganizer( Person( KOPrefs::instance()->fullName(),
00129                           KOPrefs::instance()->email() ) );
00130 
00131   return freebusy;
00132 }
00133 
00134 QString FreeBusyManager::ownerFreeBusyAsString()
00135 {
00136   FreeBusy *freebusy = ownerFreeBusy();
00137 
00138   QString result = freeBusyToIcal( freebusy );
00139 
00140   delete freebusy;
00141 
00142   return result;
00143 }
00144 
00145 QString FreeBusyManager::freeBusyToIcal( KCal::FreeBusy *freebusy )
00146 {
00147   return mFormat.createScheduleMessage( freebusy, Scheduler::Publish );
00148 }
00149 
00150 void FreeBusyManager::slotPerhapsUploadFB()
00151 {
00152   // user has automtic uploading disabled, bail out
00153   if ( !KOPrefs::instance()->freeBusyPublishAuto() )
00154      return;
00155   if( mTimerID != 0 )
00156     // A timer is already running, so we don't need to do anything
00157     return;
00158 
00159   int now = static_cast<int>( QDateTime::currentDateTime().toTime_t() );
00160   int eta = static_cast<int>( mNextUploadTime.toTime_t() ) - now;
00161 
00162   if( !mUploadingFreeBusy ) {
00163     // Not currently uploading
00164     if( mNextUploadTime.isNull() ||
00165     QDateTime::currentDateTime() > mNextUploadTime ) {
00166       // No uploading have been done in this session, or delay time is over
00167       publishFreeBusy();
00168       return;
00169     }
00170 
00171     // We're in the delay time and no timer is running. Start one
00172     if( eta <= 0 ) {
00173       // Sanity check failed - better do the upload
00174       publishFreeBusy();
00175       return;
00176     }
00177   } else {
00178     // We are currently uploading the FB list. Start the timer
00179     if( eta <= 0 ) {
00180       kdDebug(5850) << "This shouldn't happen! eta <= 0\n";
00181       eta = 10; // whatever
00182     }
00183   }
00184 
00185   // Start the timer
00186   mTimerID = startTimer( eta * 1000 );
00187 
00188   if( mTimerID == 0 )
00189     // startTimer failed - better do the upload
00190     publishFreeBusy();
00191 }
00192 
00193 // This is used for delayed Free/Busy list uploading
00194 void FreeBusyManager::timerEvent( QTimerEvent* )
00195 {
00196   publishFreeBusy();
00197 }
00198 
00203 void FreeBusyManager::publishFreeBusy()
00204 {
00205   // Already uploading? Skip this one then.
00206   if ( mUploadingFreeBusy )
00207     return;
00208   mUploadingFreeBusy = true;
00209 
00210   // If we have a timer running, it should be stopped now
00211   if( mTimerID != 0 ) {
00212     killTimer( mTimerID );
00213     mTimerID = 0;
00214   }
00215 
00216   // Save the time of the next free/busy uploading
00217   mNextUploadTime = QDateTime::currentDateTime();
00218   if( KOPrefs::instance()->mFreeBusyPublishDelay > 0 )
00219     mNextUploadTime = mNextUploadTime.addSecs(
00220         KOPrefs::instance()->mFreeBusyPublishDelay * 60 );
00221 
00222   QString messageText = ownerFreeBusyAsString();
00223 
00224   // We need to massage the list a bit so that Outlook understands
00225   // it.
00226   messageText = messageText.replace( QRegExp( "ORGANIZER\\s*:mailto:" ),
00227                                      "ORGANIZER:" );
00228 
00229   // Create a local temp file and save the message to it
00230   KTempFile tempFile;
00231   QTextStream *textStream = tempFile.textStream();
00232   if( textStream ) {
00233     *textStream << messageText;
00234     tempFile.close();
00235 
00236 #if 0
00237     QString defaultEmail = KOCore()::self()->email();
00238     QString emailHost = defaultEmail.mid( defaultEmail.find( '@' ) + 1 );
00239 
00240     // Put target string together
00241     KURL targetURL;
00242     if( KOPrefs::instance()->mPublishKolab ) {
00243       // we use Kolab
00244       QString server;
00245       if( KOPrefs::instance()->mPublishKolabServer == "%SERVER%" ||
00246       KOPrefs::instance()->mPublishKolabServer.isEmpty() )
00247     server = emailHost;
00248       else
00249     server = KOPrefs::instance()->mPublishKolabServer;
00250 
00251       targetURL.setProtocol( "webdavs" );
00252       targetURL.setHost( server );
00253 
00254       QString fbname = KOPrefs::instance()->mPublishUserName;
00255       int at = fbname.find('@');
00256       if( at > 1 && fbname.length() > (uint)at ) {
00257     fbname = fbname.left(at);
00258       }
00259       targetURL.setPath( "/freebusy/" + fbname + ".ifb" );
00260       targetURL.setUser( KOPrefs::instance()->mPublishUserName );
00261       targetURL.setPass( KOPrefs::instance()->mPublishPassword );
00262     } else {
00263       // we use something else
00264       targetURL = KOPrefs::instance()->mPublishAnyURL.replace( "%SERVER%",
00265                                                                emailHost );
00266       targetURL.setUser( KOPrefs::instance()->mPublishUserName );
00267       targetURL.setPass( KOPrefs::instance()->mPublishPassword );
00268     }
00269 #endif
00270 
00271     KURL targetURL ( KOPrefs::instance()->freeBusyPublishUrl() );
00272     targetURL.setUser( KOPrefs::instance()->mFreeBusyPublishUser );
00273     targetURL.setPass( KOPrefs::instance()->mFreeBusyPublishPassword );
00274 
00275     KURL src;
00276     src.setPath( tempFile.name() );
00277 
00278     kdDebug(5850) << "FreeBusyManager::publishFreeBusy(): " << targetURL << endl;
00279 
00280     KIO::Job * job = KIO::file_copy( src, targetURL, -1,
00281                                      true /*overwrite*/,
00282                                      false /*don't resume*/,
00283                                      false /*don't show progress info*/ );
00284     connect( job, SIGNAL( result( KIO::Job * ) ),
00285              SLOT( slotUploadFreeBusyResult( KIO::Job * ) ) );
00286   }
00287 }
00288 
00289 void FreeBusyManager::slotUploadFreeBusyResult(KIO::Job *_job)
00290 {
00291     KIO::FileCopyJob* job = static_cast<KIO::FileCopyJob *>(_job);
00292     if ( job->error() )
00293         KMessageBox::sorry( 0,
00294           i18n( "<qt>The software could not upload your free/busy list to the "
00295                 "URL '%1'. There might be a problem with the access rights, or "
00296                 "you specified an incorrect URL. The system said: <em>%2</em>."
00297                 "<br>Please check the URL or contact your system administrator."
00298                 "</qt>" ).arg( job->destURL().prettyURL() )
00299                          .arg( job->errorString() ) );
00300     // Delete temp file
00301     KURL src = job->srcURL();
00302     Q_ASSERT( src.isLocalFile() );
00303     if( src.isLocalFile() )
00304         QFile::remove(src.path());
00305     mUploadingFreeBusy = false;
00306 }
00307 
00308 bool FreeBusyManager::retrieveFreeBusy( const QString &email )
00309 {
00310   kdDebug(5850) << "FreeBusyManager::retrieveFreeBusy(): " << email << endl;
00311   if ( email.isEmpty() ) return false;
00312 
00313   if( KOPrefs::instance()->thatIsMe( email ) ) {
00314     // Don't download our own free-busy list from the net
00315     kdDebug(5850) << "freebusy of owner" << endl;
00316     emit freeBusyRetrieved( ownerFreeBusy(), email );
00317     return true;
00318   }
00319 
00320   // Check for cached copy of free/busy list
00321   KCal::FreeBusy *fb = loadFreeBusy( email );
00322   if ( fb ) {
00323     emit freeBusyRetrieved( fb, email );
00324   }
00325 
00326   // Don't download free/busy if the user does not want it.
00327   if( !KOPrefs::instance()->mFreeBusyRetrieveAuto )
00328     return false;
00329 
00330   mRetrieveQueue.append( email );
00331 
00332   if ( mRetrieveQueue.count() > 1 ) return true;
00333 
00334   return processRetrieveQueue();
00335 }
00336 
00337 bool FreeBusyManager::processRetrieveQueue()
00338 {
00339   if ( mRetrieveQueue.isEmpty() ) return true;
00340 
00341   QString email = mRetrieveQueue.first();
00342   mRetrieveQueue.pop_front();
00343 
00344   KURL sourceURL = freeBusyUrl( email );
00345 
00346   kdDebug(5850) << "FreeBusyManager::retrieveFreeBusy(): url: " << sourceURL.url()
00347             << endl;
00348 
00349   if ( !sourceURL.isValid() ) {
00350     kdDebug(5850) << "Invalid FB URL\n";
00351     return false;
00352   }
00353 
00354   FreeBusyDownloadJob *job = new FreeBusyDownloadJob( email, sourceURL, this,
00355                                                       "freebusy_download_job" );
00356   connect( job, SIGNAL( freeBusyDownloaded( KCal::FreeBusy *,
00357                                             const QString & ) ),
00358        SIGNAL( freeBusyRetrieved( KCal::FreeBusy *, const QString & ) ) );
00359   connect( job, SIGNAL( freeBusyDownloaded( KCal::FreeBusy *,
00360                                             const QString & ) ),
00361            SLOT( processRetrieveQueue() ) );
00362 
00363   return true;
00364 }
00365 
00366 void FreeBusyManager::cancelRetrieval()
00367 {
00368   mRetrieveQueue.clear();
00369 }
00370 
00371 KURL FreeBusyManager::freeBusyUrl( const QString &email )
00372 {
00373   // First check if there is a specific FB url for this email
00374   QString configFile = locateLocal( "data", "korganizer/freebusyurls" );
00375   KConfig cfg( configFile );
00376 
00377   cfg.setGroup( email );
00378   QString url = cfg.readEntry( "url" );
00379   if ( !url.isEmpty() ) {
00380     return KURL( url );
00381   }
00382 
00383   // None found. Check if we do automatic FB retrieving then
00384   if ( !KOPrefs::instance()->mFreeBusyRetrieveAuto )
00385     // No, so no FB list here
00386     return KURL();
00387 
00388   // Sanity check: Don't download if it's not a correct email
00389   // address (this also avoids downloading for "(empty email)").
00390   int emailpos = email.find( '@' );
00391   if( emailpos == -1 )
00392     return KURL();
00393 
00394   // Cut off everything left of the @ sign to get the user name.
00395   const QString emailName = email.left( emailpos );
00396   const QString emailHost = email.mid( emailpos + 1 );
00397 
00398   // Build the URL
00399   KURL sourceURL;
00400   sourceURL = KOPrefs::instance()->mFreeBusyRetrieveUrl;
00401 
00402   // This test is disabled to make the multidomains of Kolab2 work.
00403   // It's a temporary workaround until a real solution can be agreed with
00404   // the kdepim people
00405 #if 0
00406   // Don't try to fetch free/busy data for users not on the specified servers
00407   // This tests if the hostnames match, or one is a subset of the other
00408   const QString hostDomain = sourceURL.host();
00409   if ( hostDomain != emailHost && !hostDomain.endsWith( '.' + emailHost )
00410        && !emailHost.endsWith( '.' + hostDomain ) )
00411     // Host names do not match
00412     return KURL();
00413 
00414   if ( KOPrefs::instance()->mFreeBusyFullDomainRetrieval )
00415     sourceURL.setFileName( email + ".ifb" );
00416   else
00417     sourceURL.setFileName( emailName + ".ifb" );
00418 #endif
00419 
00420   //Add some intelligence to the fb URL (MS Outlook style)
00421   QString fbAddress = sourceURL.fileName();
00422   fbAddress.replace( "%EMAIL%", email );
00423   fbAddress.replace( "%NAME%", emailName );
00424   fbAddress.replace( "%SERVER%", emailHost );
00425 
00426   //Just set it to what the user wants, make no assumptions on his/her behalf
00427   kdDebug() << "FreeBusyManager::freeBusyUrl(): " << sourceURL.fileName()
00428             << " set to " << fbAddress << "."
00429             << endl;
00430   sourceURL.setFileName(  fbAddress );
00431 
00432   sourceURL.setUser( KOPrefs::instance()->mFreeBusyRetrieveUser );
00433   sourceURL.setPass( KOPrefs::instance()->mFreeBusyRetrievePassword );
00434 
00435   return sourceURL;
00436 }
00437 
00438 KCal::FreeBusy *FreeBusyManager::iCalToFreeBusy( const QCString &data )
00439 {
00440   kdDebug(5850) << "FreeBusyManager::iCalToFreeBusy()" << endl;
00441 
00442   QString freeBusyVCal = QString::fromUtf8( data );
00443   KCal::FreeBusy *fb = mFormat.parseFreeBusy( freeBusyVCal );
00444   if ( !fb ) {
00445     kdDebug(5850) << "FreeBusyManager::iCalToFreeBusy(): Error parsing free/busy"
00446               << endl;
00447   } else {
00448     saveFreeBusy( fb, fb->organizer() );
00449   }
00450   return fb;
00451 }
00452 
00453 QString FreeBusyManager::freeBusyDir()
00454 {
00455   return locateLocal( "data", "korganizer/freebusy" );
00456 }
00457 
00458 FreeBusy *FreeBusyManager::loadFreeBusy( const QString &email )
00459 {
00460   kdDebug(5850) << "FreeBusyManager::loadFreeBusy(): " << email << endl;
00461 
00462   QString fbd = freeBusyDir();
00463 
00464   QFile f( fbd + "/" + email + ".ifb" );
00465   if ( !f.exists() ) {
00466     kdDebug(5850) << "FreeBusyManager::loadFreeBusy() " << f.name()
00467               << " doesn't exist." << endl;
00468     return 0;
00469   }
00470 
00471   if ( !f.open( IO_ReadOnly ) ) {
00472     kdDebug(5850) << "FreeBusyManager::loadFreeBusy() Unable to open file "
00473               << f.name() << endl;
00474     return 0;
00475   }
00476 
00477   QTextStream ts( &f );
00478   QString str = ts.read();
00479 
00480   return iCalToFreeBusy( str.utf8() );
00481 }
00482 
00483 bool FreeBusyManager::saveFreeBusy( FreeBusy *freebusy, const Person &person )
00484 {
00485   kdDebug(5850) << "FreeBusyManager::saveFreeBusy(): " << person.fullName() << endl;
00486 
00487   QString fbd = freeBusyDir();
00488 
00489   QDir freeBusyDirectory( fbd );
00490   if ( !freeBusyDirectory.exists() ) {
00491     kdDebug(5850) << "Directory " << fbd << " does not exist!" << endl;
00492     kdDebug(5850) << "Creating directory: " << fbd << endl;
00493 
00494     if( !freeBusyDirectory.mkdir( fbd, true ) ) {
00495       kdDebug(5850) << "Could not create directory: " << fbd << endl;
00496       return false;
00497     }
00498   }
00499 
00500   QString filename( fbd );
00501   filename += "/";
00502   filename += person.email();
00503   filename += ".ifb";
00504   QFile f( filename );
00505 
00506   kdDebug(5850) << "FreeBusyManager::saveFreeBusy(): filename: " << filename
00507             << endl;
00508 
00509   freebusy->clearAttendees();
00510   freebusy->setOrganizer( person );
00511 
00512   QString messageText = mFormat.createScheduleMessage( freebusy,
00513                                                        Scheduler::Publish );
00514 
00515   if ( !f.open( IO_ReadWrite ) ) {
00516     kdDebug(5850) << "acceptFreeBusy: Can't open:" << filename << " for writing"
00517               << endl;
00518     return false;
00519   }
00520   QTextStream t( &f );
00521   t << messageText;
00522   f.close();
00523 
00524   return true;
00525 }
00526 
00527 #include "freebusymanager.moc"
KDE Logo
This file is part of the documentation for korganizer Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jan 31 15:55:47 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003