summaryrefslogtreecommitdiffstats
path: root/kresources/exchange/resourceexchange.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kresources/exchange/resourceexchange.cpp')
-rw-r--r--kresources/exchange/resourceexchange.cpp605
1 files changed, 605 insertions, 0 deletions
diff --git a/kresources/exchange/resourceexchange.cpp b/kresources/exchange/resourceexchange.cpp
new file mode 100644
index 000000000..355244922
--- /dev/null
+++ b/kresources/exchange/resourceexchange.cpp
@@ -0,0 +1,605 @@
+/*
+ This file is part of libkpimexchange.
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include <stdlib.h>
+
+#include <qdatetime.h>
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qwidgetlist.h>
+#include <qwidget.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kstringhandler.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+#include <libkcal/calendarlocal.h>
+#include <libkcal/calendar.h>
+#include <libkcal/journal.h>
+
+#include <kresources/configwidget.h>
+
+#include <kabc/locknull.h>
+
+#include "dateset.h"
+#include "exchangeaccount.h"
+#include "exchangeclient.h"
+#include "exchangemonitor.h"
+
+#include "resourceexchange.h"
+#include "resourceexchangeconfig.h"
+
+
+using namespace KCal;
+using namespace KPIM;
+
+typedef KRES::PluginFactory<ResourceExchange,ResourceExchangeConfig> ExchangeFactory;
+
+// FIXME: Use K_EXPORT_COMPONENT_FACTORY( resourcecalendarexchange, ExchangeFactory ); here
+// Problem: How to insert the catalogue!
+extern "C"
+{
+ void* init_resourcecalendarexchange()
+ {
+ KGlobal::locale()->insertCatalogue( "kres_exchange" );
+ return new ExchangeFactory;
+ }
+}
+
+class ResourceExchange::EventInfo {
+public:
+ KCal::Event* event;
+ KURL url;
+ long updateWatch;
+};
+
+ResourceExchange::ResourceExchange( const KConfig *config )
+ : ResourceCalendar( config ), mClient(0), mMonitor(0), mCache(0), mDates(0),
+ mEventDates(0), mCacheDates(0)
+{
+ mLock = new KABC::LockNull( true );
+
+ mTimeZoneId = QString::fromLatin1( "UTC" );
+
+ kdDebug() << "Creating ResourceExchange" << endl;
+ if (config ) {
+ mAccount = new ExchangeAccount(
+ config->readEntry( "ExchangeHost" ),
+ config->readEntry( "ExchangePort" ),
+ config->readEntry( "ExchangeAccount" ),
+ KStringHandler::obscure( config->readEntry( "ExchangePassword" ) ),
+ config->readEntry( "ExchangeMailbox" ) );
+ mCachedSeconds = config->readNumEntry( "ExchangeCacheTimeout", 600 );
+ mAutoMailbox = config->readBoolEntry( "ExchangeAutoMailbox", true );
+ } else {
+ mAccount = new ExchangeAccount( "", "", "", "" );
+ mCachedSeconds = 600;
+ }
+}
+
+ResourceExchange::~ResourceExchange()
+{
+ kdDebug() << "Destructing ResourceExchange" << endl;
+
+ close();
+
+ delete mAccount; mAccount = 0;
+}
+
+void ResourceExchange::writeConfig( KConfig* config )
+{
+ ResourceCalendar::writeConfig( config );
+ config->writeEntry( "ExchangeHost", mAccount->host() );
+ config->writeEntry( "ExchangePort", mAccount->port() );
+ config->writeEntry( "ExchangeAccount", mAccount->account() );
+ config->writeEntry( "ExchangeMailbox", mAccount->mailbox() );
+ config->writeEntry( "ExchangePassword", KStringHandler::obscure( mAccount->password() ) );
+ config->writeEntry( "ExchangeCacheTimeout", mCachedSeconds );
+ config->writeEntry( "ExchangeAutoMailbox", mAutoMailbox );
+}
+
+bool ResourceExchange::doOpen()
+{
+ kdDebug() << "ResourceExchange::doOpen()" << endl;
+
+ mClient = new ExchangeClient( mAccount, mTimeZoneId );
+ connect( mClient, SIGNAL( downloadFinished( int, const QString & ) ),
+ SLOT( slotDownloadFinished( int, const QString & ) ) );
+ connect( mClient, SIGNAL( event( KCal::Event *, const KURL & ) ),
+ SLOT( downloadedEvent( KCal::Event *, const KURL & ) ) );
+
+#if 0
+ kdDebug() << "Creating monitor" << endl;
+ QHostAddress ip;
+ ip.setAddress( mAccount->host() );
+ mMonitor = new ExchangeMonitor( mAccount, ExchangeMonitor::CallBack, ip );
+ connect( mMonitor, SIGNAL(notify( const QValueList<long>& , const QValueList<KURL>& )), this, SLOT(slotMonitorNotify( const QValueList<long>& , const QValueList<KURL>& )) );
+ connect( mMonitor, SIGNAL(error(int , const QString&)), this, SLOT(slotMonitorError(int , const QString&)) );
+
+ mMonitor->addWatch( mAccount->calendarURL(), ExchangeMonitor::UpdateNewMember, 1 );
+#endif
+
+ QWidgetList* widgets = QApplication::topLevelWidgets();
+ if ( !widgets->isEmpty() )
+ mClient->setWindow( widgets->first() );
+ delete widgets;
+
+ mDates = new DateSet();
+
+ mEventDates = new QMap<Event,QDateTime>();
+ mCacheDates = new QMap<QDate, QDateTime>();
+
+ mCache = new CalendarLocal( mTimeZoneId );
+ // mOldestDate = 0L;
+ // mNewestDate = 0L;
+
+ // FIXME: check if server exists, account is OK, etc.
+ return true;
+}
+
+void ResourceExchange::doClose()
+{
+ kdDebug() << "ResourceExchange::doClose()" << endl;
+
+ // delete mNewestDate;
+ // delete mOldestDate;
+ delete mDates; mDates = 0;
+// delete mMonitor; mMonitor = 0;
+ delete mClient; mClient = 0;
+ delete mEventDates; mEventDates = 0;
+ delete mCacheDates; mCacheDates = 0;
+ if (mCache) {
+ mCache->close();
+ delete mCache; mCache = 0;
+ }
+// setModified( false );
+}
+
+bool ResourceExchange::doLoad()
+{
+ return true;
+}
+
+bool ResourceExchange::doSave()
+{
+ kdDebug() << "ResourceExchange::save() " << mChangedIncidences.count()
+ << endl;
+
+ Incidence::List::Iterator it = mChangedIncidences.begin();
+ while( it != mChangedIncidences.end() ) {
+ if ( (*it)->type() == "Event" ) {
+ if ( uploadEvent( static_cast<Event *>( *it ) ) ) {
+ it = mChangedIncidences.remove( it );
+ } else {
+ kdError() << "ResourceExchange::save(): upload failed." << endl;
+ ++it;
+ }
+ } else {
+ kdError() << "ResourceExchange::save() type not handled: "
+ << (*it)->type() << endl;
+ ++it;
+ }
+ }
+ return true;
+}
+
+KABC::Lock *ResourceExchange::lock()
+{
+ return mLock;
+}
+
+void ResourceExchange::slotMonitorNotify( const QValueList<long>& IDs, const QValueList<KURL>& urls )
+{
+ kdDebug() << "ResourceExchange::slotMonitorNotify()" << endl;
+
+ QString result;
+ KPIM::ExchangeMonitor::IDList::ConstIterator it;
+ for ( it = IDs.begin(); it != IDs.end(); ++it ) {
+ if ( it == IDs.begin() )
+ result += QString::number( (*it) );
+ else
+ result += "," + QString::number( (*it) );
+ }
+ kdDebug() << "Got signals for " << result << endl;
+ QValueList<KURL>::ConstIterator it2;
+ for ( it2 = urls.begin(); it2 != urls.end(); ++it2 ) {
+ kdDebug() << "URL: " << (*it2).prettyURL() << endl;
+ }
+
+ /* Now find out what happened:
+ * One or more of the following:
+ * 1. Event added in period that we think we have cached
+ * 2. Event deleted that we have in cache
+ * 3. Event modified that we have in cache
+ * 4. Something else happened that isn't relevant to us
+ * Update cache, then notify whoever's watching us
+ * We may be able to find (1) and (3) by looking at the
+ * DAV:getlastmodified property
+ * (2) is trickier: we might have to resort to checking
+ * all uids in the cache
+ * Or: put monitors on every event in the cache, so that
+ * we know when one gets deleted or modified
+ * Only look for new events using the global monitor
+ */
+}
+
+void ResourceExchange::slotMonitorError( int errorCode, const QString& moreInfo )
+{
+ kdError() << "Ignoring error from Exchange monitor, code=" << errorCode << "; more info: " << moreInfo << endl;
+}
+
+
+bool ResourceExchange::addEvent(Event *anEvent)
+{
+ if( !mCache ) return false;
+ kdDebug() << "ResourceExchange::addEvent" << endl;
+
+ // FIXME: first check of upload finished successfully, only then
+ // add to cache
+ mCache->addEvent( anEvent );
+
+ uploadEvent( anEvent );
+// insertEvent(anEvent);
+
+ anEvent->registerObserver( this );
+// setModified( true );
+
+ return true;
+}
+
+bool ResourceExchange::uploadEvent( Event *event )
+{
+ mClient->uploadSynchronous( event );
+ return true;
+}
+
+bool ResourceExchange::deleteEvent(Event *event)
+{
+ if ( !mCache ) return false;
+ kdDebug(5800) << "ResourceExchange::deleteEvent" << endl;
+
+ mClient->removeSynchronous( event );
+
+ // This also frees the event
+ return mCache->deleteEvent( event );
+
+// setModified( true );
+}
+
+void ResourceExchange::changeIncidence( Incidence *incidence )
+{
+ kdDebug() << "ResourceExchange::changeIncidence(): "
+ << incidence->summary() << endl;
+
+ if ( mChangedIncidences.find( incidence ) == mChangedIncidences.end() ) {
+ mChangedIncidences.append( incidence );
+ }
+}
+
+Event *ResourceExchange::event( const QString &uid )
+{
+ kdDebug(5800) << "ResourceExchange::event(): " << uid << endl;
+
+ // FIXME: Look in exchange server for uid!
+ Event *event = 0;
+ if ( mCache )
+ event = mCache->event( uid );
+ return event;
+}
+
+void ResourceExchange::subscribeEvents( const QDate &start, const QDate &end )
+{
+ kdDebug(5800) << "ResourceExchange::subscribeEvents()" << endl;
+ // FIXME: possible race condition if several subscribe events are run close
+ // to each other
+ mClient->download( start, end, false );
+}
+
+void ResourceExchange::downloadedEvent( KCal::Event *event, const KURL &url )
+{
+ kdDebug() << "Downloaded event: " << event->summary() << " from url "
+ << url.prettyURL() << endl;
+ // FIXME: add watches to the monitor for these events
+ // KURL url =
+ // mMonitor->addWatch( url, KPIM::ExchangeMonitor::Update, 0 );
+// emit eventsAdded( events );
+}
+
+void ResourceExchange::slotDownloadFinished( int result,
+ const QString &moreinfo )
+{
+ kdDebug() << "ResourceExchange::downloadFinished" << endl;
+
+ if ( result != KPIM::ExchangeClient::ResultOK ) {
+ // Do something useful with the error report
+ kdError() << "ResourceExchange::slotDownloadFinished(): error " << result
+ << ": " << moreinfo << endl;
+ }
+}
+
+void ResourceExchange::unsubscribeEvents( const QDate &/*start*/, const QDate &/*end*/ )
+{
+ kdDebug() << "ResourceExchange::unsubscribeEvents()" << endl;
+}
+
+bool ResourceExchange::addTodo(Todo */*todo*/)
+{
+ // This resource doesn't handle todos yet!
+ return false;
+/* if( !mCache)
+ return false;
+ mCache->addTodo( todo );
+
+ todo->registerObserver( this );
+
+// setModified( true );
+
+ return true;*/
+}
+
+bool ResourceExchange::deleteTodo(Todo */*todo*/)
+{
+ // We don't handle todos yet
+// if( !mCache )
+ return false;
+// mCache->deleteTodo( todo );
+
+// setModified( true );
+}
+
+Todo::List ResourceExchange::rawTodos( TodoSortField /*sortField*/, SortDirection /*sortDirection*/ )
+{
+ // We don't handle todos yet
+ return Todo::List();
+/* Todo::List list;
+ if ( mCache )
+ list = mCache->rawTodos( sortField, sortDirection );
+ return list;*/
+}
+
+Todo *ResourceExchange::todo( const QString &/*uid*/ )
+{
+ // We don't handle todos yet
+ return 0;
+/* if ( !mCache )
+ return 0;
+ else
+ return mCache->todo( uid );*/
+}
+
+Todo::List ResourceExchange::rawTodosForDate( const QDate &/*date*/ )
+{
+ Todo::List list;
+ // We don't handle todos yet
+/* if ( mCache )
+ list = mCache->rawTodosForDate( date );*/
+ return list;
+}
+
+Alarm::List ResourceExchange::alarmsTo( const QDateTime &to )
+{
+ Alarm::List list;
+ if ( mCache )
+ list = mCache->alarmsTo( to );
+ return list;
+}
+
+/* Invoked by korgac when checking alarms. Always updates the cache. */
+Alarm::List ResourceExchange::alarms( const QDateTime &from, const QDateTime &to )
+{
+ kdDebug(5800) << "ResourceExchange::alarms(" << from.toString() << " - " << to.toString() << ")\n";
+ Alarm::List list;
+
+ QDate start = from.date();
+ QDate end = to.date();
+
+ if ( mCache ) {
+
+ /* Clear the cache */
+ Event::List oldEvents = mCache->rawEvents( start, end, false );
+
+ Event::List::ConstIterator it;
+ for( it = oldEvents.begin(); it != oldEvents.end(); ++it ) {
+ mCache->deleteEvent( *it );
+ }
+
+ /* Fetch events */
+ mClient->downloadSynchronous( mCache, start, end, false );
+
+ list = mCache->alarms( from, to );
+ }
+ return list;
+}
+
+/****************************** PROTECTED METHODS ****************************/
+
+// after changes are made to an event, this should be called.
+void ResourceExchange::incidenceUpdated( IncidenceBase *incidence )
+{
+ Event* event = dynamic_cast<Event *>( incidence );
+ if ( event ) {
+ kdDebug() << "Event updated, resubmit to server..." << endl;
+ uploadEvent( event );
+ }
+// setModified( true );
+}
+
+// this function will take a VEvent and insert it into the event
+// dictionary for the ResourceExchange. If there is no list of events for that
+// particular location in the dictionary, a new one will be created.
+/*
+void ResourceExchange::insertEvent(const Event *anEvent)
+{
+ kdDebug() << "ResourceExchange::insertEvent" << endl;
+
+}
+*/
+// taking a QDate, this function will look for an eventlist in the dict
+// with that date attached -
+Event::List ResourceExchange::rawEventsForDate( const QDate &qd,
+ EventSortField sortField,
+ SortDirection sortDirection )
+{
+ if (!mCache) return Event::List();
+ // If the events for this date are not in the cache, or if they are old,
+ // get them again
+ QDateTime now = QDateTime::currentDateTime();
+ // kdDebug() << "Now is " << now.toString() << endl;
+ // kdDebug() << "mDates: " << mDates << endl;
+ QDate start = QDate( qd.year(), qd.month(), 1 ); // First day of month
+ if ( mDates && ( !mDates->contains( start ) ||
+ (*mCacheDates)[start].secsTo( now ) > mCachedSeconds ) ) {
+ QDate end = start.addMonths( 1 ).addDays( -1 ); // Last day of month
+ // Get events that occur in this period from the cache
+ Event::List oldEvents = mCache->rawEvents( start, end, false );
+ // And remove them all
+ Event::List::ConstIterator it;
+ for( it = oldEvents.begin(); it != oldEvents.end(); ++it ) {
+ mCache->deleteEvent( *it );
+ }
+
+ // FIXME: This is needed for the hack below:
+ Event::List eventsBefore = mCache->rawEvents();
+
+ kdDebug() << "Reading events for month of " << start.toString() << endl;
+ mClient->downloadSynchronous( mCache, start, end, true ); // Show progress dialog
+
+ // FIXME: This is a terrible hack! We need to install the observer for
+ // newly downloaded events.However, downloading is done by
+ // mClient->downloadSynchronous, where we don't have the pointer to this
+ // available... On the other hand, here we don't really know which events
+ // are really new.
+ Event::List eventsAfter = mCache->rawEvents();
+ for ( it = eventsAfter.begin(); it != eventsAfter.end(); ++it ) {
+ if ( eventsBefore.find( *it ) == eventsBefore.end() ) {
+ // it's a new event downloaded by downloadSynchronous -> install observer
+ (*it)->registerObserver( this );
+ }
+ }
+
+ mDates->add( start );
+ mCacheDates->insert( start, now );
+ }
+
+ // Events are safely in the cache now, return them from cache
+ Event::List events;
+ if ( mCache )
+ events = mCache->rawEventsForDate( qd, sortField, sortDirection );
+ // kdDebug() << "Found " << events.count() << " events." << endl;
+ return events;
+}
+
+
+Event::List ResourceExchange::rawEvents( const QDate &start, const QDate &end,
+ bool inclusive )
+{
+ kdDebug() << "ResourceExchange::rawEvents(start,end,inclusive)" << endl;
+ if (!mCache) return Event::List();
+ return mCache->rawEvents( start, end, inclusive );
+}
+
+Event::List ResourceExchange::rawEventsForDate(const QDateTime &qdt)
+{
+ kdDebug() << "ResourceExchange::rawEventsForDate(qdt)" << endl;
+ return rawEventsForDate( qdt.date() );
+}
+
+Event::List ResourceExchange::rawEvents( EventSortField sortField, SortDirection sortDirection )
+{
+ kdDebug() << "ResourceExchange::rawEvents()" << endl;
+ if (!mCache) return Event::List();
+ return mCache->rawEvents( sortField, sortDirection );
+}
+
+bool ResourceExchange::addJournal(Journal */*journal*/)
+{
+ // This resource doesn't handle journals yet
+ return false;
+/* kdDebug(5800) << "Adding Journal on " << journal->dtStart().toString() << endl;
+ if (mCache) {
+ mCache->addJournal( journal );
+
+ journal->registerObserver( this );
+
+// setModified( true );
+ }
+
+ return true;*/
+}
+
+bool ResourceExchange::deleteJournal(Journal */*journal*/)
+{
+ // Wedon't handle journals yet
+// if( !mCache )
+ return false;
+// mCache->deleteJournal( journal );
+
+// setModified( true );
+}
+
+Journal::List ResourceExchange::journals(const QDate &/*date*/)
+{
+ // We don't handle journals yet
+ return Journal::List();
+/* Journal::List list;
+ if ( mCache )
+ list = mCache->journals( date );
+ return list;*/
+}
+
+Journal *ResourceExchange::journal(const QString &/*uid*/)
+{
+ // We don't handle journals yet
+ return 0;
+/* if( !mCache )
+ return 0;
+ return mCache->journal( uid );*/
+}
+
+Journal::List ResourceExchange::rawJournals( JournalSortField /*sortField*/, SortDirection /*sortDirection*/ )
+{
+ // We don't handle journals yet
+ return Journal::List();
+/* Journal::List list;
+ if ( mCache )
+ list = mCache->rawJournals( sortField, sortDirection );
+ return list;*/
+}
+
+Journal::List ResourceExchange::rawJournalsForDate( const QDate &/*date*/ )
+{
+ // We don't handle journals yet
+ return Journal::List();
+/* Journal::List list;
+ if ( mCache )
+ list = mCache->rawJournalsForDate( date );
+ return list;*/
+}
+
+void ResourceExchange::setTimeZoneId( const QString &tzid )
+{
+ mTimeZoneId = tzid;
+ if ( mCache ) mCache->setTimeZoneId( tzid );
+ if ( mClient ) mClient->setTimeZoneId( tzid );
+}
+
+#include "resourceexchange.moc"