libkcal

icalformatimpl.cpp
1 /*
2  This file is part of libkcal.
3 
4  Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
5  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 #include <tqdatetime.h>
24 #include <tqstring.h>
25 #include <tqptrlist.h>
26 #include <tqfile.h>
27 #include <cstdlib>
28 
29 #include <kdebug.h>
30 #include <klocale.h>
31 #include <kmdcodec.h>
32 
33 extern "C" {
34  #include <libical/ical.h>
35  #include <libical/icalparser.h>
36  #include <libical/icalrestriction.h>
37 }
38 
39 #include "calendar.h"
40 #include "journal.h"
41 #include "icalformat.h"
42 #include "icalformatimpl.h"
43 #include "compat.h"
44 
45 #include "config.h"
46 
47 #define _ICAL_VERSION "2.0"
48 
49 using namespace KCal;
50 
51 /* Static helpers */
52 static TQDateTime ICalDate2TQDate(const icaltimetype& t)
53 {
54  // Outlook sends dates starting from 1601-01-01, but TQDate()
55  // can only handle dates starting 1752-09-14.
56  const int year = (t.year>=1754) ? t.year : 1754;
57  return TQDateTime(TQDate(year,t.month,t.day), TQTime(t.hour,t.minute,t.second));
58 }
59 
60 /*
61 static void _dumpIcaltime( const icaltimetype& t)
62 {
63  kdDebug(5800) << "--- Y: " << t.year << " M: " << t.month << " D: " << t.day
64  << endl;
65  kdDebug(5800) << "--- H: " << t.hour << " M: " << t.minute << " S: " << t.second
66  << endl;
67  kdDebug(5800) << "--- isUtc: " << icaltime_is_utc( t )<< endl;
68  kdDebug(5800) << "--- zoneId: " << icaltimezone_get_tzid( const_cast<icaltimezone*>( t.zone ) )<< endl;
69 }
70 */
71 
72 static TQString quoteForParam( const TQString &text )
73 {
74  TQString tmp = text;
75  tmp.remove( '"' );
76  if ( tmp.contains( ';' ) || tmp.contains( ':' ) || tmp.contains( ',' ) )
77  return tmp; // libical quotes in this case already, see icalparameter_as_ical_string()
78  return TQString::fromLatin1( "\"" ) + tmp + TQString::fromLatin1( "\"" );
79 }
80 
81 const int gSecondsPerMinute = 60;
82 const int gSecondsPerHour = gSecondsPerMinute * 60;
83 const int gSecondsPerDay = gSecondsPerHour * 24;
84 const int gSecondsPerWeek = gSecondsPerDay * 7;
85 
86 ICalFormatImpl::ICalFormatImpl( ICalFormat *parent ) :
87  mParent( parent ), mCompat( new Compat )
88 {
89 }
90 
91 ICalFormatImpl::~ICalFormatImpl()
92 {
93  delete mCompat;
94 }
95 
96 class ICalFormatImpl::ToComponentVisitor : public IncidenceBase::Visitor
97 {
98  public:
99  ToComponentVisitor( ICalFormatImpl *impl, Scheduler::Method m ) : mImpl( impl ), mComponent( 0 ), mMethod( m ) {}
100 
101  bool visit( Event *e ) { mComponent = mImpl->writeEvent( e ); return true; }
102  bool visit( Todo *e ) { mComponent = mImpl->writeTodo( e ); return true; }
103  bool visit( Journal *e ) { mComponent = mImpl->writeJournal( e ); return true; }
104  bool visit( FreeBusy *fb ) { mComponent = mImpl->writeFreeBusy( fb, mMethod ); return true; }
105 
106  icalcomponent *component() { return mComponent; }
107 
108  private:
109  ICalFormatImpl *mImpl;
110  icalcomponent *mComponent;
111  Scheduler::Method mMethod;
112 };
113 
114 icalcomponent *ICalFormatImpl::writeIncidence( IncidenceBase *incidence, Scheduler::Method method )
115 {
116  ToComponentVisitor v( this, method );
117  if ( incidence->accept(v) )
118  return v.component();
119  else return 0;
120 }
121 
122 icalcomponent *ICalFormatImpl::writeTodo(Todo *todo)
123 {
124  TQString tmpStr;
125  TQStringList tmpStrList;
126 
127  icalcomponent *vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
128 
129  writeIncidence(vtodo,todo);
130 
131  // due date
132  if (todo->hasDueDate()) {
133  icaltimetype due;
134  if (todo->doesFloat()) {
135  due = writeICalDate(todo->dtDue(true).date());
136  } else {
137  due = writeICalDateTime(todo->dtDue(true));
138  }
139  icalcomponent_add_property(vtodo,icalproperty_new_due(due));
140  }
141 
142  // start time
143  if ( todo->hasStartDate() || todo->doesRecur() ) {
144  icaltimetype start;
145  if (todo->doesFloat()) {
146 // kdDebug(5800) << " Incidence " << todo->summary() << " floats." << endl;
147  start = writeICalDate(todo->dtStart(true).date());
148  } else {
149 // kdDebug(5800) << " incidence " << todo->summary() << " has time." << endl;
150  start = writeICalDateTime(todo->dtStart(true));
151  }
152  icalcomponent_add_property(vtodo,icalproperty_new_dtstart(start));
153  }
154 
155  // completion date
156  if (todo->isCompleted()) {
157  if (!todo->hasCompletedDate()) {
158  // If todo was created by KOrganizer <2.2 it has no correct completion
159  // date. Set it to now.
160  todo->setCompleted(TQDateTime::currentDateTime());
161  }
162  icaltimetype completed = writeICalDateTime(todo->completed());
163  icalcomponent_add_property(vtodo,icalproperty_new_completed(completed));
164  }
165 
166  icalcomponent_add_property(vtodo,
167  icalproperty_new_percentcomplete(todo->percentComplete()));
168 
169  if( todo->doesRecur() ) {
170  icalcomponent_add_property(vtodo,
171  icalproperty_new_recurrenceid( writeICalDateTime( todo->dtDue())));
172  }
173 
174  return vtodo;
175 }
176 
177 icalcomponent *ICalFormatImpl::writeEvent(Event *event)
178 {
179 #if 0
180  kdDebug(5800) << "Write Event '" << event->summary() << "' (" << event->uid()
181  << ")" << endl;
182 #endif
183 
184  TQString tmpStr;
185  TQStringList tmpStrList;
186 
187  icalcomponent *vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
188 
189  writeIncidence(vevent,event);
190 
191  // start time
192  icaltimetype start;
193  if (event->doesFloat()) {
194 // kdDebug(5800) << " Incidence " << event->summary() << " floats." << endl;
195  start = writeICalDate(event->dtStart().date());
196  } else {
197 // kdDebug(5800) << " incidence " << event->summary() << " has time." << endl;
198  start = writeICalDateTime(event->dtStart());
199  }
200  icalcomponent_add_property(vevent,icalproperty_new_dtstart(start));
201 
202  if (event->hasEndDate()) {
203  // End time.
204  // RFC2445 says that if DTEND is present, it has to be greater than DTSTART.
205  icaltimetype end;
206  if (event->doesFloat()) {
207 // kdDebug(5800) << " Event " << event->summary() << " floats." << endl;
208  // +1 day because end date is non-inclusive.
209  end = writeICalDate( event->dtEnd().date().addDays( 1 ) );
210  icalcomponent_add_property(vevent,icalproperty_new_dtend(end));
211  } else {
212 // kdDebug(5800) << " Event " << event->summary() << " has time." << endl;
213  if (event->dtEnd() != event->dtStart()) {
214  end = writeICalDateTime(event->dtEnd());
215  icalcomponent_add_property(vevent,icalproperty_new_dtend(end));
216  }
217  }
218  }
219 
220 // TODO: resources
221 #if 0
222  // resources
223  tmpStrList = anEvent->resources();
224  tmpStr = tmpStrList.join(";");
225  if (!tmpStr.isEmpty())
226  addPropValue(vevent, VCResourcesProp, tmpStr.utf8());
227 
228 #endif
229 
230  // Transparency
231  switch( event->transparency() ) {
232  case Event::Transparent:
233  icalcomponent_add_property(
234  vevent,
235  icalproperty_new_transp( ICAL_TRANSP_TRANSPARENT ) );
236  break;
237  case Event::Opaque:
238  icalcomponent_add_property(
239  vevent,
240  icalproperty_new_transp( ICAL_TRANSP_OPAQUE ) );
241  break;
242  }
243 
244  return vevent;
245 }
246 
247 icalcomponent *ICalFormatImpl::writeFreeBusy(FreeBusy *freebusy,
248  Scheduler::Method method)
249 {
250  kdDebug(5800) << "icalformatimpl: writeFreeBusy: startDate: "
251  << freebusy->dtStart().toString("ddd MMMM d yyyy: h:m:s ap") << " End Date: "
252  << freebusy->dtEnd().toString("ddd MMMM d yyyy: h:m:s ap") << endl;
253 
254  icalcomponent *vfreebusy = icalcomponent_new(ICAL_VFREEBUSY_COMPONENT);
255 
256  writeIncidenceBase(vfreebusy,freebusy);
257 
258  icalcomponent_add_property(vfreebusy, icalproperty_new_dtstart(
259  writeICalDateTime(freebusy->dtStart())));
260 
261  icalcomponent_add_property(vfreebusy, icalproperty_new_dtend(
262  writeICalDateTime(freebusy->dtEnd())));
263 
264  if (method == Scheduler::Request) {
265  icalcomponent_add_property(vfreebusy,icalproperty_new_uid(
266  freebusy->uid().utf8()));
267  }
268 
269  //Loops through all the periods in the freebusy object
270  TQValueList<Period> list = freebusy->busyPeriods();
271  TQValueList<Period>::Iterator it;
272  icalperiodtype period = icalperiodtype_null_period();
273  for (it = list.begin(); it!= list.end(); ++it) {
274  period.start = writeICalDateTime((*it).start());
275  if ( (*it).hasDuration() ) {
276  period.duration = writeICalDuration( (*it).duration().asSeconds() );
277  } else {
278  period.end = writeICalDateTime((*it).end());
279  }
280  icalcomponent_add_property(vfreebusy, icalproperty_new_freebusy(period) );
281  }
282 
283  return vfreebusy;
284 }
285 
286 icalcomponent *ICalFormatImpl::writeJournal(Journal *journal)
287 {
288  icalcomponent *vjournal = icalcomponent_new(ICAL_VJOURNAL_COMPONENT);
289 
290  writeIncidence(vjournal,journal);
291 
292  // start time
293  if (journal->dtStart().isValid()) {
294  icaltimetype start;
295  if (journal->doesFloat()) {
296 // kdDebug(5800) << " Incidence " << event->summary() << " floats." << endl;
297  start = writeICalDate(journal->dtStart().date());
298  } else {
299 // kdDebug(5800) << " incidence " << event->summary() << " has time." << endl;
300  start = writeICalDateTime(journal->dtStart());
301  }
302  icalcomponent_add_property(vjournal,icalproperty_new_dtstart(start));
303  }
304 
305  return vjournal;
306 }
307 
308 void ICalFormatImpl::writeIncidence(icalcomponent *parent,Incidence *incidence)
309 {
310  // pilot sync stuff
311 // TODO: move this application-specific code to kpilot
312  if (incidence->pilotId()) {
313  // NOTE: we can't do setNonKDECustomProperty here because this changes
314  // data and triggers an updated() event...
315  // incidence->setNonKDECustomProperty("X-PILOTSTAT", TQString::number(incidence->syncStatus()));
316  // incidence->setNonKDECustomProperty("X-PILOTID", TQString::number(incidence->pilotId()));
317 
318  icalproperty *p = 0;
319  p = icalproperty_new_x(TQString::number(incidence->syncStatus()).utf8());
320  icalproperty_set_x_name(p,"X-PILOTSTAT");
321  icalcomponent_add_property(parent,p);
322 
323  p = icalproperty_new_x(TQString::number(incidence->pilotId()).utf8());
324  icalproperty_set_x_name(p,"X-PILOTID");
325  icalcomponent_add_property(parent,p);
326  }
327 
328  TQString modifiedUid;
329  if ( incidence->hasRecurrenceID() ) {
330  // Recurring incidences are special; they must match their parent's UID
331  // Each child has the parent set as the first item in the list
332  // So, get and set the UID...
333  IncidenceList il = incidence->childIncidences();
334  IncidenceListIterator it;
335  it = il.begin();
336  modifiedUid = (*it);
337  }
338  else {
339  modifiedUid = incidence->uid();
340  }
341 
342  if ( incidence->schedulingID() != modifiedUid )
343  // We need to store the UID in here. The rawSchedulingID will
344  // go into the iCal UID component
345  incidence->setCustomProperty( "LIBKCAL", "ID", modifiedUid );
346  else
347  incidence->removeCustomProperty( "LIBKCAL", "ID" );
348 
349  writeIncidenceBase(parent,incidence);
350 
351  // creation date
352  icalcomponent_add_property(parent,icalproperty_new_created(
353  writeICalDateTime(incidence->created())));
354 
355  // unique id
356  // If the scheduling ID is different from the real UID, the real
357  // one is stored on X-REALID above
358  if ( incidence->hasRecurrenceID() ) {
359  // Recurring incidences are special; they must match their parent's UID
360  icalcomponent_add_property(parent,icalproperty_new_uid(modifiedUid.utf8()));
361  }
362  else {
363  if ( !incidence->schedulingID().isEmpty() ) {
364  icalcomponent_add_property(parent,icalproperty_new_uid(
365  incidence->schedulingID().utf8()));
366  }
367  }
368 
369  // revision
370  if ( incidence->revision() > 0 ) { // 0 is default, so don't write that out
371  icalcomponent_add_property(parent,icalproperty_new_sequence(
372  incidence->revision()));
373  }
374 
375  // last modification date
376  if ( incidence->lastModified().isValid() ) {
377  icalcomponent_add_property(parent,icalproperty_new_lastmodified(
378  writeICalDateTime(incidence->lastModified())));
379  }
380 
381  // description
382  if (!incidence->description().isEmpty()) {
383  icalcomponent_add_property(parent,icalproperty_new_description(
384  incidence->description().utf8()));
385  }
386 
387  // summary
388  if (!incidence->summary().isEmpty()) {
389  icalcomponent_add_property(parent,icalproperty_new_summary(
390  incidence->summary().utf8()));
391  }
392 
393  // location
394  if (!incidence->location().isEmpty()) {
395  icalcomponent_add_property(parent,icalproperty_new_location(
396  incidence->location().utf8()));
397  }
398 
399  // status
400  icalproperty_status status = ICAL_STATUS_NONE;
401  switch (incidence->status()) {
402  case Incidence::StatusTentative: status = ICAL_STATUS_TENTATIVE; break;
403  case Incidence::StatusConfirmed: status = ICAL_STATUS_CONFIRMED; break;
404  case Incidence::StatusCompleted: status = ICAL_STATUS_COMPLETED; break;
405  case Incidence::StatusNeedsAction: status = ICAL_STATUS_NEEDSACTION; break;
406  case Incidence::StatusCanceled: status = ICAL_STATUS_CANCELLED; break;
407  case Incidence::StatusInProcess: status = ICAL_STATUS_INPROCESS; break;
408  case Incidence::StatusDraft: status = ICAL_STATUS_DRAFT; break;
409  case Incidence::StatusFinal: status = ICAL_STATUS_FINAL; break;
410  case Incidence::StatusX: {
411  icalproperty* p = icalproperty_new_status(ICAL_STATUS_X);
412  icalvalue_set_x(icalproperty_get_value(p), incidence->statusStr().utf8());
413  icalcomponent_add_property(parent, p);
414  break;
415  }
416  case Incidence::StatusNone:
417  default:
418  break;
419  }
420  if (status != ICAL_STATUS_NONE)
421  icalcomponent_add_property(parent, icalproperty_new_status(status));
422 
423  // secrecy
424  icalproperty_class secClass;
425  switch (incidence->secrecy()) {
426  case Incidence::SecrecyPublic:
427  secClass = ICAL_CLASS_PUBLIC;
428  break;
429  case Incidence::SecrecyConfidential:
430  secClass = ICAL_CLASS_CONFIDENTIAL;
431  break;
432  case Incidence::SecrecyPrivate:
433  default:
434  secClass = ICAL_CLASS_PRIVATE;
435  break;
436  }
437  if ( secClass != ICAL_CLASS_PUBLIC ) {
438  icalcomponent_add_property(parent,icalproperty_new_class(secClass));
439  }
440 
441  // priority
442  if ( incidence->priority() > 0 ) { // 0 is undefined priority
443  icalcomponent_add_property(parent,icalproperty_new_priority(
444  incidence->priority()));
445  }
446 
447  // categories
448  TQStringList categories = incidence->categories();
449  TQStringList::Iterator it;
450  for(it = categories.begin(); it != categories.end(); ++it ) {
451  icalcomponent_add_property(parent,icalproperty_new_categories((*it).utf8()));
452  }
453 
454  // related event
455  if ( !incidence->relatedToUid().isEmpty() ) {
456  icalcomponent_add_property(parent,icalproperty_new_relatedto(
457  incidence->relatedToUid().utf8()));
458  }
459 
460  // recurrenceid
461  if ( incidence->hasRecurrenceID() ) {
462  icalcomponent_add_property(parent, icalproperty_new_recurrenceid( writeICalDateTime( incidence->recurrenceID() ) ));
463  }
464 
465 // kdDebug(5800) << "Write recurrence for '" << incidence->summary() << "' (" << incidence->uid()
466 // << ")" << endl;
467 
468  RecurrenceRule::List rrules( incidence->recurrence()->rRules() );
469  RecurrenceRule::List::ConstIterator rit;
470  for ( rit = rrules.begin(); rit != rrules.end(); ++rit ) {
471  icalcomponent_add_property( parent, icalproperty_new_rrule(
472  writeRecurrenceRule( (*rit) ) ) );
473  }
474 
475  RecurrenceRule::List exrules( incidence->recurrence()->exRules() );
476  RecurrenceRule::List::ConstIterator exit;
477  for ( exit = exrules.begin(); exit != exrules.end(); ++exit ) {
478  icalcomponent_add_property( parent, icalproperty_new_rrule(
479  writeRecurrenceRule( (*exit) ) ) );
480  }
481 
482  DateList dateList = incidence->recurrence()->exDates();
483  DateList::ConstIterator exIt;
484  for(exIt = dateList.begin(); exIt != dateList.end(); ++exIt) {
485  icalcomponent_add_property(parent,icalproperty_new_exdate(
486  writeICalDate(*exIt)));
487  }
488  DateTimeList dateTimeList = incidence->recurrence()->exDateTimes();
489  DateTimeList::ConstIterator extIt;
490  for(extIt = dateTimeList.begin(); extIt != dateTimeList.end(); ++extIt) {
491  icalcomponent_add_property(parent,icalproperty_new_exdate(
492  writeICalDateTime(*extIt)));
493  }
494 
495 
496  dateList = incidence->recurrence()->rDates();
497  DateList::ConstIterator rdIt;
498  for( rdIt = dateList.begin(); rdIt != dateList.end(); ++rdIt) {
499  icalcomponent_add_property( parent, icalproperty_new_rdate(
500  writeICalDatePeriod(*rdIt) ) );
501  }
502  dateTimeList = incidence->recurrence()->rDateTimes();
503  DateTimeList::ConstIterator rdtIt;
504  for( rdtIt = dateTimeList.begin(); rdtIt != dateTimeList.end(); ++rdtIt) {
505  icalcomponent_add_property( parent, icalproperty_new_rdate(
506  writeICalDateTimePeriod(*rdtIt) ) );
507  }
508 
509  // attachments
510  Attachment::List attachments = incidence->attachments();
511  Attachment::List::ConstIterator atIt;
512  for ( atIt = attachments.begin(); atIt != attachments.end(); ++atIt ) {
513  icalcomponent_add_property( parent, writeAttachment( *atIt ) );
514  }
515 
516  // alarms
517  Alarm::List::ConstIterator alarmIt;
518  for ( alarmIt = incidence->alarms().begin();
519  alarmIt != incidence->alarms().end(); ++alarmIt ) {
520  if ( (*alarmIt)->enabled() ) {
521 // kdDebug(5800) << "Write alarm for " << incidence->summary() << endl;
522  icalcomponent_add_component( parent, writeAlarm( *alarmIt ) );
523  }
524  }
525 
526  // duration
527  if (incidence->hasDuration()) {
528  icaldurationtype duration;
529  duration = writeICalDuration( incidence->duration() );
530  icalcomponent_add_property(parent,icalproperty_new_duration(duration));
531  }
532 }
533 
534 void ICalFormatImpl::writeIncidenceBase( icalcomponent *parent,
535  IncidenceBase * incidenceBase )
536 {
537  icalcomponent_add_property( parent, icalproperty_new_dtstamp(
538  writeICalDateTime( TQDateTime::currentDateTime() ) ) );
539 
540  // organizer stuff
541  if ( !incidenceBase->organizer().isEmpty() ) {
542  icalcomponent_add_property( parent, writeOrganizer( incidenceBase->organizer() ) );
543  }
544 
545  // attendees
546  if ( incidenceBase->attendeeCount() > 0 ) {
547  Attendee::List::ConstIterator it;
548  for( it = incidenceBase->attendees().begin();
549  it != incidenceBase->attendees().end(); ++it ) {
550  icalcomponent_add_property( parent, writeAttendee( *it ) );
551  }
552  }
553 
554  // comments
555  TQStringList comments = incidenceBase->comments();
556  for (TQStringList::Iterator it=comments.begin(); it!=comments.end(); ++it) {
557  icalcomponent_add_property(parent, icalproperty_new_comment((*it).utf8()));
558  }
559 
560  // custom properties
561  writeCustomProperties( parent, incidenceBase );
562 }
563 
564 void ICalFormatImpl::writeCustomProperties(icalcomponent *parent,CustomProperties *properties)
565 {
566  TQMap<TQCString, TQString> custom = properties->customProperties();
567  for (TQMap<TQCString, TQString>::Iterator c = custom.begin(); c != custom.end(); ++c) {
568  icalproperty *p = icalproperty_new_x(c.data().utf8());
569  icalproperty_set_x_name(p,c.key());
570  icalcomponent_add_property(parent,p);
571  }
572 }
573 
574 icalproperty *ICalFormatImpl::writeOrganizer( const Person &organizer )
575 {
576  icalproperty *p = icalproperty_new_organizer("MAILTO:" + organizer.email().utf8());
577 
578  if (!organizer.name().isEmpty()) {
579  icalproperty_add_parameter( p, icalparameter_new_cn(quoteForParam(organizer.name()).utf8()) );
580  }
581  // TODO: Write dir, sent-by and language
582 
583  return p;
584 }
585 
586 
587 icalproperty *ICalFormatImpl::writeAttendee(Attendee *attendee)
588 {
589  icalproperty *p = icalproperty_new_attendee("mailto:" + attendee->email().utf8());
590 
591  if (!attendee->name().isEmpty()) {
592  icalproperty_add_parameter(p,icalparameter_new_cn(quoteForParam(attendee->name()).utf8()));
593  }
594 
595 
596  icalproperty_add_parameter(p,icalparameter_new_rsvp(
597  attendee->RSVP() ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE ));
598 
599  icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
600  switch (attendee->status()) {
601  default:
602  case Attendee::NeedsAction:
603  status = ICAL_PARTSTAT_NEEDSACTION;
604  break;
605  case Attendee::Accepted:
606  status = ICAL_PARTSTAT_ACCEPTED;
607  break;
608  case Attendee::Declined:
609  status = ICAL_PARTSTAT_DECLINED;
610  break;
611  case Attendee::Tentative:
612  status = ICAL_PARTSTAT_TENTATIVE;
613  break;
614  case Attendee::Delegated:
615  status = ICAL_PARTSTAT_DELEGATED;
616  break;
617  case Attendee::Completed:
618  status = ICAL_PARTSTAT_COMPLETED;
619  break;
620  case Attendee::InProcess:
621  status = ICAL_PARTSTAT_INPROCESS;
622  break;
623  }
624  icalproperty_add_parameter(p,icalparameter_new_partstat(status));
625 
626  icalparameter_role role = ICAL_ROLE_REQPARTICIPANT;
627  switch (attendee->role()) {
628  case Attendee::Chair:
629  role = ICAL_ROLE_CHAIR;
630  break;
631  default:
632  case Attendee::ReqParticipant:
633  role = ICAL_ROLE_REQPARTICIPANT;
634  break;
635  case Attendee::OptParticipant:
636  role = ICAL_ROLE_OPTPARTICIPANT;
637  break;
638  case Attendee::NonParticipant:
639  role = ICAL_ROLE_NONPARTICIPANT;
640  break;
641  }
642  icalproperty_add_parameter(p,icalparameter_new_role(role));
643 
644  if (!attendee->uid().isEmpty()) {
645  icalparameter* icalparameter_uid = icalparameter_new_x(attendee->uid().utf8());
646  icalparameter_set_xname(icalparameter_uid,"X-UID");
647  icalproperty_add_parameter(p,icalparameter_uid);
648  }
649 
650  if ( !attendee->delegate().isEmpty() ) {
651  icalparameter* icalparameter_delegate = icalparameter_new_delegatedto( attendee->delegate().utf8() );
652  icalproperty_add_parameter( p, icalparameter_delegate );
653  }
654 
655  if ( !attendee->delegator().isEmpty() ) {
656  icalparameter* icalparameter_delegator = icalparameter_new_delegatedfrom( attendee->delegator().utf8() );
657  icalproperty_add_parameter( p, icalparameter_delegator );
658  }
659 
660  return p;
661 }
662 
663 icalproperty *ICalFormatImpl::writeAttachment( Attachment *att )
664 {
665  icalattach *attach;
666  if ( att->isUri() ) {
667  attach = icalattach_new_from_url( att->uri().utf8().data() );
668  } else {
669 #ifdef USE_LIBICAL_0_46
670  attach = icalattach_new_from_data ( (const char *)att->data(), 0, 0 );
671 #else
672  attach = icalattach_new_from_data ( (unsigned char *)att->data(), 0, 0 );
673 #endif
674  }
675  icalproperty *p = icalproperty_new_attach( attach );
676 
677  if ( !att->mimeType().isEmpty() ) {
678  icalproperty_add_parameter( p,
679  icalparameter_new_fmttype( att->mimeType().utf8().data() ) );
680  }
681 
682  if ( att->isBinary() ) {
683  icalproperty_add_parameter( p,
684  icalparameter_new_value( ICAL_VALUE_BINARY ) );
685  icalproperty_add_parameter( p,
686  icalparameter_new_encoding( ICAL_ENCODING_BASE64 ) );
687  }
688 
689  if ( att->showInline() ) {
690  icalparameter* icalparameter_inline = icalparameter_new_x( "inline" );
691  icalparameter_set_xname( icalparameter_inline, "X-CONTENT-DISPOSITION" );
692  icalproperty_add_parameter( p, icalparameter_inline );
693  }
694 
695  if ( !att->label().isEmpty() ) {
696  icalparameter* icalparameter_label = icalparameter_new_x( att->label().utf8() );
697  icalparameter_set_xname( icalparameter_label, "X-LABEL" );
698  icalproperty_add_parameter( p, icalparameter_label );
699  }
700 
701  return p;
702 }
703 
704 icalrecurrencetype ICalFormatImpl::writeRecurrenceRule( RecurrenceRule *recur )
705 {
706 // kdDebug(5800) << "ICalFormatImpl::writeRecurrenceRule()" << endl;
707 
708  icalrecurrencetype r;
709  icalrecurrencetype_clear(&r);
710 
711  switch( recur->recurrenceType() ) {
712  case RecurrenceRule::rSecondly:
713  r.freq = ICAL_SECONDLY_RECURRENCE;
714  break;
715  case RecurrenceRule::rMinutely:
716  r.freq = ICAL_MINUTELY_RECURRENCE;
717  break;
718  case RecurrenceRule::rHourly:
719  r.freq = ICAL_HOURLY_RECURRENCE;
720  break;
721  case RecurrenceRule::rDaily:
722  r.freq = ICAL_DAILY_RECURRENCE;
723  break;
724  case RecurrenceRule::rWeekly:
725  r.freq = ICAL_WEEKLY_RECURRENCE;
726  break;
727  case RecurrenceRule::rMonthly:
728  r.freq = ICAL_MONTHLY_RECURRENCE;
729  break;
730  case RecurrenceRule::rYearly:
731  r.freq = ICAL_YEARLY_RECURRENCE;
732  break;
733  default:
734  r.freq = ICAL_NO_RECURRENCE;
735  kdDebug(5800) << "ICalFormatImpl::writeRecurrence(): no recurrence" << endl;
736  break;
737  }
738 
739  int index = 0;
740  TQValueList<int> bys;
741  TQValueList<int>::ConstIterator it;
742 
743  // Now write out the BY* parts:
744  bys = recur->bySeconds();
745  index = 0;
746  for ( it = bys.begin(); it != bys.end(); ++it ) {
747  r.by_second[index++] = *it;
748  }
749 
750  bys = recur->byMinutes();
751  index = 0;
752  for ( it = bys.begin(); it != bys.end(); ++it ) {
753  r.by_minute[index++] = *it;
754  }
755 
756  bys = recur->byHours();
757  index = 0;
758  for ( it = bys.begin(); it != bys.end(); ++it ) {
759  r.by_hour[index++] = *it;
760  }
761 
762  bys = recur->byMonthDays();
763  index = 0;
764  for ( it = bys.begin(); it != bys.end(); ++it ) {
765  r.by_month_day[index++] = icalrecurrencetype_day_position( (*it) * 8 );
766  }
767 
768  bys = recur->byYearDays();
769  index = 0;
770  for ( it = bys.begin(); it != bys.end(); ++it ) {
771  r.by_year_day[index++] = *it;
772  }
773 
774  bys = recur->byWeekNumbers();
775  index = 0;
776  for ( it = bys.begin(); it != bys.end(); ++it ) {
777  r.by_week_no[index++] = *it;
778  }
779 
780  bys = recur->byMonths();
781  index = 0;
782  for ( it = bys.begin(); it != bys.end(); ++it ) {
783  r.by_month[index++] = *it;
784  }
785 
786  bys = recur->bySetPos();
787  index = 0;
788  for ( it = bys.begin(); it != bys.end(); ++it ) {
789  r.by_set_pos[index++] = *it;
790  }
791 
792 
793  TQValueList<RecurrenceRule::WDayPos> byd = recur->byDays();
794  int day;
795  index = 0;
796  for ( TQValueList<RecurrenceRule::WDayPos>::ConstIterator dit = byd.begin();
797  dit != byd.end(); ++dit ) {
798  day = (*dit).day() % 7 + 1; // convert from Monday=1 to Sunday=1
799  if ( (*dit).pos() < 0 ) {
800  day += (-(*dit).pos())*8;
801  day = -day;
802  } else {
803  day += (*dit).pos()*8;
804  }
805  r.by_day[index++] = day;
806  }
807 
808  r.week_start = static_cast<icalrecurrencetype_weekday>(
809  recur->weekStart()%7 + 1);
810 
811  if ( recur->frequency() > 1 ) {
812  // Dont' write out INTERVAL=1, because that's the default anyway
813  r.interval = recur->frequency();
814  }
815 
816  if ( recur->duration() > 0 ) {
817  r.count = recur->duration();
818  } else if ( recur->duration() == -1 ) {
819  r.count = 0;
820  } else {
821  if ( recur->doesFloat() )
822  r.until = writeICalDate(recur->endDt().date());
823  else
824  r.until = writeICalDateTime(recur->endDt());
825  }
826 
827 // Debug output
828 #if 0
829  const char *str = icalrecurrencetype_as_string(&r);
830  if (str) {
831  kdDebug(5800) << " String: " << str << endl;
832  } else {
833  kdDebug(5800) << " No String" << endl;
834  }
835 #endif
836 
837  return r;
838 }
839 
840 
841 icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm)
842 {
843 // kdDebug(5800) << " ICalFormatImpl::writeAlarm" << endl;
844  icalcomponent *a = icalcomponent_new(ICAL_VALARM_COMPONENT);
845 
846  icalproperty_action action;
847  icalattach *attach = 0;
848 
849  switch (alarm->type()) {
850  case Alarm::Procedure:
851  action = ICAL_ACTION_PROCEDURE;
852  attach = icalattach_new_from_url(TQFile::encodeName(alarm->programFile()).data());
853  icalcomponent_add_property(a,icalproperty_new_attach(attach));
854  if (!alarm->programArguments().isEmpty()) {
855  icalcomponent_add_property(a,icalproperty_new_description(alarm->programArguments().utf8()));
856  }
857  break;
858  case Alarm::Audio:
859  action = ICAL_ACTION_AUDIO;
860 // kdDebug(5800) << " It's an audio action, file: " << alarm->audioFile() << endl;
861  if (!alarm->audioFile().isEmpty()) {
862  attach = icalattach_new_from_url(TQFile::encodeName( alarm->audioFile() ).data());
863  icalcomponent_add_property(a,icalproperty_new_attach(attach));
864  }
865  break;
866  case Alarm::Email: {
867  action = ICAL_ACTION_EMAIL;
868  TQValueList<Person> addresses = alarm->mailAddresses();
869  for (TQValueList<Person>::Iterator ad = addresses.begin(); ad != addresses.end(); ++ad) {
870  icalproperty *p = icalproperty_new_attendee("MAILTO:" + (*ad).email().utf8());
871  if (!(*ad).name().isEmpty()) {
872  icalproperty_add_parameter(p,icalparameter_new_cn(quoteForParam((*ad).name()).utf8()));
873  }
874  icalcomponent_add_property(a,p);
875  }
876  icalcomponent_add_property(a,icalproperty_new_summary(alarm->mailSubject().utf8()));
877  icalcomponent_add_property(a,icalproperty_new_description(alarm->mailText().utf8()));
878  TQStringList attachments = alarm->mailAttachments();
879  if (attachments.count() > 0) {
880  for (TQStringList::Iterator at = attachments.begin(); at != attachments.end(); ++at) {
881  attach = icalattach_new_from_url(TQFile::encodeName( *at ).data());
882  icalcomponent_add_property(a,icalproperty_new_attach(attach));
883  }
884  }
885  break;
886  }
887  case Alarm::Display:
888  action = ICAL_ACTION_DISPLAY;
889  icalcomponent_add_property(a,icalproperty_new_description(alarm->text().utf8()));
890  break;
891  case Alarm::Invalid:
892  default:
893  kdDebug(5800) << "Unknown type of alarm" << endl;
894  action = ICAL_ACTION_NONE;
895  break;
896  }
897  icalcomponent_add_property(a,icalproperty_new_action(action));
898 
899  // Trigger time
900  icaltriggertype trigger;
901  if ( alarm->hasTime() ) {
902  trigger.time = writeICalDateTime(alarm->time());
903  trigger.duration = icaldurationtype_null_duration();
904  } else {
905  trigger.time = icaltime_null_time();
906  Duration offset;
907  if ( alarm->hasStartOffset() )
908  offset = alarm->startOffset();
909  else
910  offset = alarm->endOffset();
911  trigger.duration = writeICalDuration( offset.asSeconds() );
912  }
913  icalproperty *p = icalproperty_new_trigger(trigger);
914  if ( alarm->hasEndOffset() )
915  icalproperty_add_parameter(p,icalparameter_new_related(ICAL_RELATED_END));
916  icalcomponent_add_property(a,p);
917 
918  // Repeat count and duration
919  if (alarm->repeatCount()) {
920  icalcomponent_add_property(a,icalproperty_new_repeat(alarm->repeatCount()));
921  icalcomponent_add_property(a,icalproperty_new_duration(
922  writeICalDuration(alarm->snoozeTime().value())));
923  }
924 
925  // Custom properties
926  TQMap<TQCString, TQString> custom = alarm->customProperties();
927  for (TQMap<TQCString, TQString>::Iterator c = custom.begin(); c != custom.end(); ++c) {
928  icalproperty *p = icalproperty_new_x(c.data().utf8());
929  icalproperty_set_x_name(p,c.key());
930  icalcomponent_add_property(a,p);
931  }
932 
933  return a;
934 }
935 
936 Todo *ICalFormatImpl::readTodo(icalcomponent *vtodo)
937 {
938  Todo *todo = new Todo;
939 
940  readIncidence(vtodo, 0, todo); // FIXME timezone
941 
942  icalproperty *p = icalcomponent_get_first_property(vtodo,ICAL_ANY_PROPERTY);
943 
944 // int intvalue;
945  icaltimetype icaltime;
946 
947  TQStringList categories;
948 
949  while (p) {
950  icalproperty_kind kind = icalproperty_isa(p);
951  switch (kind) {
952 
953  case ICAL_DUE_PROPERTY: // due date
954  icaltime = icalproperty_get_due(p);
955  if (icaltime.is_date) {
956  todo->setDtDue(TQDateTime(readICalDate(icaltime),TQTime(0,0,0)),true);
957  } else {
958  todo->setDtDue(readICalDateTime(p, icaltime),true);
959  todo->setFloats(false);
960  }
961  todo->setHasDueDate(true);
962  break;
963 
964  case ICAL_COMPLETED_PROPERTY: // completion date
965  icaltime = icalproperty_get_completed(p);
966  todo->setCompleted(readICalDateTime(p, icaltime));
967  break;
968 
969  case ICAL_PERCENTCOMPLETE_PROPERTY: // Percent completed
970  todo->setPercentComplete(icalproperty_get_percentcomplete(p));
971  break;
972 
973  case ICAL_RELATEDTO_PROPERTY: // related todo (parent)
974  todo->setRelatedToUid(TQString::fromUtf8(icalproperty_get_relatedto(p)));
975  mTodosRelate.append(todo);
976  break;
977 
978  case ICAL_DTSTART_PROPERTY: {
979  // Flag that todo has start date. Value is read in by readIncidence().
980  if ( todo->comments().grep("NoStartDate").count() )
981  todo->setHasStartDate( false );
982  else
983  todo->setHasStartDate( true );
984  break;
985  }
986 
987  case ICAL_RECURRENCEID_PROPERTY:
988  icaltime = icalproperty_get_recurrenceid(p);
989  todo->setDtRecurrence( readICalDateTime(p, icaltime) );
990  break;
991 
992  default:
993 // kdDebug(5800) << "ICALFormat::readTodo(): Unknown property: " << kind
994 // << endl;
995  break;
996  }
997 
998  p = icalcomponent_get_next_property(vtodo,ICAL_ANY_PROPERTY);
999  }
1000 
1001  if (mCompat) mCompat->fixEmptySummary( todo );
1002 
1003  return todo;
1004 }
1005 
1006 Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezone )
1007 {
1008  Event *event = new Event;
1009 
1010  // FIXME where is this freed?
1011  icaltimezone *tz = icaltimezone_new();
1012  if ( !icaltimezone_set_component( tz, vtimezone ) ) {
1013  icaltimezone_free( tz, 1 );
1014  tz = 0;
1015  }
1016 
1017  readIncidence( vevent, tz, event);
1018 
1019  icalproperty *p = icalcomponent_get_first_property( vevent, ICAL_ANY_PROPERTY );
1020 
1021  // int intvalue;
1022  icaltimetype icaltime;
1023 
1024  TQStringList categories;
1025  icalproperty_transp transparency;
1026 
1027  bool dtEndProcessed = false;
1028 
1029  while ( p ) {
1030  icalproperty_kind kind = icalproperty_isa( p );
1031  switch ( kind ) {
1032 
1033  case ICAL_DTEND_PROPERTY: // start date and time
1034  icaltime = icalproperty_get_dtend( p );
1035  if ( icaltime.is_date ) {
1036  // End date is non-inclusive
1037  TQDate endDate = readICalDate( icaltime ).addDays( -1 );
1038  if ( mCompat ) {
1039  mCompat->fixFloatingEnd( endDate );
1040  }
1041 
1042  if ( endDate < event->dtStart().date() ) {
1043  endDate = event->dtStart().date();
1044  }
1045  event->setDtEnd( TQDateTime( endDate, TQTime( 0, 0, 0 ) ) );
1046  } else {
1047  event->setDtEnd(readICalDateTime(p, icaltime, tz));
1048  event->setFloats( false );
1049  }
1050  dtEndProcessed = true;
1051  break;
1052 
1053  case ICAL_RELATEDTO_PROPERTY: // related event (parent)
1054  event->setRelatedToUid( TQString::fromUtf8( icalproperty_get_relatedto( p ) ) );
1055  mEventsRelate.append( event );
1056  break;
1057 
1058  case ICAL_TRANSP_PROPERTY: // Transparency
1059  transparency = icalproperty_get_transp( p );
1060  if ( transparency == ICAL_TRANSP_TRANSPARENT ) {
1061  event->setTransparency( Event::Transparent );
1062  } else {
1063  event->setTransparency( Event::Opaque );
1064  }
1065  break;
1066 
1067  default:
1068  // kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind
1069  // << endl;
1070  break;
1071  }
1072 
1073  p = icalcomponent_get_next_property( vevent, ICAL_ANY_PROPERTY );
1074  }
1075 
1076  // according to rfc2445 the dtend shouldn't be written when it equals
1077  // start date. so assign one equal to start date.
1078  if ( !dtEndProcessed && !event->hasDuration() ) {
1079  event->setDtEnd( event->dtStart() );
1080  }
1081 
1082  const TQString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT");
1083  if ( !msade.isEmpty() ) {
1084  const bool floats = ( msade == TQString::fromLatin1("TRUE") );
1085  event->setFloats(floats);
1086  }
1087 
1088  if ( mCompat ) {
1089  mCompat->fixEmptySummary( event );
1090  }
1091 
1092  return event;
1093 }
1094 
1095 FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy)
1096 {
1097  FreeBusy *freebusy = new FreeBusy;
1098 
1099  readIncidenceBase(vfreebusy, freebusy);
1100 
1101  icalproperty *p = icalcomponent_get_first_property(vfreebusy,ICAL_ANY_PROPERTY);
1102 
1103  icaltimetype icaltime;
1104  PeriodList periods;
1105 
1106  while (p) {
1107  icalproperty_kind kind = icalproperty_isa(p);
1108  switch (kind) {
1109 
1110  case ICAL_DTSTART_PROPERTY: // start date and time
1111  icaltime = icalproperty_get_dtstart(p);
1112  freebusy->setDtStart(readICalDateTime(p, icaltime));
1113  break;
1114 
1115  case ICAL_DTEND_PROPERTY: // end Date and Time
1116  icaltime = icalproperty_get_dtend(p);
1117  freebusy->setDtEnd(readICalDateTime(p, icaltime));
1118  break;
1119 
1120  case ICAL_FREEBUSY_PROPERTY: //Any FreeBusy Times
1121  {
1122  icalperiodtype icalperiod = icalproperty_get_freebusy(p);
1123  TQDateTime period_start = readICalDateTime(p, icalperiod.start);
1124  Period period;
1125  if ( !icaltime_is_null_time(icalperiod.end) ) {
1126  TQDateTime period_end = readICalDateTime(p, icalperiod.end);
1127  period = Period(period_start, period_end);
1128  } else {
1129  Duration duration = readICalDuration( icalperiod.duration );
1130  period = Period(period_start, duration);
1131  }
1132  icalparameter *param = icalproperty_get_first_parameter( p, ICAL_X_PARAMETER );
1133  while ( param ) {
1134  if ( strncmp( icalparameter_get_xname( param ), "X-SUMMARY", 9 ) == 0 ) {
1135  period.setSummary( TQString::fromUtf8(
1136  KCodecs::base64Decode( TQCString( icalparameter_get_xvalue( param ) ) ) ) );
1137  }
1138  if ( strncmp( icalparameter_get_xname( param ), "X-LOCATION", 10 ) == 0 ) {
1139  period.setLocation( TQString::fromUtf8(
1140  KCodecs::base64Decode( TQCString( icalparameter_get_xvalue( param ) ) ) ) );
1141  }
1142  param = icalproperty_get_next_parameter( p, ICAL_X_PARAMETER );
1143  }
1144  periods.append( period );
1145  break;
1146  }
1147 
1148  default:
1149 // kdDebug(5800) << "ICalFormatImpl::readFreeBusy(): Unknown property: "
1150 // << kind << endl;
1151  break;
1152  }
1153  p = icalcomponent_get_next_property(vfreebusy,ICAL_ANY_PROPERTY);
1154  }
1155  freebusy->addPeriods( periods );
1156 
1157  return freebusy;
1158 }
1159 
1160 Journal *ICalFormatImpl::readJournal(icalcomponent *vjournal)
1161 {
1162  Journal *journal = new Journal;
1163 
1164  readIncidence(vjournal, 0, journal); // FIXME tz?
1165 
1166  return journal;
1167 }
1168 
1169 Attendee *ICalFormatImpl::readAttendee(icalproperty *attendee)
1170 {
1171  icalparameter *p = 0;
1172 
1173  TQString email = TQString::fromUtf8(icalproperty_get_attendee(attendee));
1174  if ( email.startsWith( "mailto:", false ) ) {
1175  email = email.mid( 7 );
1176  }
1177 
1178  TQString name;
1179  TQString uid = TQString();
1180  p = icalproperty_get_first_parameter(attendee,ICAL_CN_PARAMETER);
1181  if (p) {
1182  name = TQString::fromUtf8(icalparameter_get_cn(p));
1183  } else {
1184  }
1185 
1186  bool rsvp=false;
1187  p = icalproperty_get_first_parameter(attendee,ICAL_RSVP_PARAMETER);
1188  if (p) {
1189  icalparameter_rsvp rsvpParameter = icalparameter_get_rsvp(p);
1190  if (rsvpParameter == ICAL_RSVP_TRUE) rsvp = true;
1191  }
1192 
1193  Attendee::PartStat status = Attendee::NeedsAction;
1194  p = icalproperty_get_first_parameter(attendee,ICAL_PARTSTAT_PARAMETER);
1195  if (p) {
1196  icalparameter_partstat partStatParameter = icalparameter_get_partstat(p);
1197  switch(partStatParameter) {
1198  default:
1199  case ICAL_PARTSTAT_NEEDSACTION:
1200  status = Attendee::NeedsAction;
1201  break;
1202  case ICAL_PARTSTAT_ACCEPTED:
1203  status = Attendee::Accepted;
1204  break;
1205  case ICAL_PARTSTAT_DECLINED:
1206  status = Attendee::Declined;
1207  break;
1208  case ICAL_PARTSTAT_TENTATIVE:
1209  status = Attendee::Tentative;
1210  break;
1211  case ICAL_PARTSTAT_DELEGATED:
1212  status = Attendee::Delegated;
1213  break;
1214  case ICAL_PARTSTAT_COMPLETED:
1215  status = Attendee::Completed;
1216  break;
1217  case ICAL_PARTSTAT_INPROCESS:
1218  status = Attendee::InProcess;
1219  break;
1220  }
1221  }
1222 
1223  Attendee::Role role = Attendee::ReqParticipant;
1224  p = icalproperty_get_first_parameter(attendee,ICAL_ROLE_PARAMETER);
1225  if (p) {
1226  icalparameter_role roleParameter = icalparameter_get_role(p);
1227  switch(roleParameter) {
1228  case ICAL_ROLE_CHAIR:
1229  role = Attendee::Chair;
1230  break;
1231  default:
1232  case ICAL_ROLE_REQPARTICIPANT:
1233  role = Attendee::ReqParticipant;
1234  break;
1235  case ICAL_ROLE_OPTPARTICIPANT:
1236  role = Attendee::OptParticipant;
1237  break;
1238  case ICAL_ROLE_NONPARTICIPANT:
1239  role = Attendee::NonParticipant;
1240  break;
1241  }
1242  }
1243 
1244  p = icalproperty_get_first_parameter(attendee,ICAL_X_PARAMETER);
1245  uid = icalparameter_get_xvalue(p);
1246  // This should be added, but there seems to be a libical bug here.
1247  // TODO: does this work now in libical-0.24 or greater?
1248  /*while (p) {
1249  // if (icalparameter_get_xname(p) == "X-UID") {
1250  uid = icalparameter_get_xvalue(p);
1251  p = icalproperty_get_next_parameter(attendee,ICAL_X_PARAMETER);
1252  } */
1253 
1254  Attendee *a = new Attendee( name, email, rsvp, status, role, uid );
1255 
1256  p = icalproperty_get_first_parameter( attendee, ICAL_DELEGATEDTO_PARAMETER );
1257  if ( p )
1258  a->setDelegate( icalparameter_get_delegatedto( p ) );
1259 
1260  p = icalproperty_get_first_parameter( attendee, ICAL_DELEGATEDFROM_PARAMETER );
1261  if ( p )
1262  a->setDelegator( icalparameter_get_delegatedfrom( p ) );
1263 
1264  return a;
1265 }
1266 
1267 Person ICalFormatImpl::readOrganizer( icalproperty *organizer )
1268 {
1269  TQString email = TQString::fromUtf8(icalproperty_get_organizer(organizer));
1270  if ( email.startsWith( "mailto:", false ) ) {
1271  email = email.mid( 7 );
1272  }
1273  TQString cn;
1274 
1275  icalparameter *p = icalproperty_get_first_parameter(
1276  organizer, ICAL_CN_PARAMETER );
1277 
1278  if ( p ) {
1279  cn = TQString::fromUtf8( icalparameter_get_cn( p ) );
1280  }
1281  Person org( cn, email );
1282  // TODO: Treat sent-by, dir and language here, too
1283  return org;
1284 }
1285 
1286 Attachment *ICalFormatImpl::readAttachment(icalproperty *attach)
1287 {
1288  Attachment *attachment = 0;
1289 
1290  const char *p;
1291  icalvalue *value = icalproperty_get_value( attach );
1292 
1293  switch( icalvalue_isa( value ) ) {
1294  case ICAL_ATTACH_VALUE:
1295  {
1296  icalattach *a = icalproperty_get_attach( attach );
1297  if ( !icalattach_get_is_url( a ) ) {
1298  p = (const char *)icalattach_get_data( a );
1299  if ( p ) {
1300  attachment = new Attachment( p );
1301  }
1302  } else {
1303  p = icalattach_get_url( a );
1304  if ( p ) {
1305  attachment = new Attachment( TQString::fromUtf8( p ) );
1306  }
1307  }
1308  break;
1309  }
1310  case ICAL_BINARY_VALUE:
1311  {
1312  icalattach *a = icalproperty_get_attach( attach );
1313  p = (const char *)icalattach_get_data( a );
1314  if ( p ) {
1315  attachment = new Attachment( p );
1316  }
1317  break;
1318  }
1319  case ICAL_URI_VALUE:
1320  p = icalvalue_get_uri( value );
1321  attachment = new Attachment( TQString::fromUtf8( p ) );
1322  break;
1323  default:
1324  break;
1325  }
1326 
1327  if ( attachment ) {
1328  icalparameter *p =
1329  icalproperty_get_first_parameter( attach, ICAL_FMTTYPE_PARAMETER );
1330  if ( p ) {
1331  attachment->setMimeType( TQString( icalparameter_get_fmttype( p ) ) );
1332  }
1333 
1334  p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER );
1335  while ( p ) {
1336  TQString xname = TQString( icalparameter_get_xname( p ) ).upper();
1337  TQString xvalue = TQString::fromUtf8( icalparameter_get_xvalue( p ) );
1338  if ( xname == "X-CONTENT-DISPOSITION" ) {
1339  attachment->setShowInline( xvalue.lower() == "inline" );
1340  }
1341  if ( xname == "X-LABEL" ) {
1342  attachment->setLabel( xvalue );
1343  }
1344  p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER );
1345  }
1346 
1347  p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER );
1348  while ( p ) {
1349  if ( strncmp( icalparameter_get_xname( p ), "X-LABEL", 7 ) == 0 ) {
1350  attachment->setLabel( TQString::fromUtf8( icalparameter_get_xvalue( p ) ) );
1351  }
1352  p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER );
1353  }
1354  }
1355 
1356  return attachment;
1357 }
1358 
1359 void ICalFormatImpl::readIncidence(icalcomponent *parent, icaltimezone *tz, Incidence *incidence)
1360 {
1361  readIncidenceBase(parent,incidence);
1362 
1363  icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY);
1364 
1365  const char *text;
1366  int intvalue, inttext;
1367  icaltimetype icaltime;
1368  icaldurationtype icalduration;
1369 
1370  TQStringList categories;
1371 
1372  while (p) {
1373  icalproperty_kind kind = icalproperty_isa(p);
1374  switch (kind) {
1375 
1376  case ICAL_CREATED_PROPERTY:
1377  icaltime = icalproperty_get_created(p);
1378  incidence->setCreated(readICalDateTime(p, icaltime, tz));
1379  break;
1380 
1381  case ICAL_SEQUENCE_PROPERTY: // sequence
1382  intvalue = icalproperty_get_sequence(p);
1383  incidence->setRevision(intvalue);
1384  break;
1385 
1386  case ICAL_LASTMODIFIED_PROPERTY: // last modification date
1387  icaltime = icalproperty_get_lastmodified(p);
1388  incidence->setLastModified(readICalDateTime(p, icaltime, tz));
1389  break;
1390 
1391  case ICAL_DTSTART_PROPERTY: // start date and time
1392  icaltime = icalproperty_get_dtstart(p);
1393  if (icaltime.is_date) {
1394  incidence->setDtStart(TQDateTime(readICalDate(icaltime),TQTime(0,0,0)));
1395  incidence->setFloats( true );
1396  } else {
1397  incidence->setDtStart(readICalDateTime(p, icaltime, tz));
1398  incidence->setFloats( false );
1399  }
1400  break;
1401 
1402  case ICAL_DURATION_PROPERTY: // start date and time
1403  icalduration = icalproperty_get_duration(p);
1404  incidence->setDuration(readICalDuration(icalduration));
1405  break;
1406 
1407  case ICAL_DESCRIPTION_PROPERTY: // description
1408  text = icalproperty_get_description(p);
1409  incidence->setDescription(TQString::fromUtf8(text));
1410  break;
1411 
1412  case ICAL_SUMMARY_PROPERTY: // summary
1413  text = icalproperty_get_summary(p);
1414  incidence->setSummary(TQString::fromUtf8(text));
1415  break;
1416 
1417  case ICAL_LOCATION_PROPERTY: // location
1418  text = icalproperty_get_location(p);
1419  incidence->setLocation(TQString::fromUtf8(text));
1420  break;
1421 
1422  case ICAL_STATUS_PROPERTY: { // status
1423  Incidence::Status stat;
1424  switch (icalproperty_get_status(p)) {
1425  case ICAL_STATUS_TENTATIVE: stat = Incidence::StatusTentative; break;
1426  case ICAL_STATUS_CONFIRMED: stat = Incidence::StatusConfirmed; break;
1427  case ICAL_STATUS_COMPLETED: stat = Incidence::StatusCompleted; break;
1428  case ICAL_STATUS_NEEDSACTION: stat = Incidence::StatusNeedsAction; break;
1429  case ICAL_STATUS_CANCELLED: stat = Incidence::StatusCanceled; break;
1430  case ICAL_STATUS_INPROCESS: stat = Incidence::StatusInProcess; break;
1431  case ICAL_STATUS_DRAFT: stat = Incidence::StatusDraft; break;
1432  case ICAL_STATUS_FINAL: stat = Incidence::StatusFinal; break;
1433  case ICAL_STATUS_X:
1434  incidence->setCustomStatus(TQString::fromUtf8(icalvalue_get_x(icalproperty_get_value(p))));
1435  stat = Incidence::StatusX;
1436  break;
1437  case ICAL_STATUS_NONE:
1438  default: stat = Incidence::StatusNone; break;
1439  }
1440  if (stat != Incidence::StatusX)
1441  incidence->setStatus(stat);
1442  break;
1443  }
1444 
1445  case ICAL_PRIORITY_PROPERTY: // priority
1446  intvalue = icalproperty_get_priority( p );
1447  if ( mCompat )
1448  intvalue = mCompat->fixPriority( intvalue );
1449  incidence->setPriority( intvalue );
1450  break;
1451 
1452  case ICAL_CATEGORIES_PROPERTY: // categories
1453  text = icalproperty_get_categories(p);
1454  categories.append(TQString::fromUtf8(text));
1455  break;
1456 
1457  case ICAL_RECURRENCEID_PROPERTY: // recurrenceID
1458  icaltime = icalproperty_get_recurrenceid(p);
1459  incidence->setRecurrenceID( readICalDateTime( p, icaltime ) );
1460  incidence->setHasRecurrenceID( true );
1461  break;
1462 
1463  case ICAL_RRULE_PROPERTY:
1464  readRecurrenceRule( p, incidence );
1465  break;
1466 
1467 // case ICAL_CONTACT_PROPERTY:
1468 // incidenceBase->addContact(
1469 // TQString::fromUtf8( icalproperty_get_contact( p ) ) );
1470 // break;
1471 
1472  case ICAL_RDATE_PROPERTY: {
1473  icaldatetimeperiodtype rd = icalproperty_get_rdate( p );
1474  if ( icaltime_is_valid_time( rd.time ) ) {
1475  if ( icaltime_is_date( rd.time ) ) {
1476  incidence->recurrence()->addRDate( readICalDate( rd.time ) );
1477  } else {
1478  incidence->recurrence()->addRDateTime( readICalDateTime(p, rd.time, tz ) );
1479  }
1480  } else {
1481  // TODO: RDates as period are not yet implemented!
1482  }
1483  break; }
1484 
1485  case ICAL_EXRULE_PROPERTY:
1486  readExceptionRule( p, incidence );
1487  break;
1488 
1489  case ICAL_EXDATE_PROPERTY:
1490  icaltime = icalproperty_get_exdate(p);
1491  if ( icaltime_is_date(icaltime) ) {
1492  incidence->recurrence()->addExDate( readICalDate(icaltime) );
1493  } else {
1494  incidence->recurrence()->addExDateTime( readICalDateTime(p, icaltime, tz) );
1495  }
1496  break;
1497 
1498  case ICAL_CLASS_PROPERTY:
1499  inttext = icalproperty_get_class(p);
1500  if (inttext == ICAL_CLASS_PUBLIC ) {
1501  incidence->setSecrecy(Incidence::SecrecyPublic);
1502  } else if (inttext == ICAL_CLASS_CONFIDENTIAL ) {
1503  incidence->setSecrecy(Incidence::SecrecyConfidential);
1504  } else {
1505  incidence->setSecrecy(Incidence::SecrecyPrivate);
1506  }
1507  break;
1508 
1509  case ICAL_ATTACH_PROPERTY: // attachments
1510  incidence->addAttachment(readAttachment(p));
1511  break;
1512 
1513  default:
1514 // kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind
1515 // << endl;
1516  break;
1517  }
1518 
1519  p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY);
1520  }
1521 
1522  // Set the scheduling ID
1523  const TQString uid = incidence->customProperty( "LIBKCAL", "ID" );
1524  if ( !uid.isNull() ) {
1525  // The UID stored in incidencebase is actually the scheduling ID
1526  // It has to be stored in the iCal UID component for compatibility
1527  // with other iCal applications
1528  incidence->setSchedulingID( incidence->uid() );
1529  incidence->setUid( uid );
1530  }
1531 
1532  // Now that recurrence and exception stuff is completely set up,
1533  // do any backwards compatibility adjustments.
1534  if ( incidence->doesRecur() && mCompat )
1535  mCompat->fixRecurrence( incidence );
1536 
1537  // add categories
1538  incidence->setCategories(categories);
1539 
1540  // iterate through all alarms
1541  for (icalcomponent *alarm = icalcomponent_get_first_component(parent,ICAL_VALARM_COMPONENT);
1542  alarm;
1543  alarm = icalcomponent_get_next_component(parent,ICAL_VALARM_COMPONENT)) {
1544  readAlarm(alarm,incidence);
1545  }
1546  // Fix incorrect alarm settings by other applications (like outloook 9)
1547  if ( mCompat ) mCompat->fixAlarms( incidence );
1548 
1549 }
1550 
1551 void ICalFormatImpl::readIncidenceBase(icalcomponent *parent,IncidenceBase *incidenceBase)
1552 {
1553  icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY);
1554 
1555  bool uidProcessed = false;
1556 
1557  while ( p ) {
1558  icalproperty_kind kind = icalproperty_isa( p );
1559  switch (kind) {
1560 
1561  case ICAL_UID_PROPERTY: // unique id
1562  uidProcessed = true;
1563  incidenceBase->setUid( TQString::fromUtf8(icalproperty_get_uid( p ) ) );
1564  break;
1565 
1566  case ICAL_ORGANIZER_PROPERTY: // organizer
1567  incidenceBase->setOrganizer( readOrganizer( p ) );
1568  break;
1569 
1570  case ICAL_ATTENDEE_PROPERTY: // attendee
1571  incidenceBase->addAttendee( readAttendee( p ) );
1572  break;
1573 
1574  case ICAL_COMMENT_PROPERTY:
1575  incidenceBase->addComment(
1576  TQString::fromUtf8( icalproperty_get_comment( p ) ) );
1577  break;
1578 
1579  default:
1580  break;
1581  }
1582 
1583  p = icalcomponent_get_next_property( parent, ICAL_ANY_PROPERTY );
1584  }
1585 
1586  if ( !uidProcessed ) {
1587  kdWarning() << "The incidence didn't have any UID! Report a bug "
1588  << "to the application that generated this file."
1589  << endl;
1590 
1591  // Our in-memory incidence has a random uid generated in Event's ctor.
1592  // Make it empty so it matches what's in the file:
1593  incidenceBase->setUid( TQString() );
1594 
1595  // Otherwise, next time we read the file, this function will return
1596  // an event with another random uid and we will have two events in the calendar.
1597  }
1598 
1599  // kpilot stuff
1600  // TODO: move this application-specific code to kpilot
1601  // need to get X-PILOT* attributes out, set correct properties, and get
1602  // rid of them...
1603  // Pointer fun, as per libical documentation
1604  // (documented in UsingLibical.txt)
1605  icalproperty *next =0;
1606 
1607  for ( p = icalcomponent_get_first_property(parent,ICAL_X_PROPERTY);
1608  p != 0;
1609  p = next )
1610  {
1611 
1612  next = icalcomponent_get_next_property(parent,ICAL_X_PROPERTY);
1613 
1614  TQString value = TQString::fromUtf8(icalproperty_get_x(p));
1615  TQString name = icalproperty_get_x_name(p);
1616 
1617  if (name == "X-PILOTID" && !value.isEmpty()) {
1618  incidenceBase->setPilotId(value.toInt());
1619  icalcomponent_remove_property(parent,p);
1620  } else if (name == "X-PILOTSTAT" && !value.isEmpty()) {
1621  incidenceBase->setSyncStatus(value.toInt());
1622  icalcomponent_remove_property(parent,p);
1623  }
1624  }
1625 
1626  // custom properties
1627  readCustomProperties(parent, incidenceBase);
1628 }
1629 
1630 void ICalFormatImpl::readCustomProperties(icalcomponent *parent,CustomProperties *properties)
1631 {
1632  TQMap<TQCString, TQString> customProperties;
1633  TQString lastProperty;
1634 
1635  icalproperty *p = icalcomponent_get_first_property(parent,ICAL_X_PROPERTY);
1636 
1637  while (p) {
1638 
1639  TQString value = TQString::fromUtf8(icalproperty_get_x(p));
1640  const char *name = icalproperty_get_x_name(p);
1641  if ( lastProperty != name ) {
1642  customProperties[name] = value;
1643  } else {
1644  customProperties[name] = customProperties[name].append( "," ).append( value );
1645  }
1646  // kdDebug(5800) << "Set custom property [" << name << '=' << value << ']' << endl;
1647  p = icalcomponent_get_next_property(parent,ICAL_X_PROPERTY);
1648  lastProperty = name;
1649  }
1650 
1651  properties->setCustomProperties(customProperties);
1652 }
1653 
1654 
1655 
1656 void ICalFormatImpl::readRecurrenceRule(icalproperty *rrule,Incidence *incidence )
1657 {
1658 // kdDebug(5800) << "Read recurrence for " << incidence->summary() << endl;
1659 
1660  Recurrence *recur = incidence->recurrence();
1661 
1662  struct icalrecurrencetype r = icalproperty_get_rrule(rrule);
1663 // dumpIcalRecurrence(r);
1664 
1665  RecurrenceRule *recurrule = new RecurrenceRule( /*incidence*/ );
1666  recurrule->setStartDt( incidence->dtStart() );
1667  readRecurrence( r, recurrule );
1668  recur->addRRule( recurrule );
1669 }
1670 
1671 void ICalFormatImpl::readExceptionRule( icalproperty *rrule, Incidence *incidence )
1672 {
1673 // kdDebug(5800) << "Read recurrence for " << incidence->summary() << endl;
1674 
1675  struct icalrecurrencetype r = icalproperty_get_exrule(rrule);
1676 // dumpIcalRecurrence(r);
1677 
1678  RecurrenceRule *recurrule = new RecurrenceRule( /*incidence*/ );
1679  recurrule->setStartDt( incidence->dtStart() );
1680  readRecurrence( r, recurrule );
1681 
1682  Recurrence *recur = incidence->recurrence();
1683  recur->addExRule( recurrule );
1684 }
1685 
1686 void ICalFormatImpl::readRecurrence( const struct icalrecurrencetype &r, RecurrenceRule* recur )
1687 {
1688  // Generate the RRULE string
1689  recur->mRRule = TQString( icalrecurrencetype_as_string( const_cast<struct icalrecurrencetype*>(&r) ) );
1690  // Period
1691  switch ( r.freq ) {
1692  case ICAL_SECONDLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rSecondly ); break;
1693  case ICAL_MINUTELY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rMinutely ); break;
1694  case ICAL_HOURLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rHourly ); break;
1695  case ICAL_DAILY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rDaily ); break;
1696  case ICAL_WEEKLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rWeekly ); break;
1697  case ICAL_MONTHLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rMonthly ); break;
1698  case ICAL_YEARLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rYearly ); break;
1699  case ICAL_NO_RECURRENCE:
1700  default:
1701  recur->setRecurrenceType( RecurrenceRule::rNone );
1702  }
1703  // Frequency
1704  recur->setFrequency( r.interval );
1705 
1706  // Duration & End Date
1707  if ( !icaltime_is_null_time( r.until ) ) {
1708  icaltimetype t;
1709  t = r.until;
1710  // Convert to the correct time zone! it's in UTC by specification.
1711  TQDateTime endDate( readICalDateTime(0, t) );
1712  recur->setEndDt( endDate );
1713  } else {
1714  if (r.count == 0)
1715  recur->setDuration( -1 );
1716  else
1717  recur->setDuration( r.count );
1718  }
1719 
1720  // Week start setting
1721  int wkst = (r.week_start + 5)%7 + 1;
1722  recur->setWeekStart( wkst );
1723 
1724  // And now all BY*
1725  TQValueList<int> lst;
1726  int i;
1727  int index = 0;
1728 
1729 #define readSetByList(rrulecomp,setfunc) \
1730  index = 0; \
1731  lst.clear(); \
1732  while ( (i = r.rrulecomp[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) \
1733  lst.append( i ); \
1734  if ( !lst.isEmpty() ) recur->setfunc( lst );
1735 
1736  // BYSECOND, MINUTE and HOUR, MONTHDAY, YEARDAY, WEEKNUMBER, MONTH
1737  // and SETPOS are standard int lists, so we can treat them with the
1738  // same macro
1739  readSetByList( by_second, setBySeconds );
1740  readSetByList( by_minute, setByMinutes );
1741  readSetByList( by_hour, setByHours );
1742  readSetByList( by_month_day, setByMonthDays );
1743  readSetByList( by_year_day, setByYearDays );
1744  readSetByList( by_week_no, setByWeekNumbers );
1745  readSetByList( by_month, setByMonths );
1746  readSetByList( by_set_pos, setBySetPos );
1747 #undef readSetByList
1748 
1749  // BYDAY is a special case, since it's not an int list
1750  TQValueList<RecurrenceRule::WDayPos> wdlst;
1751  short day;
1752  index=0;
1753  while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
1755  pos.setDay( ( icalrecurrencetype_day_day_of_week( day ) + 5 )%7 + 1 );
1756  pos.setPos( icalrecurrencetype_day_position( day ) );
1757 // kdDebug(5800)<< " o) By day, index="<<index-1<<", pos="<<pos.Pos<<", day="<<pos.Day<<endl;
1758  wdlst.append( pos );
1759  }
1760  if ( !wdlst.isEmpty() ) recur->setByDays( wdlst );
1761 
1762 
1763  // TODO Store all X- fields of the RRULE inside the recurrence (so they are
1764  // preserved
1765 }
1766 
1767 
1768 void ICalFormatImpl::readAlarm(icalcomponent *alarm,Incidence *incidence)
1769 {
1770 // kdDebug(5800) << "Read alarm for " << incidence->summary() << endl;
1771 
1772  Alarm* ialarm = incidence->newAlarm();
1773  ialarm->setRepeatCount(0);
1774  ialarm->setEnabled(true);
1775 
1776  // Determine the alarm's action type
1777  icalproperty *p = icalcomponent_get_first_property(alarm,ICAL_ACTION_PROPERTY);
1778  Alarm::Type type = Alarm::Display;
1779  icalproperty_action action = ICAL_ACTION_DISPLAY;
1780  if ( !p ) {
1781  kdDebug(5800) << "Unknown type of alarm, using default" << endl;
1782 // return;
1783  } else {
1784 
1785  action = icalproperty_get_action(p);
1786  switch ( action ) {
1787  case ICAL_ACTION_DISPLAY: type = Alarm::Display; break;
1788  case ICAL_ACTION_AUDIO: type = Alarm::Audio; break;
1789  case ICAL_ACTION_PROCEDURE: type = Alarm::Procedure; break;
1790  case ICAL_ACTION_EMAIL: type = Alarm::Email; break;
1791  default:
1792  kdDebug(5800) << "Unknown type of alarm: " << action << endl;
1793 // type = Alarm::Invalid;
1794  }
1795  }
1796  ialarm->setType(type);
1797 // kdDebug(5800) << " alarm type =" << type << endl;
1798 
1799  p = icalcomponent_get_first_property(alarm,ICAL_ANY_PROPERTY);
1800  while (p) {
1801  icalproperty_kind kind = icalproperty_isa(p);
1802 
1803  switch (kind) {
1804 
1805  case ICAL_TRIGGER_PROPERTY: {
1806  icaltriggertype trigger = icalproperty_get_trigger(p);
1807  if (icaltime_is_null_time(trigger.time)) {
1808  if (icaldurationtype_is_null_duration(trigger.duration)) {
1809  kdDebug(5800) << "ICalFormatImpl::readAlarm(): Trigger has no time and no duration." << endl;
1810  } else {
1811  Duration duration = icaldurationtype_as_int( trigger.duration );
1812  icalparameter *param = icalproperty_get_first_parameter(p,ICAL_RELATED_PARAMETER);
1813  if (param && icalparameter_get_related(param) == ICAL_RELATED_END)
1814  ialarm->setEndOffset(duration);
1815  else
1816  ialarm->setStartOffset(duration);
1817  }
1818  } else {
1819  ialarm->setTime(readICalDateTime(p, trigger.time));
1820  }
1821  break;
1822  }
1823  case ICAL_DURATION_PROPERTY: {
1824  icaldurationtype duration = icalproperty_get_duration(p);
1825  ialarm->setSnoozeTime( readICalDuration( duration ) );
1826  break;
1827  }
1828  case ICAL_REPEAT_PROPERTY:
1829  ialarm->setRepeatCount(icalproperty_get_repeat(p));
1830  break;
1831 
1832  // Only in DISPLAY and EMAIL and PROCEDURE alarms
1833  case ICAL_DESCRIPTION_PROPERTY: {
1834  TQString description = TQString::fromUtf8(icalproperty_get_description(p));
1835  switch ( action ) {
1836  case ICAL_ACTION_DISPLAY:
1837  ialarm->setText( description );
1838  break;
1839  case ICAL_ACTION_PROCEDURE:
1840  ialarm->setProgramArguments( description );
1841  break;
1842  case ICAL_ACTION_EMAIL:
1843  ialarm->setMailText( description );
1844  break;
1845  default:
1846  break;
1847  }
1848  break;
1849  }
1850  // Only in EMAIL alarm
1851  case ICAL_SUMMARY_PROPERTY:
1852  ialarm->setMailSubject(TQString::fromUtf8(icalproperty_get_summary(p)));
1853  break;
1854 
1855  // Only in EMAIL alarm
1856  case ICAL_ATTENDEE_PROPERTY: {
1857  TQString email = TQString::fromUtf8(icalproperty_get_attendee(p));
1858  if ( email.startsWith("mailto:", false ) ) {
1859  email = email.mid( 7 );
1860  }
1861  TQString name;
1862  icalparameter *param = icalproperty_get_first_parameter(p,ICAL_CN_PARAMETER);
1863  if (param) {
1864  name = TQString::fromUtf8(icalparameter_get_cn(param));
1865  }
1866  ialarm->addMailAddress(Person(name, email));
1867  break;
1868  }
1869  // Only in AUDIO and EMAIL and PROCEDURE alarms
1870  case ICAL_ATTACH_PROPERTY: {
1871  Attachment *attach = readAttachment( p );
1872  if ( attach && attach->isUri() ) {
1873  switch ( action ) {
1874  case ICAL_ACTION_AUDIO:
1875  ialarm->setAudioFile( attach->uri() );
1876  break;
1877  case ICAL_ACTION_PROCEDURE:
1878  ialarm->setProgramFile( attach->uri() );
1879  break;
1880  case ICAL_ACTION_EMAIL:
1881  ialarm->addMailAttachment( attach->uri() );
1882  break;
1883  default:
1884  break;
1885  }
1886  } else {
1887  kdDebug() << "Alarm attachments currently only support URIs, but "
1888  "no binary data" << endl;
1889  }
1890  delete attach;
1891  break;
1892  }
1893  default:
1894  break;
1895  }
1896 
1897  p = icalcomponent_get_next_property(alarm,ICAL_ANY_PROPERTY);
1898  }
1899 
1900  // custom properties
1901  readCustomProperties(alarm, ialarm);
1902 
1903  // TODO: check for consistency of alarm properties
1904 }
1905 
1906 icaldatetimeperiodtype ICalFormatImpl::writeICalDatePeriod( const TQDate &date )
1907 {
1908  icaldatetimeperiodtype t;
1909  t.time = writeICalDate( date );
1910  t.period = icalperiodtype_null_period();
1911  return t;
1912 }
1913 
1914 icaldatetimeperiodtype ICalFormatImpl::writeICalDateTimePeriod( const TQDateTime &date )
1915 {
1916  icaldatetimeperiodtype t;
1917  t.time = writeICalDateTime( date );
1918  t.period = icalperiodtype_null_period();
1919  return t;
1920 }
1921 
1922 icaltimetype ICalFormatImpl::writeICalDate(const TQDate &date)
1923 {
1924  icaltimetype t = icaltime_null_time();
1925 
1926  t.year = date.year();
1927  t.month = date.month();
1928  t.day = date.day();
1929 
1930  t.hour = 0;
1931  t.minute = 0;
1932  t.second = 0;
1933 
1934  t.is_date = 1;
1935 
1936  t.is_utc = 0;
1937 
1938  t.zone = 0;
1939 
1940  return t;
1941 }
1942 
1943 icaltimetype ICalFormatImpl::writeICalDateTime(const TQDateTime &datetime)
1944 {
1945  icaltimetype t = icaltime_null_time();
1946 
1947  t.year = datetime.date().year();
1948  t.month = datetime.date().month();
1949  t.day = datetime.date().day();
1950 
1951  t.hour = datetime.time().hour();
1952  t.minute = datetime.time().minute();
1953  t.second = datetime.time().second();
1954 
1955  t.is_date = 0;
1956  t.zone = icaltimezone_get_builtin_timezone ( mParent->timeZoneId().latin1() );
1957  t.is_utc = 0;
1958 
1959  // _dumpIcaltime( t );
1960  /* The TQDateTime we get passed in is to be considered in the timezone of
1961  * the current calendar (mParent's), or, if there is none, to be floating.
1962  * In the later case store a floating time, in the former normalize to utc. */
1963  if (mParent->timeZoneId().isEmpty())
1964  t = icaltime_convert_to_zone( t, 0 ); //make floating timezone
1965  else {
1966  icaltimezone* tz = icaltimezone_get_builtin_timezone ( mParent->timeZoneId().latin1() );
1967  icaltimezone* utc = icaltimezone_get_utc_timezone();
1968  if ( tz != utc ) {
1969  t.zone = tz;
1970  t = icaltime_convert_to_zone( t, utc );
1971  } else {
1972  t.is_utc = 1;
1973  t.zone = utc;
1974  }
1975  }
1976 // _dumpIcaltime( t );
1977 
1978  return t;
1979 }
1980 
1981 TQDateTime ICalFormatImpl::readICalDateTime( icalproperty *p, icaltimetype& t, icaltimezone* tz )
1982 {
1983 // kdDebug(5800) << "ICalFormatImpl::readICalDateTime()" << endl;
1984  if ( tz && t.is_utc == 0 ) { // Only use the TZ if time is not UTC.
1985  // FIXME: We'll need to make sure to apply the appropriate TZ, not just
1986  // the first one found.
1987  t.is_utc = (tz == icaltimezone_get_utc_timezone())?1:0;
1988  if (t.is_utc == 0) {
1989  icalparameter *param = p ? icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) : 0;
1990  const char *tzid = param ? icalparameter_get_tzid(param) : 0;
1991  if ( !tzid )
1992  t.zone = tz;
1993  else {
1994  icaltimezone* icaltz;
1995  // Try to match the ID with the libical time zone's location property
1996  icaltz = icaltimezone_get_builtin_timezone( tzid );
1997  if ( icaltz ) {
1998 // kdDebug(5800) << "ICalFormatImpl::readICalDateTime(): time zone '" << tzid << "' read from libical database" << endl;
1999  }
2000  t.zone = icaltz;
2001  }
2002  }
2003  } else {
2004  t.zone = icaltimezone_get_utc_timezone();
2005  }
2006  //_dumpIcaltime( t );
2007 
2008  // Convert to view time
2009  if ( !mParent->timeZoneId().isEmpty() && t.zone ) {
2010 // kdDebug(5800) << "--- Converting time from: " << icaltimezone_get_tzid( const_cast<icaltimezone*>( t.zone ) ) << " (" << ICalDate2TQDate(t) << ")." << endl;
2011  icaltimezone* viewTimeZone = icaltimezone_get_builtin_timezone ( mParent->timeZoneId().latin1() );
2012  icaltimezone_convert_time( &t, const_cast<icaltimezone*>(t.zone), viewTimeZone );
2013 // kdDebug(5800) << "--- Converted to zone " << mParent->timeZoneId() << " (" << ICalDate2TQDate(t) << ")." << endl;
2014  }
2015 
2016  return ICalDate2TQDate(t);
2017 }
2018 
2019 TQDate ICalFormatImpl::readICalDate(icaltimetype t)
2020 {
2021  return ICalDate2TQDate(t).date();
2022 }
2023 
2024 icaldurationtype ICalFormatImpl::writeICalDuration(int seconds)
2025 {
2026  // should be able to use icaldurationtype_from_int(), except we know
2027  // that some older tools do not properly support weeks. So we never
2028  // set a week duration, only days
2029 
2030  icaldurationtype d;
2031 
2032  d.is_neg = (seconds<0)?1:0;
2033  if (seconds<0) seconds = -seconds;
2034 
2035  d.weeks = 0;
2036  d.days = seconds / gSecondsPerDay;
2037  seconds %= gSecondsPerDay;
2038  d.hours = seconds / gSecondsPerHour;
2039  seconds %= gSecondsPerHour;
2040  d.minutes = seconds / gSecondsPerMinute;
2041  seconds %= gSecondsPerMinute;
2042  d.seconds = seconds;
2043 
2044  return d;
2045 }
2046 
2047 int ICalFormatImpl::readICalDuration(icaldurationtype d)
2048 {
2049  int result = 0;
2050 
2051  result += d.weeks * gSecondsPerWeek;
2052  result += d.days * gSecondsPerDay;
2053  result += d.hours * gSecondsPerHour;
2054  result += d.minutes * gSecondsPerMinute;
2055  result += d.seconds;
2056 
2057  if (d.is_neg) result *= -1;
2058 
2059  return result;
2060 }
2061 
2062 icalcomponent *ICalFormatImpl::createCalendarComponent(Calendar *cal)
2063 {
2064  icalcomponent *calendar;
2065 
2066  // Root component
2067  calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
2068 
2069  icalproperty *p;
2070 
2071  // Product Identifier
2072  p = icalproperty_new_prodid(CalFormat::productId().utf8());
2073  icalcomponent_add_property(calendar,p);
2074 
2075  // TODO: Add time zone
2076 
2077  // iCalendar version (2.0)
2078  p = icalproperty_new_version(const_cast<char *>(_ICAL_VERSION));
2079  icalcomponent_add_property(calendar,p);
2080 
2081  // Custom properties
2082  if( cal != 0 )
2083  writeCustomProperties(calendar, cal);
2084 
2085  return calendar;
2086 }
2087 
2088 
2089 
2090 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
2091 // and break it down from its tree-like format into the dictionary format
2092 // that is used internally in the ICalFormatImpl.
2093 bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar )
2094 {
2095  // this function will populate the caldict dictionary and other event
2096  // lists. It turns vevents into Events and then inserts them.
2097 
2098  if (!calendar) return false;
2099 
2100 // TODO: check for METHOD
2101 
2102  icalproperty *p;
2103 
2104  p = icalcomponent_get_first_property(calendar,ICAL_PRODID_PROPERTY);
2105  if (!p) {
2106  kdDebug(5800) << "No PRODID property found" << endl;
2107  mLoadedProductId = "";
2108  } else {
2109  mLoadedProductId = TQString::fromUtf8(icalproperty_get_prodid(p));
2110 // kdDebug(5800) << "VCALENDAR prodid: '" << mLoadedProductId << "'" << endl;
2111 
2112  delete mCompat;
2113  mCompat = CompatFactory::createCompat( mLoadedProductId );
2114  }
2115 
2116  p = icalcomponent_get_first_property(calendar,ICAL_VERSION_PROPERTY);
2117  if (!p) {
2118  kdDebug(5800) << "No VERSION property found" << endl;
2119  mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown));
2120  return false;
2121  } else {
2122  const char *version = icalproperty_get_version(p);
2123  if ( !version ) {
2124  kdDebug(5800) << "No VERSION property found" << endl;
2125  mParent->setException( new ErrorFormat(
2127  i18n( "No VERSION property found" ) ) );
2128  return false;
2129  }
2130 
2131 // kdDebug(5800) << "VCALENDAR version: '" << version << "'" << endl;
2132 
2133  if (strcmp(version,"1.0") == 0) {
2134  kdDebug(5800) << "Expected iCalendar, got vCalendar" << endl;
2135  mParent->setException(new ErrorFormat(ErrorFormat::CalVersion1,
2136  i18n("Expected iCalendar format")));
2137  return false;
2138  } else if (strcmp(version,"2.0") != 0) {
2139  kdDebug(5800) << "Expected iCalendar, got unknown format" << endl;
2140  mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown));
2141  return false;
2142  }
2143  }
2144 
2145  // custom properties
2146  readCustomProperties(calendar, cal);
2147 
2148 // TODO: set time zone
2149 
2150  // read a VTIMEZONE if there is one
2151  icalcomponent *ctz =
2152  icalcomponent_get_first_component( calendar, ICAL_VTIMEZONE_COMPONENT );
2153 
2154  // Store all events with a relatedTo property in a list for post-processing
2155  mEventsRelate.clear();
2156  mTodosRelate.clear();
2157  // TODO: make sure that only actually added events go to this lists.
2158 
2159  icalcomponent *c;
2160 
2161  // Iterate through all todos
2162  c = icalcomponent_get_first_component(calendar,ICAL_VTODO_COMPONENT);
2163  while (c) {
2164 // kdDebug(5800) << "----Todo found" << endl;
2165  Todo *todo = readTodo(c);
2166  if (todo) {
2167  if (todo->hasRecurrenceID()) {
2168  TQString originalUid = todo->uid();
2169  todo->setUid(originalUid + TQString("-recur-%1").arg(todo->recurrenceID().toTime_t()));
2170  if (!cal->todo(todo->uid())) {
2171  if ( !cal->addTodo( todo ) ) {
2172  cal->endBatchAdding();
2173  // If the user pressed cancel, return true, it's not an error.
2174  return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
2175  }
2176  if (!cal->event(originalUid)) {
2177  printf("FIXME! [WARNING] Parent for child event does not yet exist!\n");
2178  }
2179  else {
2180  // Add this todo to its parent
2181  cal->todo(originalUid)->addChildIncidence(todo->uid());
2182  // And the parent to the child
2183  todo->addChildIncidence(cal->todo(originalUid)->uid());
2184  }
2185  }
2186  }
2187  else {
2188  if (!cal->todo(todo->uid())) {
2189  if ( !cal->addTodo( todo ) ) {
2190  cal->endBatchAdding();
2191  // If the user pressed cancel, return true, it's not an error.
2192  return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
2193  }
2194  } else {
2195  delete todo;
2196  mTodosRelate.remove( todo );
2197  }
2198  }
2199  }
2200  c = icalcomponent_get_next_component(calendar,ICAL_VTODO_COMPONENT);
2201  }
2202 
2203  // Iterate through all events
2204  c = icalcomponent_get_first_component(calendar,ICAL_VEVENT_COMPONENT);
2205  while (c) {
2206 // kdDebug(5800) << "----Event found" << endl;
2207  Event *event = readEvent(c, ctz);
2208  if (event) {
2209  if (event->hasRecurrenceID()) {
2210  TQString originalUid = event->uid();
2211  event->setUid(originalUid + TQString("-recur-%1").arg(event->recurrenceID().toTime_t()));
2212  if (!cal->event(event->uid())) {
2213  cal->addEvent(event);
2214  if (!cal->event(originalUid)) {
2215  printf("FIXME! [WARNING] Parent for child event does not yet exist!\n");
2216  }
2217  else {
2218  // Add this event to its parent
2219  cal->event(originalUid)->addChildIncidence(event->uid());
2220  // And the parent to the child
2221  event->addChildIncidence(cal->event(originalUid)->uid());
2222  }
2223  }
2224  }
2225  else {
2226  if (!cal->event(event->uid())) {
2227  if ( !cal->addEvent( event ) ) {
2228  cal->endBatchAdding();
2229  // If the user pressed cancel, return true, it's not an error.
2230  return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
2231  }
2232  } else {
2233  delete event;
2234  mEventsRelate.remove( event );
2235  }
2236  }
2237  }
2238  c = icalcomponent_get_next_component(calendar,ICAL_VEVENT_COMPONENT);
2239  }
2240 
2241  // Iterate through all journals
2242  c = icalcomponent_get_first_component(calendar,ICAL_VJOURNAL_COMPONENT);
2243  while (c) {
2244 // kdDebug(5800) << "----Journal found" << endl;
2245  Journal *journal = readJournal(c);
2246  if (journal) {
2247  if (journal->hasRecurrenceID()) {
2248  TQString originalUid = journal->uid();
2249  journal->setUid(originalUid + TQString("-recur-%1").arg(journal->recurrenceID().toTime_t()));
2250  if (!cal->journal(journal->uid())) {
2251  cal->addJournal(journal);
2252  if (!cal->event(originalUid)) {
2253  printf("FIXME! [WARNING] Parent for child event does not yet exist!\n");
2254  }
2255  else {
2256  // Add this journal to its parent
2257  cal->journal(originalUid)->addChildIncidence(journal->uid());
2258  // And the parent to the child
2259  journal->addChildIncidence(cal->journal(originalUid)->uid());
2260  }
2261  }
2262  }
2263  else {
2264  if (!cal->journal(journal->uid())) {
2265  if ( !cal->addJournal(journal) ) {
2266  cal->endBatchAdding();
2267  // If the user pressed cancel, return true, it's not an error.
2268  return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
2269  }
2270  } else {
2271  delete journal;
2272  }
2273  }
2274  }
2275  c = icalcomponent_get_next_component(calendar,ICAL_VJOURNAL_COMPONENT);
2276  }
2277 
2278  cal->endBatchAdding();
2279 
2280  // Post-Process list of events with relations, put Event objects in relation
2281  Event::List::ConstIterator eIt;
2282  for ( eIt = mEventsRelate.begin(); eIt != mEventsRelate.end(); ++eIt ) {
2283  (*eIt)->setRelatedTo( cal->incidence( (*eIt)->relatedToUid() ) );
2284  }
2285  Todo::List::ConstIterator tIt;
2286  for ( tIt = mTodosRelate.begin(); tIt != mTodosRelate.end(); ++tIt ) {
2287  (*tIt)->setRelatedTo( cal->incidence( (*tIt)->relatedToUid() ) );
2288  }
2289 
2290  return true;
2291 }
2292 
2293 TQString ICalFormatImpl::extractErrorProperty(icalcomponent *c)
2294 {
2295 // kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: "
2296 // << icalcomponent_as_ical_string(c) << endl;
2297 
2298  TQString errorMessage;
2299 
2300  icalproperty *error;
2301  error = icalcomponent_get_first_property(c,ICAL_XLICERROR_PROPERTY);
2302  while(error) {
2303  errorMessage += icalproperty_get_xlicerror(error);
2304  errorMessage += "\n";
2305  error = icalcomponent_get_next_property(c,ICAL_XLICERROR_PROPERTY);
2306  }
2307 
2308 // kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: " << errorMessage << endl;
2309 
2310  return errorMessage;
2311 }
2312 
2313 void ICalFormatImpl::dumpIcalRecurrence(icalrecurrencetype r)
2314 {
2315  int i;
2316 
2317  kdDebug(5800) << " Freq: " << r.freq << endl;
2318  kdDebug(5800) << " Until: " << icaltime_as_ical_string(r.until) << endl;
2319  kdDebug(5800) << " Count: " << r.count << endl;
2320  if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) {
2321  int index = 0;
2322  TQString out = " By Day: ";
2323  while((i = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
2324  out.append(TQString::number(i) + " ");
2325  }
2326  kdDebug(5800) << out << endl;
2327  }
2328  if (r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX) {
2329  int index = 0;
2330  TQString out = " By Month Day: ";
2331  while((i = r.by_month_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
2332  out.append(TQString::number(i) + " ");
2333  }
2334  kdDebug(5800) << out << endl;
2335  }
2336  if (r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX) {
2337  int index = 0;
2338  TQString out = " By Year Day: ";
2339  while((i = r.by_year_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
2340  out.append(TQString::number(i) + " ");
2341  }
2342  kdDebug(5800) << out << endl;
2343  }
2344  if (r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX) {
2345  int index = 0;
2346  TQString out = " By Month: ";
2347  while((i = r.by_month[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
2348  out.append(TQString::number(i) + " ");
2349  }
2350  kdDebug(5800) << out << endl;
2351  }
2352  if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) {
2353  int index = 0;
2354  TQString out = " By Set Pos: ";
2355  while((i = r.by_set_pos[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
2356  kdDebug(5800) << "========= " << i << endl;
2357  out.append(TQString::number(i) + " ");
2358  }
2359  kdDebug(5800) << out << endl;
2360  }
2361 }
2362 
2363 icalcomponent *ICalFormatImpl::createScheduleComponent(IncidenceBase *incidence,
2364  Scheduler::Method method)
2365 {
2366  icalcomponent *message = createCalendarComponent();
2367 
2368  icalproperty_method icalmethod = ICAL_METHOD_NONE;
2369 
2370  switch (method) {
2371  case Scheduler::Publish:
2372  icalmethod = ICAL_METHOD_PUBLISH;
2373  break;
2374  case Scheduler::Request:
2375  icalmethod = ICAL_METHOD_REQUEST;
2376  break;
2377  case Scheduler::Refresh:
2378  icalmethod = ICAL_METHOD_REFRESH;
2379  break;
2380  case Scheduler::Cancel:
2381  icalmethod = ICAL_METHOD_CANCEL;
2382  break;
2383  case Scheduler::Add:
2384  icalmethod = ICAL_METHOD_ADD;
2385  break;
2386  case Scheduler::Reply:
2387  icalmethod = ICAL_METHOD_REPLY;
2388  break;
2389  case Scheduler::Counter:
2390  icalmethod = ICAL_METHOD_COUNTER;
2391  break;
2392  case Scheduler::Declinecounter:
2393  icalmethod = ICAL_METHOD_DECLINECOUNTER;
2394  break;
2395  default:
2396  kdDebug(5800) << "ICalFormat::createScheduleMessage(): Unknow method" << endl;
2397  return message;
2398  }
2399 
2400  icalcomponent_add_property(message,icalproperty_new_method(icalmethod));
2401 
2402  icalcomponent *inc = writeIncidence( incidence, method );
2403  /*
2404  * RFC 2446 states in section 3.4.3 ( REPLY to a VTODO ), that
2405  * a REQUEST-STATUS property has to be present. For the other two, event and
2406  * free busy, it can be there, but is optional. Until we do more
2407  * fine grained handling, assume all is well. Note that this is the
2408  * status of the _request_, not the attendee. Just to avoid confusion.
2409  * - till
2410  */
2411  if ( icalmethod == ICAL_METHOD_REPLY ) {
2412  struct icalreqstattype rst;
2413  rst.code = ICAL_2_0_SUCCESS_STATUS;
2414  rst.desc = 0;
2415  rst.debug = 0;
2416  icalcomponent_add_property( inc, icalproperty_new_requeststatus( rst ) );
2417  }
2418  icalcomponent_add_component( message, inc );
2419 
2420  return message;
2421 }