• Skip to content
  • Skip to link menu
KDE 4.1 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() << 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.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() << "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() << "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.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     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.begin();  it != zones.end();  ++it ) {
00266     icaltimezone *tz = (*it).icalTimezone();
00267     if ( !tz ) {
00268       kError() << "bad time zone";
00269     } else {
00270       component = icalcomponent_new_clone( icaltimezone_get_component( tz ) );
00271       icalcomponent_add_component( calendar, component );
00272       icaltimezone_free( tz, 1 );
00273     }
00274   }
00275 
00276   QString text = QString::fromUtf8( icalcomponent_as_ical_string( calendar ) );
00277 
00278   icalcomponent_free( calendar );
00279   icalmemory_free_ring();
00280 
00281   if ( text.isNull() ) {
00282     setException( new ErrorFormat( ErrorFormat::SaveError,
00283                                    i18n( "libical error" ) ) );
00284     return QString();
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   kDebug() << "getting method...";
00482 
00483   icalproperty_method icalmethod = icalproperty_get_method( m );
00484   iTIPMethod method;
00485 
00486   switch ( icalmethod ) {
00487   case ICAL_METHOD_PUBLISH:
00488     method = iTIPPublish;
00489     break;
00490   case ICAL_METHOD_REQUEST:
00491     method = iTIPRequest;
00492     break;
00493   case ICAL_METHOD_REFRESH:
00494     method = iTIPRefresh;
00495     break;
00496   case ICAL_METHOD_CANCEL:
00497     method = iTIPCancel;
00498     break;
00499   case ICAL_METHOD_ADD:
00500     method = iTIPAdd;
00501     break;
00502   case ICAL_METHOD_REPLY:
00503     method = iTIPReply;
00504     break;
00505   case ICAL_METHOD_COUNTER:
00506     method = iTIPCounter;
00507     break;
00508   case ICAL_METHOD_DECLINECOUNTER:
00509     method = iTIPDeclineCounter;
00510     break;
00511   default:
00512     method = iTIPNoMethod;
00513     kDebug() << "Unknown method";
00514     break;
00515   }
00516 
00517   kDebug() << "restriction...";
00518 
00519   if ( !icalrestriction_check( message ) ) {
00520     kWarning() << endl
00521                << "kcal library reported a problem while parsing:";
00522     kWarning() << Scheduler::translatedMethodName( method ) << ":"
00523                << d->mImpl->extractErrorProperty( c );
00524   }
00525 
00526   Incidence *existingIncidence = cal->incidenceFromSchedulingID( incidence->uid() );
00527 
00528   icalcomponent *calendarComponent;
00529   if ( existingIncidence ) {
00530     calendarComponent = d->mImpl->createCalendarComponent( cal );
00531 
00532     // TODO: check, if cast is required, or if it can be done by virtual funcs.
00533     // TODO: Use a visitor for this!
00534     if ( existingIncidence->type() == "Todo" ) {
00535       Todo *todo = static_cast<Todo *>( existingIncidence );
00536       icalcomponent_add_component( calendarComponent,
00537                                    d->mImpl->writeTodo( todo ) );
00538     }
00539     if ( existingIncidence->type() == "Event" ) {
00540       Event *event = static_cast<Event *>( existingIncidence );
00541       icalcomponent_add_component( calendarComponent,
00542                                    d->mImpl->writeEvent( event ) );
00543     }
00544   } else {
00545     calendarComponent = 0;
00546   }
00547 
00548   kDebug() << "classify...";
00549 
00550   icalproperty_xlicclass result =
00551     icalclassify( message, calendarComponent, (char *)"" );
00552 
00553   kDebug() << "returning with result = " << result;
00554 
00555   ScheduleMessage::Status status;
00556 
00557   switch ( result ) {
00558   case ICAL_XLICCLASS_PUBLISHNEW:
00559     status = ScheduleMessage::PublishNew;
00560     break;
00561   case ICAL_XLICCLASS_PUBLISHUPDATE:
00562     status = ScheduleMessage::PublishUpdate;
00563     break;
00564   case ICAL_XLICCLASS_OBSOLETE:
00565     status = ScheduleMessage::Obsolete;
00566     break;
00567   case ICAL_XLICCLASS_REQUESTNEW:
00568     status = ScheduleMessage::RequestNew;
00569     break;
00570   case ICAL_XLICCLASS_REQUESTUPDATE:
00571     status = ScheduleMessage::RequestUpdate;
00572     break;
00573   case ICAL_XLICCLASS_UNKNOWN:
00574   default:
00575     status = ScheduleMessage::Unknown;
00576     break;
00577   }
00578 
00579   kDebug() << "status =" << status;
00580 
00581   icalcomponent_free( message );
00582   icalcomponent_free( calendarComponent );
00583   return new ScheduleMessage( incidence, method, status );
00584 }
00585 
00586 void ICalFormat::setTimeSpec( const KDateTime::Spec &timeSpec )
00587 {
00588   d->mTimeSpec = timeSpec;
00589 }
00590 
00591 KDateTime::Spec ICalFormat::timeSpec() const
00592 {
00593   return d->mTimeSpec;
00594 }
00595 
00596 QString ICalFormat::timeZoneId() const
00597 {
00598   KTimeZone tz = d->mTimeSpec.timeZone();
00599   return tz.isValid() ? tz.name() : QString();
00600 }

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"
  • akonadi
  • 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.6
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