00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <tqdatetime.h>
00023 #include <tqstring.h>
00024 #include <tqptrlist.h>
00025 #include <tqregexp.h>
00026 #include <tqclipboard.h>
00027 #include <tqfile.h>
00028 #include <tqtextstream.h>
00029
00030 #include <kdebug.h>
00031 #include <klocale.h>
00032
00033 extern "C" {
00034 #include <libical/ical.h>
00035 #include <libical/icalss.h>
00036 #include <libical/icalparser.h>
00037 #include <libical/icalrestriction.h>
00038 #include <libical/icalmemory.h>
00039 }
00040
00041 #include "calendar.h"
00042 #include "calendarlocal.h"
00043 #include "journal.h"
00044
00045 #include "icalformat.h"
00046 #include "icalformatimpl.h"
00047 #include <ksavefile.h>
00048
00049 #include <stdio.h>
00050
00051 #define _ICAL_VERSION "2.0"
00052
00053 using namespace KCal;
00054
00055 ICalFormat::ICalFormat() : mImpl(0)
00056 {
00057 setImplementation( new ICalFormatImpl( this ) );
00058
00059 mTimeZoneId = "UTC";
00060 mUtc = true;
00061 }
00062
00063 ICalFormat::~ICalFormat()
00064 {
00065 delete mImpl;
00066 }
00067
00068 void ICalFormat::setImplementation( ICalFormatImpl *impl )
00069 {
00070 if ( mImpl ) delete mImpl;
00071 mImpl = impl;
00072 }
00073
00074 #if defined(_AIX) && defined(open)
00075 #undef open
00076 #endif
00077
00078 bool ICalFormat::load( Calendar *calendar, const TQString &fileName)
00079 {
00080 kdDebug(5800) << "ICalFormat::load() " << fileName << endl;
00081
00082 clearException();
00083
00084 TQFile file( fileName );
00085 if (!file.open( IO_ReadOnly ) ) {
00086 kdDebug(5800) << "ICalFormat::load() load error" << endl;
00087 setException(new ErrorFormat(ErrorFormat::LoadError));
00088 return false;
00089 }
00090 TQTextStream ts( &file );
00091 ts.setEncoding( TQTextStream::Latin1 );
00092 TQString text = ts.read();
00093 file.close();
00094
00095 if ( text.stripWhiteSpace().isEmpty() )
00096 return true;
00097 else
00098 return fromRawString( calendar, text.latin1() );
00099 }
00100
00101
00102 bool ICalFormat::save( Calendar *calendar, const TQString &fileName )
00103 {
00104 kdDebug(5800) << "ICalFormat::save(): " << fileName << endl;
00105
00106 clearException();
00107
00108 TQString text = toString( calendar );
00109
00110 if ( text.isNull() ) return false;
00111
00112
00113 KSaveFile::backupFile( fileName );
00114
00115 KSaveFile file( fileName );
00116 if ( file.status() != 0 ) {
00117 kdDebug(5800) << "ICalFormat::save() errno: " << strerror( file.status() )
00118 << endl;
00119 setException( new ErrorFormat( ErrorFormat::SaveError,
00120 i18n( "Error saving to '%1'." ).arg( fileName ) ) );
00121 return false;
00122 }
00123
00124
00125 TQCString textUtf8 = text.utf8();
00126 file.file()->writeBlock( textUtf8.data(), textUtf8.size() - 1 );
00127
00128 if ( !file.close() ) {
00129 kdDebug(5800) << "KSaveFile: close: status was " << file.status() << ". See errno.h." << endl;
00130 setException(new ErrorFormat(ErrorFormat::SaveError,
00131 i18n("Could not save '%1'").arg(fileName)));
00132 return false;
00133 }
00134
00135 return true;
00136 }
00137
00138 bool ICalFormat::fromString( Calendar *cal, const TQString &text )
00139 {
00140 return fromRawString( cal, text.utf8() );
00141 }
00142
00143 bool ICalFormat::fromRawString( Calendar *cal, const TQCString &text )
00144 {
00145 setTimeZone( cal->timeZoneId(), !cal->isLocalTime() );
00146
00147
00148
00149 icalcomponent *calendar;
00150
00151
00152 calendar = icalcomponent_new_from_string( const_cast<char*>( (const char*)text ) );
00153
00154 if (!calendar) {
00155 kdDebug(5800) << "ICalFormat::load() parse error" << endl;
00156 setException(new ErrorFormat(ErrorFormat::ParseErrorIcal));
00157 return false;
00158 }
00159
00160 bool success = true;
00161
00162 if (icalcomponent_isa(calendar) == ICAL_XROOT_COMPONENT) {
00163 icalcomponent *comp;
00164 for ( comp = icalcomponent_get_first_component(calendar, ICAL_VCALENDAR_COMPONENT);
00165 comp != 0; comp = icalcomponent_get_next_component(calendar, ICAL_VCALENDAR_COMPONENT) ) {
00166
00167 if ( !mImpl->populate( cal, comp ) ) {
00168 kdDebug(5800) << "ICalFormat::load(): Could not populate calendar" << endl;
00169 if ( !exception() ) {
00170 setException(new ErrorFormat(ErrorFormat::ParseErrorKcal));
00171 }
00172 success = false;
00173 } else {
00174 mLoadedProductId = mImpl->loadedProductId();
00175 }
00176 icalcomponent_free( comp );
00177 }
00178 } else if (icalcomponent_isa(calendar) != ICAL_VCALENDAR_COMPONENT) {
00179 kdDebug(5800) << "ICalFormat::load(): No VCALENDAR component found" << endl;
00180 setException(new ErrorFormat(ErrorFormat::NoCalendar));
00181 success = false;
00182 } else {
00183
00184 if ( !mImpl->populate( cal, calendar ) ) {
00185 kdDebug(5800) << "ICalFormat::load(): Could not populate calendar" << endl;
00186 if ( !exception() ) {
00187 setException(new ErrorFormat(ErrorFormat::ParseErrorKcal));
00188 }
00189 success = false;
00190 } else
00191 mLoadedProductId = mImpl->loadedProductId();
00192 }
00193
00194 icalcomponent_free( calendar );
00195 icalmemory_free_ring();
00196
00197 return success;
00198 }
00199
00200 Incidence *ICalFormat::fromString( const TQString &text )
00201 {
00202 CalendarLocal cal( mTimeZoneId );
00203 fromString(&cal, text);
00204
00205 Incidence *ical = 0;
00206 Event::List elist = cal.events();
00207 if ( elist.count() > 0 ) {
00208 ical = elist.first();
00209 } else {
00210 Todo::List tlist = cal.todos();
00211 if ( tlist.count() > 0 ) {
00212 ical = tlist.first();
00213 } else {
00214 Journal::List jlist = cal.journals();
00215 if ( jlist.count() > 0 ) {
00216 ical = jlist.first();
00217 }
00218 }
00219 }
00220
00221 return ical ? ical->clone() : 0;
00222 }
00223
00224 TQString ICalFormat::toString( Calendar *cal )
00225 {
00226 setTimeZone( cal->timeZoneId(), !cal->isLocalTime() );
00227
00228 icalcomponent *calendar = mImpl->createCalendarComponent(cal);
00229
00230 icalcomponent *component;
00231
00232
00233 Todo::List todoList = cal->rawTodos();
00234 Todo::List::ConstIterator it;
00235 for( it = todoList.begin(); it != todoList.end(); ++it ) {
00236
00237
00238 component = mImpl->writeTodo( *it );
00239 icalcomponent_add_component( calendar, component );
00240 }
00241
00242
00243 Event::List events = cal->rawEvents();
00244 Event::List::ConstIterator it2;
00245 for( it2 = events.begin(); it2 != events.end(); ++it2 ) {
00246
00247
00248 component = mImpl->writeEvent( *it2 );
00249 icalcomponent_add_component( calendar, component );
00250 }
00251
00252
00253 Journal::List journals = cal->journals();
00254 Journal::List::ConstIterator it3;
00255 for( it3 = journals.begin(); it3 != journals.end(); ++it3 ) {
00256 kdDebug(5800) << "ICalFormat::toString() write journal "
00257 << (*it3)->uid() << endl;
00258 component = mImpl->writeJournal( *it3 );
00259 icalcomponent_add_component( calendar, component );
00260 }
00261
00262 TQString text = TQString::fromUtf8( icalcomponent_as_ical_string( calendar ) );
00263
00264 icalcomponent_free( calendar );
00265 icalmemory_free_ring();
00266
00267 if (!text) {
00268 setException(new ErrorFormat(ErrorFormat::SaveError,
00269 i18n("libical error")));
00270 return TQString::null;
00271 }
00272
00273 return text;
00274 }
00275
00276 TQString ICalFormat::toICalString( Incidence *incidence )
00277 {
00278 CalendarLocal cal( mTimeZoneId );
00279 cal.addIncidence( incidence->clone() );
00280 return toString( &cal );
00281 }
00282
00283 TQString ICalFormat::toString( Incidence *incidence )
00284 {
00285 icalcomponent *component;
00286
00287 component = mImpl->writeIncidence( incidence );
00288
00289 TQString text = TQString::fromUtf8( icalcomponent_as_ical_string( component ) );
00290
00291 icalcomponent_free( component );
00292
00293 return text;
00294 }
00295
00296 TQString ICalFormat::toString( Incidence *incidence, Calendar *calendar )
00297 {
00298 icalcomponent *component;
00299 TQString text = "";
00300
00301
00302 if ( incidence->hasRecurrenceID() ) {
00303
00304 IncidenceList il = incidence->childIncidences();
00305 IncidenceListIterator it;
00306 it = il.begin();
00307 Incidence *parentIncidence;
00308 parentIncidence = calendar->incidence(*it);
00309 il = parentIncidence->childIncidences();
00310 if (il.count() > 0) {
00311 for ( it = il.begin(); it != il.end(); ++it ) {
00312 component = mImpl->writeIncidence( calendar->incidence(*it) );
00313 text = text + TQString::fromUtf8( icalcomponent_as_ical_string( component ) );
00314 icalcomponent_free( component );
00315 }
00316 }
00317 component = mImpl->writeIncidence( parentIncidence );
00318 text = text + TQString::fromUtf8( icalcomponent_as_ical_string( component ) );
00319 icalcomponent_free( component );
00320 }
00321 else {
00322
00323 IncidenceList il = incidence->childIncidences();
00324 if (il.count() > 0) {
00325 IncidenceListIterator it;
00326 for ( it = il.begin(); it != il.end(); ++it ) {
00327 component = mImpl->writeIncidence( calendar->incidence(*it) );
00328 text = text + TQString::fromUtf8( icalcomponent_as_ical_string( component ) );
00329 icalcomponent_free( component );
00330 }
00331 }
00332 component = mImpl->writeIncidence( incidence );
00333 text = text + TQString::fromUtf8( icalcomponent_as_ical_string( component ) );
00334 icalcomponent_free( component );
00335 }
00336
00337 return text;
00338 }
00339
00340 TQString ICalFormat::toString( RecurrenceRule *recurrence )
00341 {
00342 icalproperty *property;
00343 property = icalproperty_new_rrule( mImpl->writeRecurrenceRule( recurrence ) );
00344 TQString text = TQString::fromUtf8( icalproperty_as_ical_string( property ) );
00345 icalproperty_free( property );
00346 return text;
00347 }
00348
00349 bool ICalFormat::fromString( RecurrenceRule * recurrence, const TQString& rrule )
00350 {
00351 if ( !recurrence ) return false;
00352 bool success = true;
00353 icalerror_clear_errno();
00354 struct icalrecurrencetype recur = icalrecurrencetype_from_string( rrule.latin1() );
00355 if ( icalerrno != ICAL_NO_ERROR ) {
00356 kdDebug(5800) << "Recurrence parsing error: " << icalerror_strerror( icalerrno ) << endl;
00357 success = false;
00358 }
00359
00360 if ( success ) {
00361 mImpl->readRecurrence( recur, recurrence );
00362 }
00363
00364 return success;
00365 }
00366
00367
00368 TQString ICalFormat::createScheduleMessage(IncidenceBase *incidence,
00369 Scheduler::Method method)
00370 {
00371 icalcomponent *message = 0;
00372
00373
00374 if ( incidence->type() == "Event" || incidence->type() == "Todo" ) {
00375 Incidence* i = static_cast<Incidence*>( incidence );
00376 if ( i->schedulingID() != i->uid() ) {
00377
00378 i = i->clone();
00379 i->setUid( i->schedulingID() );
00380 i->setSchedulingID( TQString::null );
00381
00382
00383 message = mImpl->createScheduleComponent( i, method );
00384
00385
00386 delete i;
00387 }
00388 }
00389
00390 if ( message == 0 )
00391 message = mImpl->createScheduleComponent(incidence,method);
00392
00393
00394 TQString messageText = TQString::fromUtf8( icalcomponent_as_ical_string(message) );
00395
00396 #if 0
00397 kdDebug(5800) << "ICalFormat::createScheduleMessage: message START\n"
00398 << messageText
00399 << "ICalFormat::createScheduleMessage: message END" << endl;
00400 #endif
00401
00402 return messageText;
00403 }
00404
00405 FreeBusy *ICalFormat::parseFreeBusy( const TQString &str )
00406 {
00407 clearException();
00408
00409 icalcomponent *message;
00410 message = icalparser_parse_string( str.utf8() );
00411
00412 if ( !message ) return 0;
00413
00414 FreeBusy *freeBusy = 0;
00415
00416 icalcomponent *c;
00417 for ( c = icalcomponent_get_first_component( message, ICAL_VFREEBUSY_COMPONENT );
00418 c != 0; c = icalcomponent_get_next_component( message, ICAL_VFREEBUSY_COMPONENT ) ) {
00419 FreeBusy *fb = mImpl->readFreeBusy( c );
00420
00421 if ( freeBusy ) {
00422 freeBusy->merge( fb );
00423 delete fb;
00424 } else {
00425 freeBusy = fb;
00426 }
00427 }
00428
00429 if ( !freeBusy )
00430 kdDebug(5800) << "ICalFormat:parseFreeBusy: object is not a freebusy."
00431 << endl;
00432 return freeBusy;
00433 }
00434
00435 ScheduleMessage *ICalFormat::parseScheduleMessage( Calendar *cal,
00436 const TQString &messageText )
00437 {
00438 setTimeZone( cal->timeZoneId(), !cal->isLocalTime() );
00439 clearException();
00440
00441 if (messageText.isEmpty())
00442 {
00443 setException( new ErrorFormat( ErrorFormat::ParseErrorKcal, TQString::fromLatin1( "messageText was empty, unable to parse into a ScheduleMessage" ) ) );
00444 return 0;
00445 }
00446
00447 icalcomponent *message;
00448 message = icalparser_parse_string(messageText.utf8());
00449
00450 if (!message)
00451 {
00452 setException( new ErrorFormat( ErrorFormat::ParseErrorKcal, TQString::fromLatin1( "icalparser was unable to parse messageText into a ScheduleMessage" ) ) );
00453 return 0;
00454 }
00455
00456 icalproperty *m = icalcomponent_get_first_property(message,
00457 ICAL_METHOD_PROPERTY);
00458 if (!m)
00459 {
00460 setException( new ErrorFormat( ErrorFormat::ParseErrorKcal, TQString::fromLatin1( "message didn't contain an ICAL_METHOD_PROPERTY" ) ) );
00461 return 0;
00462 }
00463
00464 icalcomponent *c;
00465
00466 IncidenceBase *incidence = 0;
00467 c = icalcomponent_get_first_component(message,ICAL_VEVENT_COMPONENT);
00468 if (c) {
00469 icalcomponent *ctz = icalcomponent_get_first_component(message,ICAL_VTIMEZONE_COMPONENT);
00470 incidence = mImpl->readEvent(c, ctz);
00471 }
00472
00473 if (!incidence) {
00474 c = icalcomponent_get_first_component(message,ICAL_VTODO_COMPONENT);
00475 if (c) {
00476 incidence = mImpl->readTodo(c);
00477 }
00478 }
00479
00480 if (!incidence) {
00481 c = icalcomponent_get_first_component(message,ICAL_VJOURNAL_COMPONENT);
00482 if (c) {
00483 incidence = mImpl->readJournal(c);
00484 }
00485 }
00486
00487 if (!incidence) {
00488 c = icalcomponent_get_first_component(message,ICAL_VFREEBUSY_COMPONENT);
00489 if (c) {
00490 incidence = mImpl->readFreeBusy(c);
00491 }
00492 }
00493
00494
00495
00496 if (!incidence) {
00497 kdDebug(5800) << "ICalFormat:parseScheduleMessage: object is not a freebusy, event, todo or journal" << endl;
00498 setException( new ErrorFormat( ErrorFormat::ParseErrorKcal, TQString::fromLatin1( "object is not a freebusy, event, todo or journal" ) ) );
00499 return 0;
00500 }
00501
00502 kdDebug(5800) << "ICalFormat::parseScheduleMessage() getting method..." << endl;
00503
00504 icalproperty_method icalmethod = icalproperty_get_method(m);
00505 Scheduler::Method method;
00506
00507 switch (icalmethod) {
00508 case ICAL_METHOD_PUBLISH:
00509 method = Scheduler::Publish;
00510 break;
00511 case ICAL_METHOD_REQUEST:
00512 method = Scheduler::Request;
00513 break;
00514 case ICAL_METHOD_REFRESH:
00515 method = Scheduler::Refresh;
00516 break;
00517 case ICAL_METHOD_CANCEL:
00518 method = Scheduler::Cancel;
00519 break;
00520 case ICAL_METHOD_ADD:
00521 method = Scheduler::Add;
00522 break;
00523 case ICAL_METHOD_REPLY:
00524 method = Scheduler::Reply;
00525 break;
00526 case ICAL_METHOD_COUNTER:
00527 method = Scheduler::Counter;
00528 break;
00529 case ICAL_METHOD_DECLINECOUNTER:
00530 method = Scheduler::Declinecounter;
00531 break;
00532 default:
00533 method = Scheduler::NoMethod;
00534 kdDebug(5800) << "ICalFormat::parseScheduleMessage(): Unknow method" << endl;
00535 break;
00536 }
00537
00538 kdDebug(5800) << "ICalFormat::parseScheduleMessage() restriction..." << endl;
00539
00540 if (!icalrestriction_check(message)) {
00541 kdWarning(5800) << k_funcinfo << endl << "libkcal reported a problem while parsing:" << endl;
00542 kdWarning(5800) << Scheduler::translatedMethodName(method) + ": " + mImpl->extractErrorProperty(c)<< endl;
00543
00544
00545
00546
00547
00548
00549
00550 }
00551 icalcomponent *calendarComponent = mImpl->createCalendarComponent(cal);
00552
00553 Incidence *existingIncidence =
00554 cal->incidenceFromSchedulingID(incidence->uid());
00555 if (existingIncidence) {
00556
00557
00558 if (existingIncidence->type() == "Todo") {
00559 Todo *todo = static_cast<Todo *>(existingIncidence);
00560 icalcomponent_add_component(calendarComponent,
00561 mImpl->writeTodo(todo));
00562 }
00563 if (existingIncidence->type() == "Event") {
00564 Event *event = static_cast<Event *>(existingIncidence);
00565 icalcomponent_add_component(calendarComponent,
00566 mImpl->writeEvent(event));
00567 }
00568 } else {
00569 calendarComponent = 0;
00570 }
00571
00572 kdDebug(5800) << "ICalFormat::parseScheduleMessage() classify..." << endl;
00573
00574 icalproperty_xlicclass result = icalclassify( message, calendarComponent,
00575 (char *)"" );
00576
00577 kdDebug(5800) << "ICalFormat::parseScheduleMessage() returning..." << endl;
00578 kdDebug(5800) << "ICalFormat::parseScheduleMessage(), result = " << result << endl;
00579
00580 ScheduleMessage::Status status;
00581
00582 switch (result) {
00583 case ICAL_XLICCLASS_PUBLISHNEW:
00584 status = ScheduleMessage::PublishNew;
00585 break;
00586 case ICAL_XLICCLASS_PUBLISHUPDATE:
00587 status = ScheduleMessage::PublishUpdate;
00588 break;
00589 case ICAL_XLICCLASS_OBSOLETE:
00590 status = ScheduleMessage::Obsolete;
00591 break;
00592 case ICAL_XLICCLASS_REQUESTNEW:
00593 status = ScheduleMessage::RequestNew;
00594 break;
00595 case ICAL_XLICCLASS_REQUESTUPDATE:
00596 status = ScheduleMessage::RequestUpdate;
00597 break;
00598 case ICAL_XLICCLASS_UNKNOWN:
00599 default:
00600 status = ScheduleMessage::Unknown;
00601 break;
00602 }
00603
00604 kdDebug(5800) << "ICalFormat::parseScheduleMessage(), status = " << status << endl;
00605
00606
00607 return new ScheduleMessage(incidence,method,status);
00608 }
00609
00610 void ICalFormat::setTimeZone( const TQString &id, bool utc )
00611 {
00612 mTimeZoneId = id;
00613 mUtc = utc;
00614 }
00615
00616 TQString ICalFormat::timeZoneId() const
00617 {
00618 return mTimeZoneId;
00619 }
00620
00621 bool ICalFormat::utc() const
00622 {
00623 return mUtc;
00624 }