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