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