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 Mon Aug 27 2012 22:10:04 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:10:04 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.