kalarm Library API Documentation

alarmcalendar.cpp

00001 /*
00002  *  alarmcalendar.cpp  -  KAlarm calendar file access
00003  *  Program:  kalarm
00004  *  (C) 2001, 2002 by David Jarvie  software@astrojar.org.uk
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  */
00020 
00021 #include "kalarm.h"
00022 #include <unistd.h>
00023 #include <time.h>
00024 
00025 #include <qfile.h>
00026 #include <qtextstream.h>
00027 
00028 #include <kmessagebox.h>
00029 #include <klocale.h>
00030 #include <kstandarddirs.h>
00031 #include <kconfig.h>
00032 #include <kaboutdata.h>
00033 #include <kio/netaccess.h>
00034 #include <kfileitem.h>
00035 #include <ktempfile.h>
00036 #include <dcopclient.h>
00037 #include <kdebug.h>
00038 
00039 extern "C" {
00040 #include <libical/ical.h>
00041 }
00042 
00043 #include <libkcal/vcaldrag.h>
00044 #include <libkcal/vcalformat.h>
00045 #include <libkcal/icalformat.h>
00046 
00047 #include "kalarmapp.h"
00048 #include "alarmcalendar.h"
00049 
00050 const QString DEFAULT_CALENDAR_FILE(QString::fromLatin1("calendar.ics"));
00051 
00052 
00053 /******************************************************************************
00054 * Read the calendar file URL from the config file (or use the default).
00055 * If there is an error, the program exits.
00056 */
00057 void AlarmCalendar::getURL() const
00058 {
00059         if (!mUrl.isValid())
00060         {
00061                 KConfig* config = kapp->config();
00062                 config->setGroup(QString::fromLatin1("General"));
00063                 *const_cast<KURL*>(&mUrl) = config->readEntry(QString::fromLatin1("Calendar"),
00064                                                              locateLocal("appdata", DEFAULT_CALENDAR_FILE));
00065                 if (!mUrl.isValid())
00066                 {
00067                         kdDebug(5950) << "AlarmCalendar::getURL(): invalid name: " << mUrl.prettyURL() << endl;
00068                         KMessageBox::error(0L, i18n("Invalid calendar file name: %1").arg(mUrl.prettyURL()),
00069                                            kapp->aboutData()->programName());
00070                         kapp->exit(1);
00071                 }
00072         }
00073 }
00074 
00075 /******************************************************************************
00076 * Open the calendar file and load it into memory.
00077 */
00078 bool AlarmCalendar::open()
00079 {
00080         getURL();
00081         mCalendar = new CalendarLocal();
00082         mCalendar->setLocalTime();    // write out using local time (i.e. no time zone)
00083 
00084         // Find out whether the calendar is ICal or VCal format
00085         QString ext = mUrl.filename().right(4);
00086         mVCal = (ext == QString::fromLatin1(".vcs"));
00087 
00088         if (!KIO::NetAccess::exists(mUrl))
00089         {
00090                 if (!create())      // create the calendar file
00091                         return false;
00092         }
00093 
00094         // Load the calendar file
00095         switch (load())
00096         {
00097                 case 1:
00098                         break;
00099                 case 0:
00100                         if (!create()  ||  load() <= 0)
00101                                 return false;
00102                 case -1:
00103 /*      if (!KIO::NetAccess::exists(mUrl))
00104         {
00105                 if (!create()  ||  load() <= 0)
00106                         return false;
00107         }*/
00108                         return false;
00109         }
00110         return true;
00111 }
00112 
00113 /******************************************************************************
00114 * Create a new calendar file.
00115 */
00116 bool AlarmCalendar::create()
00117 {
00118         // Create the calendar file
00119         KTempFile* tmpFile = 0L;
00120         QString filename;
00121         if (mUrl.isLocalFile())
00122                 filename = mUrl.path();
00123         else
00124         {
00125                 tmpFile = new KTempFile;
00126                 filename = tmpFile->name();
00127         }
00128         if (!save(filename))
00129         {
00130                 delete tmpFile;
00131                 return false;
00132         }
00133         delete tmpFile;
00134         return true;
00135 }
00136 
00137 /******************************************************************************
00138 * Load the calendar file into memory.
00139 * Reply = 1 if success, -2 if failure, 0 if zero-length file exists.
00140 */
00141 int AlarmCalendar::load()
00142 {
00143         getURL();
00144         kdDebug(5950) << "AlarmCalendar::load(): " << mUrl.prettyURL() << endl;
00145         QString tmpFile;
00146         if (!KIO::NetAccess::download(mUrl, tmpFile))
00147         {
00148                 kdError(5950) << "AlarmCalendar::load(): Load failure" << endl;
00149                 KMessageBox::error(0L, i18n("Cannot open calendar:\n%1").arg(mUrl.prettyURL()), kapp->aboutData()->programName());
00150                 return -1;
00151         }
00152         kdDebug(5950) << "AlarmCalendar::load(): --- Downloaded to " << tmpFile << endl;
00153         mKAlarmVersion        = -1;
00154         mKAlarmVersion057_UTC = false;
00155         mCalendar->setTimeZoneId(QString::null);   // default to the local time zone for reading
00156         bool loaded = mCalendar->load(tmpFile);
00157         mCalendar->setLocalTime();                 // write using local time (i.e. no time zone)
00158         if (!loaded)
00159         {
00160                 // Check if the file is zero length
00161                 KIO::NetAccess::removeTempFile(tmpFile);
00162                 KIO::UDSEntry uds;
00163                 KIO::NetAccess::stat(mUrl, uds);
00164                 KFileItem fi(uds, mUrl);
00165                 if (!fi.size())
00166                         return 0;     // file is zero length
00167                 kdDebug(5950) << "AlarmCalendar::load(): Error loading calendar file '" << tmpFile << "'" << endl;
00168                 KMessageBox::error(0L, i18n("Error loading calendar:\n%1\n\nPlease fix or delete the file.").arg(mUrl.prettyURL()),
00169                                    kapp->aboutData()->programName());
00170                 return -1;
00171         }
00172         if (!mLocalFile.isEmpty())
00173                 KIO::NetAccess::removeTempFile(mLocalFile);
00174         mLocalFile = tmpFile;
00175 
00176         // Find the version of KAlarm which wrote the calendar file, and do
00177         // any necessary conversions to the current format
00178         getKAlarmVersion();
00179         if (mKAlarmVersion == KAlarmVersion(0,5,7))
00180         {
00181                 // KAlarm version 0.5.7 - check whether times are stored in UTC, in which
00182                 // case it is the KDE 3.0.0 version which needs adjustment of summer times.
00183                 mKAlarmVersion057_UTC = isUTC();
00184                 kdDebug(5950) << "AlarmCalendar::load(): KAlarm version 0.5.7 (" << (mKAlarmVersion057_UTC ? "" : "non-") << "UTC)\n";
00185         }
00186         else
00187                 kdDebug(5950) << "AlarmCalendar::load(): KAlarm version " << mKAlarmVersion << endl;
00188         KAlarmEvent::convertKCalEvents();    // convert events to current KAlarm format for when calendar is saved
00189         return 1;
00190 }
00191 
00192 /******************************************************************************
00193 * Reload the calendar file into memory.
00194 * Reply = 1 if success, -2 if failure, 0 if zero-length file exists.
00195 */
00196 int AlarmCalendar::reload()
00197 {
00198         if (!mCalendar)
00199                 return -2;
00200         kdDebug(5950) << "AlarmCalendar::reload(): " << mUrl.prettyURL() << endl;
00201         close();
00202         return load();
00203 }
00204 
00205 /******************************************************************************
00206 * Save the calendar from memory to file.
00207 */
00208 bool AlarmCalendar::save(const QString& filename)
00209 {
00210         kdDebug(5950) << "AlarmCalendar::save(): " << filename << endl;
00211         CalFormat* format;
00212         if(mVCal)
00213                 format = new VCalFormat;
00214         else
00215                 format = new ICalFormat;
00216         bool success = mCalendar->save(filename, format);
00217         if (!success)
00218         {
00219                 kdDebug(5950) << "AlarmCalendar::save(): calendar save failed." << endl;
00220                 return false;
00221         }
00222 
00223         getURL();
00224         if (!mUrl.isLocalFile())
00225         {
00226                 if (!KIO::NetAccess::upload(filename, mUrl))
00227                 {
00228                         KMessageBox::error(0L, i18n("Cannot upload calendar to\n'%1'").arg(mUrl.prettyURL()), kapp->aboutData()->programName());
00229                         return false;
00230                 }
00231         }
00232 
00233         // Tell the alarm daemon to reload the calendar
00234         QByteArray data;
00235         QDataStream arg(data, IO_WriteOnly);
00236         arg << QCString(kapp->aboutData()->appName()) << mUrl.url();
00237         if (!kapp->dcopClient()->send(DAEMON_APP_NAME, DAEMON_DCOP_OBJECT, "reloadMsgCal(QCString,QString)", data))
00238                 kdDebug(5950) << "AlarmCalendar::save(): addCal dcop send failed" << endl;
00239         return true;
00240 }
00241 
00242 /******************************************************************************
00243 * Delete any temporary file at program exit.
00244 */
00245 void AlarmCalendar::close()
00246 {
00247         if (!mLocalFile.isEmpty())
00248                 KIO::NetAccess::removeTempFile(mLocalFile);
00249         if (mCalendar)
00250                 mCalendar->close();
00251 }
00252 
00253 /******************************************************************************
00254 * Add the specified event to the calendar.
00255 */
00256 void AlarmCalendar::addEvent(const KAlarmEvent& event)
00257 {
00258         Event* kcalEvent = new Event;
00259         event.updateEvent(*kcalEvent);
00260         mCalendar->addEvent(kcalEvent);
00261         const_cast<KAlarmEvent&>(event).setEventID(kcalEvent->uid());
00262 }
00263 
00264 /******************************************************************************
00265 * Update the specified event in the calendar with its new contents.
00266 * The event retains the same ID.
00267 */
00268 void AlarmCalendar::updateEvent(const KAlarmEvent& evnt)
00269 {
00270         Event* kcalEvent = event(evnt.id());
00271         if (kcalEvent)
00272                 evnt.updateEvent(*kcalEvent);
00273 }
00274 
00275 /******************************************************************************
00276 * Delete the specified event from the calendar.
00277 */
00278 void AlarmCalendar::deleteEvent(const QString& eventID)
00279 {
00280         Event* kcalEvent = event(eventID);
00281         if (kcalEvent)
00282                 mCalendar->deleteEvent(kcalEvent);
00283 }
00284 
00285 /******************************************************************************
00286 * Return the KAlarm version which wrote the calendar which has been loaded.
00287 * The format is, for example, 000507 for 0.5.7, or 0 if unknown.
00288 */
00289 void AlarmCalendar::getKAlarmVersion() const
00290 {
00291         // N.B. Remember to change  KAlarmVersion(int major, int minor, int rev)
00292         //      if the representation returned by this method changes.
00293         mKAlarmVersion = 0;   // set default to KAlarm pre-0.3.5, or another program
00294         if (mCalendar)
00295         {
00296                 const QString& prodid = mCalendar->loadedProductId();
00297                 QString progname = QString(" ") + theApp()->aboutData()->programName() + " ";
00298                 int i = prodid.find(progname, 0, false);
00299                 if (i >= 0)
00300                 {
00301                         QString ver = prodid.mid(i + progname.length()).stripWhiteSpace();
00302                         i = ver.find('/');
00303                         int j = ver.find(' ');
00304                         if (j >= 0  &&  j < i)
00305                                 i = j;
00306                         if (i > 0)
00307                         {
00308                                 ver = ver.left(i);
00309                                 // ver now contains the KAlarm version string
00310                                 if ((i = ver.find('.')) > 0)
00311                                 {
00312                                         bool ok;
00313                                         int version = ver.left(i).toInt(&ok) * 10000;   // major version
00314                                         if (ok)
00315                                         {
00316                                                 ver = ver.mid(i + 1);
00317                                                 if ((i = ver.find('.')) > 0)
00318                                                 {
00319                                                         int v = ver.left(i).toInt(&ok);   // minor version
00320                                                         if (ok)
00321                                                         {
00322                                                                 version += (v < 9 ? v : 9) * 100;
00323                                                                 ver = ver.mid(i + 1);
00324                                                                 if (ver.at(0).isDigit())
00325                                                                 {
00326                                                                         // Allow other characters to follow last digit
00327                                                                         v = ver.toInt();   // issue number
00328                                                                         mKAlarmVersion = version + (v < 9 ? v : 9);
00329                                                                 }
00330                                                         }
00331                                                 }
00332                                                 else
00333                                                 {
00334                                                         // There is no issue number
00335                                                         if (ver.at(0).isDigit())
00336                                                         {
00337                                                                 // Allow other characters to follow last digit
00338                                                                 int v = ver.toInt();   // minor number
00339                                                                 mKAlarmVersion = version + (v < 9 ? v : 9) * 100;
00340                                                         }
00341                                                 }
00342                                         }
00343                                 }
00344                         }
00345                 }
00346         }
00347 }
00348 
00349 /******************************************************************************
00350  * Check whether the calendar file has its times stored as UTC times,
00351  * indicating that it was written by the KDE 3.0.0 version of KAlarm 0.5.7.
00352  * Reply = true if times are stored in UTC
00353  *       = false if the calendar is a vCalendar, times are not UTC, or any error occurred.
00354  */
00355 bool AlarmCalendar::isUTC() const
00356 {
00357         // Read the calendar file into a QString
00358         QFile file(mLocalFile);
00359         if (!file.open(IO_ReadOnly))
00360                 return false;
00361         QTextStream ts(&file);
00362         ts.setEncoding(QTextStream::UnicodeUTF8);
00363         QString text = ts.read();
00364         file.close();
00365 
00366         // Extract the CREATED property for the first VEVENT from the calendar
00367         bool result = false;
00368         icalcomponent* calendar = icalcomponent_new_from_string(text.local8Bit().data());
00369         if (calendar)
00370         {
00371                 if (icalcomponent_isa(calendar) == ICAL_VCALENDAR_COMPONENT)
00372                 {
00373                         icalcomponent* c = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT);
00374                         if (c)
00375                         {
00376                                 icalproperty* p = icalcomponent_get_first_property(c, ICAL_CREATED_PROPERTY);
00377                                 if (p)
00378                                 {
00379                                         struct icaltimetype datetime = icalproperty_get_created(p);
00380                                         if (datetime.is_utc)
00381                                                 result = true;
00382                                 }
00383                         }
00384                 }
00385                 icalcomponent_free(calendar);
00386         }
00387         return result;
00388 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 15 11:41:02 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001