• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KCal Library

icalformat.cpp

Go to the documentation of this file.
00001 /*
00002   This file is part of the kcal library.
00003 
00004   Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00005 
00006   This library is free software; you can redistribute it and/or
00007   modify it under the terms of the GNU Library General Public
00008   License as published by the Free Software Foundation; either
00009   version 2 of the License, or (at your option) any later version.
00010 
00011   This library 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 GNU
00014   Library General Public License for more details.
00015 
00016   You should have received a copy of the GNU Library General Public License
00017   along with this library; see the file COPYING.LIB.  If not, write to
00018   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019   Boston, MA 02110-1301, USA.
00020 */
00032 #include "icalformat.h"
00033 #include "icalformat_p.h"
00034 #include "calendar.h"
00035 #include "calendarlocal.h"
00036 #include "icaltimezones.h"
00037 
00038 extern "C" {
00039   #include <ical.h>
00040   #include <icalss.h>
00041   #include <icalparser.h>
00042   #include <icalrestriction.h>
00043   #include <icalmemory.h>
00044 }
00045 
00046 #include <QtCore/QString>
00047 #include <QtCore/QRegExp>
00048 #include <QtCore/QFile>
00049 #include <QtCore/QTextStream>
00050 #include <QtCore/QByteArray>
00051 #include <QtGui/QClipboard>
00052 
00053 #include <kdebug.h>
00054 #include <klocale.h>
00055 #include <ksavefile.h>
00056 
00057 #include <stdio.h>
00058 
00059 using namespace KCal;
00060 
00061 //@cond PRIVATE
00062 class KCal::ICalFormat::Private
00063 {
00064   public:
00065     Private( ICalFormat *parent )
00066       : mImpl( new ICalFormatImpl( parent ) ),
00067         mTimeSpec( KDateTime::UTC )
00068     {}
00069     ~Private()  { delete mImpl; }
00070     ICalFormatImpl *mImpl;
00071     KDateTime::Spec mTimeSpec;
00072 };
00073 //@endcond
00074 
00075 ICalFormat::ICalFormat()
00076   : d( new Private( this ) )
00077 {
00078 }
00079 
00080 ICalFormat::~ICalFormat()
00081 {
00082   delete d;
00083 }
00084 
00085 bool ICalFormat::load( Calendar *calendar, const QString &fileName )
00086 {
00087   kDebug(5800) << fileName;
00088 
00089   clearException();
00090 
00091   QFile file( fileName );
00092   if ( !file.open( QIODevice::ReadOnly ) ) {
00093     kDebug(5800) << "load error";
00094     setException( new ErrorFormat( ErrorFormat::LoadError ) );
00095     return false;
00096   }
00097   QTextStream ts( &file );
00098   ts.setCodec( "ISO 8859-1" );
00099   QString text = ts.readAll();
00100   file.close();
00101 
00102   if ( text.trimmed().isEmpty() ) {
00103     // empty files are valid
00104     return true;
00105   } else {
00106     return fromRawString( calendar, text.toLatin1() );
00107   }
00108 }
00109 
00110 bool ICalFormat::save( Calendar *calendar, const QString &fileName )
00111 {
00112   kDebug(5800) << fileName;
00113 
00114   clearException();
00115 
00116   QString text = toString( calendar );
00117   if ( text.isNull() ) {
00118     return false;
00119   }
00120 
00121   // Write backup file
00122   KSaveFile::backupFile( fileName );
00123 
00124   KSaveFile file( fileName );
00125   if ( !file.open() ) {
00126     kDebug(5800) << "err:" << file.errorString();
00127     setException( new ErrorFormat( ErrorFormat::SaveError,
00128                                    i18n( "Error saving to '%1'.", fileName ) ) );
00129     return false;
00130   }
00131 
00132   // Convert to UTF8 and save
00133   QByteArray textUtf8 = text.toUtf8();
00134   file.write( textUtf8.data(), textUtf8.size() - 1 );
00135 
00136   if ( !file.finalize() ) {
00137     kDebug(5800) << "err:" << file.errorString();
00138     setException( new ErrorFormat( ErrorFormat::SaveError,
00139                                    i18n( "Could not save '%1'", fileName ) ) );
00140     return false;
00141   }
00142 
00143   return true;
00144 }
00145 
00146 bool ICalFormat::fromString( Calendar *cal, const QString &string )
00147 {
00148   return fromRawString( cal, string.toUtf8() );
00149 }
00150 
00151 bool ICalFormat::fromRawString( Calendar *cal, const QByteArray &string )
00152 {
00153   // Get first VCALENDAR component.
00154   // TODO: Handle more than one VCALENDAR or non-VCALENDAR top components
00155   icalcomponent *calendar;
00156 
00157   // Let's defend const correctness until the very gates of hell^Wlibical
00158   calendar = icalcomponent_new_from_string( const_cast<char*>( ( const char * )string ) );
00159   if ( !calendar ) {
00160     kDebug(5800) << "parse error";
00161     setException( new ErrorFormat( ErrorFormat::ParseErrorIcal ) );
00162     return false;
00163   }
00164 
00165   bool success = true;
00166 
00167   if ( icalcomponent_isa( calendar ) == ICAL_XROOT_COMPONENT ) {
00168     icalcomponent *comp;
00169     for ( comp = icalcomponent_get_first_component( calendar, ICAL_VCALENDAR_COMPONENT );
00170           comp; comp = icalcomponent_get_next_component( calendar, ICAL_VCALENDAR_COMPONENT ) ) {
00171       // put all objects into their proper places
00172       if ( !d->mImpl->populate( cal, comp ) ) {
00173         kDebug(5800) << "Could not populate calendar";
00174         if ( !exception() ) {
00175           setException( new ErrorFormat( ErrorFormat::ParseErrorKcal ) );
00176         }
00177         success = false;
00178       } else {
00179         setLoadedProductId( d->mImpl->loadedProductId() );
00180       }
00181     }
00182   } else if ( icalcomponent_isa( calendar ) != ICAL_VCALENDAR_COMPONENT ) {
00183     kDebug(5800) << "No VCALENDAR component found";
00184     setException( new ErrorFormat( ErrorFormat::NoCalendar ) );
00185     success = false;
00186   } else {
00187     // put all objects into their proper places
00188     if ( !d->mImpl->populate( cal, calendar ) ) {
00189       kDebug(5800) << "Could not populate calendar";
00190       if ( !exception() ) {
00191         setException( new ErrorFormat( ErrorFormat::ParseErrorKcal ) );
00192       }
00193       success = false;
00194     } else {
00195       setLoadedProductId( d->mImpl->loadedProductId() );
00196     }
00197   }
00198 
00199   icalcomponent_free( calendar );
00200   icalmemory_free_ring();
00201 
00202   return success;
00203 }
00204 
00205 Incidence *ICalFormat::fromString( const QString &string )
00206 {
00207   CalendarLocal cal( d->mTimeSpec );
00208   fromString( &cal, string );
00209 
00210   Incidence *ical = 0;
00211   Event::List elist = cal.events();
00212   if ( elist.count() > 0 ) {
00213     ical = elist.first();
00214   } else {
00215     Todo::List tlist = cal.todos();
00216     if ( tlist.count() > 0 ) {
00217       ical = tlist.first();
00218     } else {
00219       Journal::List jlist = cal.journals();
00220       if ( jlist.count() > 0 ) {
00221         ical = jlist.first();
00222       }
00223     }
00224   }
00225 
00226   return ical ? ical->clone() : 0;
00227 }
00228 
00229 QString ICalFormat::toString( Calendar *cal )
00230 {
00231   icalcomponent *calendar = d->mImpl->createCalendarComponent( cal );
00232   icalcomponent *component;
00233 
00234   ICalTimeZones *tzlist = cal->timeZones();  // time zones possibly used in the calendar
00235   ICalTimeZones tzUsedList;                  // time zones actually used in the calendar
00236 
00237   // todos
00238   Todo::List todoList = cal->rawTodos();
00239   Todo::List::ConstIterator it;
00240   for ( it = todoList.begin(); it != todoList.end(); ++it ) {
00241     component = d->mImpl->writeTodo( *it, tzlist, &tzUsedList );
00242     icalcomponent_add_component( calendar, component );
00243   }
00244 
00245   // events
00246   Event::List events = cal->rawEvents();
00247   Event::List::ConstIterator it2;
00248   for ( it2 = events.begin(); it2 != events.end(); ++it2 ) {
00249     if ( *it2 ) {
00250       component = d->mImpl->writeEvent( *it2, tzlist, &tzUsedList );
00251       icalcomponent_add_component( calendar, component );
00252     }
00253   }
00254 
00255   // journals
00256   Journal::List journals = cal->journals();
00257   Journal::List::ConstIterator it3;
00258   for ( it3 = journals.begin(); it3 != journals.end(); ++it3 ) {
00259     kDebug(5800) << "write journal" << (*it3)->uid();
00260     component = d->mImpl->writeJournal( *it3, tzlist, &tzUsedList );
00261     icalcomponent_add_component( calendar, component );
00262   }
00263 
00264   // time zones
00265   const ICalTimeZones::ZoneMap zones = tzUsedList.zones();
00266   for ( ICalTimeZones::ZoneMap::ConstIterator it = zones.begin();  it != zones.end();  ++it ) {
00267     icaltimezone *tz = (*it).icalTimezone();
00268     if ( !tz ) {
00269       kError(5800) << "ICalFormat::toString(): bad time zone";
00270     } else {
00271       component = icalcomponent_new_clone( icaltimezone_get_component( tz ) );
00272       icalcomponent_add_component( calendar, component );
00273       icaltimezone_free( tz, 1 );
00274     }
00275   }
00276 
00277   QString text = QString::fromUtf8( icalcomponent_as_ical_string( calendar ) );
00278 
00279   icalcomponent_free( calendar );
00280   icalmemory_free_ring();
00281 
00282   if ( text.isNull() ) {
00283     setException( new ErrorFormat( ErrorFormat::SaveError,
00284                                    i18n( "libical error" ) ) );
00285     return QString();
00286   }
00287 
00288   return text;
00289 }
00290 
00291 QString ICalFormat::toICalString( Incidence *incidence )
00292 {
00293   CalendarLocal cal( d->mTimeSpec );
00294   cal.addIncidence( incidence->clone() );
00295   return toString( &cal );
00296 }
00297 
00298 QString ICalFormat::toString( Incidence *incidence )
00299 {
00300   icalcomponent *component;
00301 
00302   component = d->mImpl->writeIncidence( incidence );
00303 
00304   QString text = QString::fromUtf8( icalcomponent_as_ical_string( component ) );
00305 
00306   icalcomponent_free( component );
00307 
00308   return text;
00309 }
00310 
00311 QString ICalFormat::toString( RecurrenceRule *recurrence )
00312 {
00313   icalproperty *property;
00314   property = icalproperty_new_rrule( d->mImpl->writeRecurrenceRule( recurrence ) );
00315   QString text = QString::fromUtf8( icalproperty_as_ical_string( property ) );
00316   icalproperty_free( property );
00317   return text;
00318 }
00319 
00320 bool ICalFormat::fromString( RecurrenceRule *recurrence, const QString &rrule )
00321 {
00322   if ( !recurrence ) {
00323     return false;
00324   }
00325   bool success = true;
00326   icalerror_clear_errno();
00327   struct icalrecurrencetype recur = icalrecurrencetype_from_string( rrule.toLatin1() );
00328   if ( icalerrno != ICAL_NO_ERROR ) {
00329     kDebug(5800) << "Recurrence parsing error:" << icalerror_strerror( icalerrno );
00330     success = false;
00331   }
00332 
00333   if ( success ) {
00334     d->mImpl->readRecurrence( recur, recurrence );
00335   }
00336 
00337   return success;
00338 }
00339 
00340 QString ICalFormat::createScheduleMessage( IncidenceBase *incidence,
00341                                            iTIPMethod method )
00342 {
00343   icalcomponent *message = 0;
00344 
00345   // Handle scheduling ID being present
00346   if ( incidence->type() == "Event" || incidence->type() == "Todo" ) {
00347     Incidence *i = static_cast<Incidence*>( incidence );
00348     if ( i->schedulingID() != i->uid() ) {
00349       // We have a separation of scheduling ID and UID
00350       i = i->clone();
00351       i->setUid( i->schedulingID() );
00352       i->setSchedulingID( QString() );
00353 
00354       // Build the message with the cloned incidence
00355       message = d->mImpl->createScheduleComponent( i, method );
00356 
00357       // And clean up
00358       delete i;
00359     }
00360   }
00361 
00362   if ( message == 0 ) {
00363     message = d->mImpl->createScheduleComponent( incidence, method );
00364   }
00365 
00366   QString messageText = QString::fromUtf8( icalcomponent_as_ical_string( message ) );
00367 
00368   icalcomponent_free( message );
00369   return messageText;
00370 }
00371 
00372 FreeBusy *ICalFormat::parseFreeBusy( const QString &str )
00373 {
00374   clearException();
00375 
00376   icalcomponent *message;
00377   message = icalparser_parse_string( str.toUtf8() );
00378 
00379   if ( !message ) {
00380     return 0;
00381   }
00382 
00383   FreeBusy *freeBusy = 0;
00384 
00385   icalcomponent *c;
00386   for ( c = icalcomponent_get_first_component( message, ICAL_VFREEBUSY_COMPONENT );
00387         c != 0; c = icalcomponent_get_next_component( message, ICAL_VFREEBUSY_COMPONENT ) ) {
00388     FreeBusy *fb = d->mImpl->readFreeBusy( c );
00389 
00390     if ( freeBusy ) {
00391       freeBusy->merge( fb );
00392       delete fb;
00393     } else {
00394       freeBusy = fb;
00395     }
00396   }
00397 
00398   if ( !freeBusy ) {
00399     kDebug(5800) << "object is not a freebusy.";
00400   }
00401   return freeBusy;
00402 }
00403 
00404 ScheduleMessage *ICalFormat::parseScheduleMessage( Calendar *cal,
00405                                                    const QString &messageText )
00406 {
00407   setTimeSpec( cal->timeSpec() );
00408   clearException();
00409 
00410   if ( messageText.isEmpty() ) {
00411     setException(
00412       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00413                        QLatin1String( "messageText is empty, unable "
00414                                       "to parse into a ScheduleMessage" ) ) );
00415     return 0;
00416   }
00417 
00418   icalcomponent *message;
00419   message = icalparser_parse_string( messageText.toUtf8() );
00420 
00421   if ( !message ) {
00422     setException(
00423       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00424                        QLatin1String( "icalparser is unable to parse "
00425                                       "messageText into a ScheduleMessage" ) ) );
00426     return 0;
00427   }
00428 
00429   icalproperty *m =
00430     icalcomponent_get_first_property( message, ICAL_METHOD_PROPERTY );
00431   if ( !m ) {
00432     setException(
00433       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00434                        QLatin1String( "message does not contain an "
00435                                       "ICAL_METHOD_PROPERTY" ) ) );
00436     return 0;
00437   }
00438 
00439   // Populate the message's time zone collection with all VTIMEZONE components
00440   ICalTimeZones tzlist;
00441   ICalTimeZoneSource tzs;
00442   tzs.parse( message, tzlist );
00443 
00444   icalcomponent *c;
00445 
00446   IncidenceBase *incidence = 0;
00447   c = icalcomponent_get_first_component( message, ICAL_VEVENT_COMPONENT );
00448   if ( c ) {
00449     incidence = d->mImpl->readEvent( c, &tzlist );
00450   }
00451 
00452   if ( !incidence ) {
00453     c = icalcomponent_get_first_component( message, ICAL_VTODO_COMPONENT );
00454     if ( c ) {
00455       incidence = d->mImpl->readTodo( c, &tzlist );
00456     }
00457   }
00458 
00459   if ( !incidence ) {
00460     c = icalcomponent_get_first_component( message, ICAL_VJOURNAL_COMPONENT );
00461     if ( c ) {
00462       incidence = d->mImpl->readJournal( c, &tzlist );
00463     }
00464   }
00465 
00466   if ( !incidence ) {
00467     c = icalcomponent_get_first_component( message, ICAL_VFREEBUSY_COMPONENT );
00468     if ( c ) {
00469       incidence = d->mImpl->readFreeBusy( c );
00470     }
00471   }
00472 
00473   if ( !incidence ) {
00474     kDebug(5800) << "object is not a freebusy, event, todo or journal";
00475     setException(
00476       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00477                        QLatin1String( "object is not a freebusy, event, "
00478                                       "todo or journal" ) ) );
00479     return 0;
00480   }
00481 
00482   kDebug(5800) << "getting method...";
00483 
00484   icalproperty_method icalmethod = icalproperty_get_method( m );
00485   iTIPMethod method;
00486 
00487   switch ( icalmethod ) {
00488   case ICAL_METHOD_PUBLISH:
00489     method = iTIPPublish;
00490     break;
00491   case ICAL_METHOD_REQUEST:
00492     method = iTIPRequest;
00493     break;
00494   case ICAL_METHOD_REFRESH:
00495     method = iTIPRefresh;
00496     break;
00497   case ICAL_METHOD_CANCEL:
00498     method = iTIPCancel;
00499     break;
00500   case ICAL_METHOD_ADD:
00501     method = iTIPAdd;
00502     break;
00503   case ICAL_METHOD_REPLY:
00504     method = iTIPReply;
00505     break;
00506   case ICAL_METHOD_COUNTER:
00507     method = iTIPCounter;
00508     break;
00509   case ICAL_METHOD_DECLINECOUNTER:
00510     method = iTIPDeclineCounter;
00511     break;
00512   default:
00513     method = iTIPNoMethod;
00514     kDebug(5800) << "Unknown method";
00515     break;
00516   }
00517 
00518   kDebug(5800) << "restriction...";
00519 
00520   if ( !icalrestriction_check( message ) ) {
00521     kWarning(5800) << endl
00522                    << "kcal library reported a problem while parsing:";
00523     kWarning(5800) << Scheduler::translatedMethodName( method ) << ":"
00524                    << d->mImpl->extractErrorProperty( c );
00525   }
00526 
00527   Incidence *existingIncidence = cal->incidenceFromSchedulingID( incidence->uid() );
00528 
00529   icalcomponent *calendarComponent;
00530   if ( existingIncidence ) {
00531     calendarComponent = d->mImpl->createCalendarComponent( cal );
00532 
00533     // TODO: check, if cast is required, or if it can be done by virtual funcs.
00534     // TODO: Use a visitor for this!
00535     if ( existingIncidence->type() == "Todo" ) {
00536       Todo *todo = static_cast<Todo *>( existingIncidence );
00537       icalcomponent_add_component( calendarComponent,
00538                                    d->mImpl->writeTodo( todo ) );
00539     }
00540     if ( existingIncidence->type() == "Event" ) {
00541       Event *event = static_cast<Event *>( existingIncidence );
00542       icalcomponent_add_component( calendarComponent,
00543                                    d->mImpl->writeEvent( event ) );
00544     }
00545   } else {
00546     calendarComponent = 0;
00547   }
00548 
00549   kDebug(5800) << "classify...";
00550 
00551   icalproperty_xlicclass result =
00552     icalclassify( message, calendarComponent, (char *)"" );
00553 
00554   kDebug(5800) << "returning with result = " << result;
00555 
00556   ScheduleMessage::Status status;
00557 
00558   switch ( result ) {
00559   case ICAL_XLICCLASS_PUBLISHNEW:
00560     status = ScheduleMessage::PublishNew;
00561     break;
00562   case ICAL_XLICCLASS_PUBLISHUPDATE:
00563     status = ScheduleMessage::PublishUpdate;
00564     break;
00565   case ICAL_XLICCLASS_OBSOLETE:
00566     status = ScheduleMessage::Obsolete;
00567     break;
00568   case ICAL_XLICCLASS_REQUESTNEW:
00569     status = ScheduleMessage::RequestNew;
00570     break;
00571   case ICAL_XLICCLASS_REQUESTUPDATE:
00572     status = ScheduleMessage::RequestUpdate;
00573     break;
00574   case ICAL_XLICCLASS_UNKNOWN:
00575   default:
00576     status = ScheduleMessage::Unknown;
00577     break;
00578   }
00579 
00580   kDebug(5800) << "status =" << status;
00581 
00582   icalcomponent_free( message );
00583   icalcomponent_free( calendarComponent );
00584   return new ScheduleMessage( incidence, method, status );
00585 }
00586 
00587 void ICalFormat::setTimeSpec( const KDateTime::Spec &timeSpec )
00588 {
00589   d->mTimeSpec = timeSpec;
00590 }
00591 
00592 KDateTime::Spec ICalFormat::timeSpec() const
00593 {
00594   return d->mTimeSpec;
00595 }
00596 
00597 QString ICalFormat::timeZoneId() const
00598 {
00599   KTimeZone tz = d->mTimeSpec.timeZone();
00600   return tz.isValid() ? tz.name() : QString();
00601 }

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal