• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.8.3 API Reference
  • KDE Home
  • 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 <libical/ical.h>
00040   #include <libical/icalss.h>
00041   #include <libical/icalparser.h>
00042   #include <libical/icalrestriction.h>
00043   #include <libical/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() << fileName;
00088 
00089   clearException();
00090 
00091   QFile file( fileName );
00092   if ( !file.open( QIODevice::ReadOnly ) ) {
00093     kDebug() << "load error";
00094     setException( new ErrorFormat( ErrorFormat::LoadError ) );
00095     return false;
00096   }
00097   QTextStream ts( &file );
00098   ts.setCodec( "ISO 8859-1" );
00099   QByteArray text = ts.readAll().trimmed().toLatin1();
00100   file.close();
00101 
00102   if ( text.isEmpty() ) {
00103     // empty files are valid
00104     return true;
00105   } else {
00106     return fromRawString( calendar, text );
00107   }
00108 }
00109 
00110 bool ICalFormat::save( Calendar *calendar, const QString &fileName )
00111 {
00112   kDebug() << fileName;
00113 
00114   clearException();
00115 
00116   QString text = toString( calendar );
00117   if ( text.isEmpty() ) {
00118     return false;
00119   }
00120 
00121   // Write backup file
00122   KSaveFile::backupFile( fileName );
00123 
00124   KSaveFile file( fileName );
00125   if ( !file.open() ) {
00126     kDebug() << "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() );
00135 
00136   if ( !file.finalize() ) {
00137     kDebug() << "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() << "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() << "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() << "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() << "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.constBegin(); it != todoList.constEnd(); ++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.constBegin(); it2 != events.constEnd(); ++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.constBegin(); it3 != journals.constEnd(); ++it3 ) {
00259     component = d->mImpl->writeJournal( *it3, tzlist, &tzUsedList );
00260     icalcomponent_add_component( calendar, component );
00261   }
00262 
00263   // time zones
00264   const ICalTimeZones::ZoneMap zones = tzUsedList.zones();
00265   for ( ICalTimeZones::ZoneMap::ConstIterator it=zones.constBegin();
00266         it != zones.constEnd(); ++it ) {
00267     icaltimezone *tz = (*it).icalTimezone();
00268     if ( !tz ) {
00269       kError() << "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.isEmpty() ) {
00283     setException( new ErrorFormat( ErrorFormat::SaveError,
00284                                    i18n( "libical error" ) ) );
00285   }
00286 
00287   return text;
00288 }
00289 
00290 QString ICalFormat::toICalString( Incidence *incidence )
00291 {
00292   CalendarLocal cal( d->mTimeSpec );
00293   cal.addIncidence( incidence->clone() );
00294   return toString( &cal );
00295 }
00296 
00297 QString ICalFormat::toString( Incidence *incidence )
00298 {
00299   icalcomponent *component;
00300 
00301   component = d->mImpl->writeIncidence( incidence );
00302 
00303   QString text = QString::fromUtf8( icalcomponent_as_ical_string( component ) );
00304 
00305   icalcomponent_free( component );
00306 
00307   return text;
00308 }
00309 
00310 QString ICalFormat::toString( RecurrenceRule *recurrence )
00311 {
00312   icalproperty *property;
00313   property = icalproperty_new_rrule( d->mImpl->writeRecurrenceRule( recurrence ) );
00314   QString text = QString::fromUtf8( icalproperty_as_ical_string( property ) );
00315   icalproperty_free( property );
00316   return text;
00317 }
00318 
00319 bool ICalFormat::fromString( RecurrenceRule *recurrence, const QString &rrule )
00320 {
00321   if ( !recurrence ) {
00322     return false;
00323   }
00324   bool success = true;
00325   icalerror_clear_errno();
00326   struct icalrecurrencetype recur = icalrecurrencetype_from_string( rrule.toLatin1() );
00327   if ( icalerrno != ICAL_NO_ERROR ) {
00328     kDebug() << "Recurrence parsing error:" << icalerror_strerror( icalerrno );
00329     success = false;
00330   }
00331 
00332   if ( success ) {
00333     d->mImpl->readRecurrence( recur, recurrence );
00334   }
00335 
00336   return success;
00337 }
00338 
00339 QString ICalFormat::createScheduleMessage( IncidenceBase *incidence,
00340                                            iTIPMethod method )
00341 {
00342   icalcomponent *message = 0;
00343 
00344   // Handle scheduling ID being present
00345   if ( incidence->type() == "Event" || incidence->type() == "Todo" ) {
00346     Incidence *i = static_cast<Incidence*>( incidence );
00347     if ( i->schedulingID() != i->uid() ) {
00348       // We have a separation of scheduling ID and UID
00349       i = i->clone();
00350       i->setUid( i->schedulingID() );
00351       i->setSchedulingID( QString() );
00352 
00353       // Build the message with the cloned incidence
00354       message = d->mImpl->createScheduleComponent( i, method );
00355 
00356       // And clean up
00357       delete i;
00358     }
00359   }
00360 
00361   if ( message == 0 ) {
00362     message = d->mImpl->createScheduleComponent( incidence, method );
00363   }
00364 
00365   QString messageText = QString::fromUtf8( icalcomponent_as_ical_string( message ) );
00366 
00367   icalcomponent_free( message );
00368   return messageText;
00369 }
00370 
00371 FreeBusy *ICalFormat::parseFreeBusy( const QString &str )
00372 {
00373   clearException();
00374 
00375   icalcomponent *message;
00376   message = icalparser_parse_string( str.toUtf8() );
00377 
00378   if ( !message ) {
00379     return 0;
00380   }
00381 
00382   FreeBusy *freeBusy = 0;
00383 
00384   icalcomponent *c;
00385   for ( c = icalcomponent_get_first_component( message, ICAL_VFREEBUSY_COMPONENT );
00386         c != 0; c = icalcomponent_get_next_component( message, ICAL_VFREEBUSY_COMPONENT ) ) {
00387     FreeBusy *fb = d->mImpl->readFreeBusy( c );
00388 
00389     if ( freeBusy ) {
00390       freeBusy->merge( fb );
00391       delete fb;
00392     } else {
00393       freeBusy = fb;
00394     }
00395   }
00396 
00397   if ( !freeBusy ) {
00398     kDebug() << "object is not a freebusy.";
00399   }
00400   return freeBusy;
00401 }
00402 
00403 ScheduleMessage *ICalFormat::parseScheduleMessage( Calendar *cal,
00404                                                    const QString &messageText )
00405 {
00406   setTimeSpec( cal->timeSpec() );
00407   clearException();
00408 
00409   if ( messageText.isEmpty() ) {
00410     setException(
00411       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00412                        QLatin1String( "messageText is empty, unable "
00413                                       "to parse into a ScheduleMessage" ) ) );
00414     return 0;
00415   }
00416 
00417   icalcomponent *message;
00418   message = icalparser_parse_string( messageText.toUtf8() );
00419 
00420   if ( !message ) {
00421     setException(
00422       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00423                        QLatin1String( "icalparser is unable to parse "
00424                                       "messageText into a ScheduleMessage" ) ) );
00425     return 0;
00426   }
00427 
00428   icalproperty *m =
00429     icalcomponent_get_first_property( message, ICAL_METHOD_PROPERTY );
00430   if ( !m ) {
00431     setException(
00432       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00433                        QLatin1String( "message does not contain an "
00434                                       "ICAL_METHOD_PROPERTY" ) ) );
00435     return 0;
00436   }
00437 
00438   // Populate the message's time zone collection with all VTIMEZONE components
00439   ICalTimeZones tzlist;
00440   ICalTimeZoneSource tzs;
00441   tzs.parse( message, tzlist );
00442 
00443   icalcomponent *c;
00444 
00445   IncidenceBase *incidence = 0;
00446   c = icalcomponent_get_first_component( message, ICAL_VEVENT_COMPONENT );
00447   if ( c ) {
00448     incidence = d->mImpl->readEvent( c, &tzlist );
00449   }
00450 
00451   if ( !incidence ) {
00452     c = icalcomponent_get_first_component( message, ICAL_VTODO_COMPONENT );
00453     if ( c ) {
00454       incidence = d->mImpl->readTodo( c, &tzlist );
00455     }
00456   }
00457 
00458   if ( !incidence ) {
00459     c = icalcomponent_get_first_component( message, ICAL_VJOURNAL_COMPONENT );
00460     if ( c ) {
00461       incidence = d->mImpl->readJournal( c, &tzlist );
00462     }
00463   }
00464 
00465   if ( !incidence ) {
00466     c = icalcomponent_get_first_component( message, ICAL_VFREEBUSY_COMPONENT );
00467     if ( c ) {
00468       incidence = d->mImpl->readFreeBusy( c );
00469     }
00470   }
00471 
00472   if ( !incidence ) {
00473     kDebug() << "object is not a freebusy, event, todo or journal";
00474     setException(
00475       new ErrorFormat( ErrorFormat::ParseErrorKcal,
00476                        QLatin1String( "object is not a freebusy, event, "
00477                                       "todo or journal" ) ) );
00478     return 0;
00479   }
00480 
00481   icalproperty_method icalmethod = icalproperty_get_method( m );
00482   iTIPMethod method;
00483 
00484   switch ( icalmethod ) {
00485   case ICAL_METHOD_PUBLISH:
00486     method = iTIPPublish;
00487     break;
00488   case ICAL_METHOD_REQUEST:
00489     method = iTIPRequest;
00490     break;
00491   case ICAL_METHOD_REFRESH:
00492     method = iTIPRefresh;
00493     break;
00494   case ICAL_METHOD_CANCEL:
00495     method = iTIPCancel;
00496     break;
00497   case ICAL_METHOD_ADD:
00498     method = iTIPAdd;
00499     break;
00500   case ICAL_METHOD_REPLY:
00501     method = iTIPReply;
00502     break;
00503   case ICAL_METHOD_COUNTER:
00504     method = iTIPCounter;
00505     break;
00506   case ICAL_METHOD_DECLINECOUNTER:
00507     method = iTIPDeclineCounter;
00508     break;
00509   default:
00510     method = iTIPNoMethod;
00511     kDebug() << "Unknown method";
00512     break;
00513   }
00514 
00515   if ( !icalrestriction_check( message ) ) {
00516     kWarning() << endl
00517                << "kcal library reported a problem while parsing:";
00518     kWarning() << Scheduler::translatedMethodName( method ) << ":"
00519                << d->mImpl->extractErrorProperty( c );
00520   }
00521 
00522   Incidence *existingIncidence = cal->incidence( incidence->uid() );
00523 
00524   icalcomponent *calendarComponent = 0;
00525   if ( existingIncidence ) {
00526     calendarComponent = d->mImpl->createCalendarComponent( cal );
00527 
00528     // TODO: check, if cast is required, or if it can be done by virtual funcs.
00529     // TODO: Use a visitor for this!
00530     if ( existingIncidence->type() == "Todo" ) {
00531       Todo *todo = static_cast<Todo *>( existingIncidence );
00532       icalcomponent_add_component( calendarComponent,
00533                                    d->mImpl->writeTodo( todo ) );
00534     }
00535     if ( existingIncidence->type() == "Event" ) {
00536       Event *event = static_cast<Event *>( existingIncidence );
00537       icalcomponent_add_component( calendarComponent,
00538                                    d->mImpl->writeEvent( event ) );
00539     }
00540   } else {
00541     icalcomponent_free( message );
00542     return new ScheduleMessage( incidence, method, ScheduleMessage::Unknown );
00543   }
00544 
00545   icalproperty_xlicclass result =
00546     icalclassify( message, calendarComponent, (const char *)"" );
00547 
00548   ScheduleMessage::Status status;
00549 
00550   switch ( result ) {
00551   case ICAL_XLICCLASS_PUBLISHNEW:
00552     status = ScheduleMessage::PublishNew;
00553     break;
00554   case ICAL_XLICCLASS_PUBLISHUPDATE:
00555     status = ScheduleMessage::PublishUpdate;
00556     break;
00557   case ICAL_XLICCLASS_OBSOLETE:
00558     status = ScheduleMessage::Obsolete;
00559     break;
00560   case ICAL_XLICCLASS_REQUESTNEW:
00561     status = ScheduleMessage::RequestNew;
00562     break;
00563   case ICAL_XLICCLASS_REQUESTUPDATE:
00564     status = ScheduleMessage::RequestUpdate;
00565     break;
00566   case ICAL_XLICCLASS_UNKNOWN:
00567   default:
00568     status = ScheduleMessage::Unknown;
00569     break;
00570   }
00571 
00572   icalcomponent_free( message );
00573   icalcomponent_free( calendarComponent );
00574 
00575   return new ScheduleMessage( incidence, method, status );
00576 }
00577 
00578 void ICalFormat::setTimeSpec( const KDateTime::Spec &timeSpec )
00579 {
00580   d->mTimeSpec = timeSpec;
00581 }
00582 
00583 KDateTime::Spec ICalFormat::timeSpec() const
00584 {
00585   return d->mTimeSpec;
00586 }
00587 
00588 QString ICalFormat::timeZoneId() const
00589 {
00590   KTimeZone tz = d->mTimeSpec.timeZone();
00591   return tz.isValid() ? tz.name() : QString();
00592 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 22:19:47 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCal Library

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

kdepimlibs-4.8.3 API Reference

Skip menu "kdepimlibs-4.8.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal