1 | /* |
2 | This file is part of the kcalcore library. |
3 | |
4 | Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> |
5 | Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> |
6 | Copyright (c) 2006 David Jarvie <software@astrojar.org.uk> |
7 | Copyright (C) 2012 Christian Mollekopf <mollekopf@kolabsys.com> |
8 | |
9 | This library is free software; you can redistribute it and/or |
10 | modify it under the terms of the GNU Library General Public |
11 | License as published by the Free Software Foundation; either |
12 | version 2 of the License, or (at your option) any later version. |
13 | |
14 | This library is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | Library General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Library General Public License |
20 | along with this library; see the file COPYING.LIB. If not, write to |
21 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
22 | Boston, MA 02110-1301, USA. |
23 | */ |
24 | /** |
25 | @file |
26 | This file is part of the API for handling calendar data and |
27 | defines the internal ICalFormat classes. |
28 | |
29 | @brief |
30 | This class provides the libical dependent functions for ICalFormat. |
31 | |
32 | @author Cornelius Schumacher \<schumacher@kde.org\> |
33 | @author Reinhold Kainhofer \<reinhold@kainhofer.com\> |
34 | @author David Jarvie \<software@astrojar.org.uk\> |
35 | */ |
36 | |
37 | #include <config-kcalcore.h> |
38 | #include "icalformat_p.h" |
39 | #include "compat.h" |
40 | #include "event.h" |
41 | #include "freebusy.h" |
42 | #include "icalformat.h" |
43 | #include "icaltimezones.h" |
44 | #include "incidencebase.h" |
45 | #include "journal.h" |
46 | #include "memorycalendar.h" |
47 | #include "todo.h" |
48 | #include "visitor.h" |
49 | |
50 | #include <KCodecs> |
51 | #include <KDebug> |
52 | |
53 | #include <QtCore/QFile> |
54 | |
55 | using namespace KCalCore; |
56 | |
57 | static const char APP_NAME_FOR_XPROPERTIES[] = "KCALCORE" ; |
58 | static const char ENABLED_ALARM_XPROPERTY[] = "ENABLED" ; |
59 | static const char IMPLEMENTATION_VERSION_XPROPERTY[] = "X-KDE-ICAL-IMPLEMENTATION-VERSION" ; |
60 | |
61 | /* Static helpers */ |
62 | /* |
63 | static void _dumpIcaltime( const icaltimetype& t) |
64 | { |
65 | kDebug() << "--- Y:" << t.year << "M:" << t.month << "D:" << t.day; |
66 | kDebug() << "--- H:" << t.hour << "M:" << t.minute << "S:" << t.second; |
67 | kDebug() << "--- isUtc:" << icaltime_is_utc( t ); |
68 | kDebug() << "--- zoneId:" << icaltimezone_get_tzid( const_cast<icaltimezone*>( t.zone ) ); |
69 | } |
70 | */ |
71 | |
72 | //@cond PRIVATE |
73 | template <typename K> |
74 | void removeAllICal(QVector< QSharedPointer<K> > &c, const QSharedPointer<K> &x) |
75 | { |
76 | if (c.count() < 1) { |
77 | return; |
78 | } |
79 | |
80 | int cnt = c.count(x); |
81 | if (cnt != 1) { |
82 | qCritical() << "There number of relatedTos for this incidence is " |
83 | << cnt << " (there must be 1 relatedTo only)" ; |
84 | Q_ASSERT_X(false, "removeAllICal" , "Count is not 1." ); |
85 | return; |
86 | } |
87 | |
88 | c.remove(c.indexOf(x)); |
89 | } |
90 | |
91 | static QString quoteForParam(const QString &text) |
92 | { |
93 | QString tmp = text; |
94 | tmp.remove(QLatin1Char('"')); |
95 | if (tmp.contains(QLatin1Char(';')) || tmp.contains(QLatin1Char(':')) || tmp.contains(QLatin1Char(','))) { |
96 | return tmp; // libical quotes in this case already, see icalparameter_as_ical_string() |
97 | } |
98 | return QString::fromLatin1("\"" ) + tmp + QString::fromLatin1("\"" ); |
99 | } |
100 | |
101 | const int gSecondsPerMinute = 60; |
102 | const int gSecondsPerHour = gSecondsPerMinute * 60; |
103 | const int gSecondsPerDay = gSecondsPerHour * 24; |
104 | const int gSecondsPerWeek = gSecondsPerDay * 7; |
105 | |
106 | class ToComponentVisitor : public Visitor |
107 | { |
108 | public: |
109 | ToComponentVisitor(ICalFormatImpl *impl, iTIPMethod m, ICalTimeZones *tzList = 0, |
110 | ICalTimeZones *tzUsedList = 0) |
111 | : mImpl(impl), mComponent(0), mMethod(m), mTzList(tzList), mTzUsedList(tzUsedList) |
112 | { |
113 | } |
114 | |
115 | bool visit(Event::Ptr e) |
116 | { |
117 | mComponent = mImpl->writeEvent(e, mTzList, mTzUsedList); |
118 | return true; |
119 | } |
120 | bool visit(Todo::Ptr t) |
121 | { |
122 | mComponent = mImpl->writeTodo(t, mTzList, mTzUsedList); |
123 | return true; |
124 | } |
125 | bool visit(Journal::Ptr j) |
126 | { |
127 | mComponent = mImpl->writeJournal(j, mTzList, mTzUsedList); |
128 | return true; |
129 | } |
130 | bool visit(FreeBusy::Ptr fb) |
131 | { |
132 | mComponent = mImpl->writeFreeBusy(fb, mMethod); |
133 | return true; |
134 | } |
135 | |
136 | icalcomponent *component() |
137 | { |
138 | return mComponent; |
139 | } |
140 | |
141 | private: |
142 | ICalFormatImpl *mImpl; |
143 | icalcomponent *mComponent; |
144 | iTIPMethod mMethod; |
145 | ICalTimeZones *mTzList; |
146 | ICalTimeZones *mTzUsedList; |
147 | }; |
148 | |
149 | class ICalFormatImpl::Private |
150 | { |
151 | public: |
152 | Private(ICalFormatImpl *impl, ICalFormat *parent) |
153 | : mImpl(impl), mParent(parent), mCompat(new Compat) {} |
154 | ~Private() { |
155 | delete mCompat; |
156 | } |
157 | void writeIncidenceBase(icalcomponent *parent, IncidenceBase::Ptr); |
158 | void readIncidenceBase(icalcomponent *parent, IncidenceBase::Ptr); |
159 | void writeCustomProperties(icalcomponent *parent, CustomProperties *); |
160 | void readCustomProperties(icalcomponent *parent, CustomProperties *); |
161 | |
162 | ICalFormatImpl *mImpl; |
163 | ICalFormat *mParent; |
164 | QString mLoadedProductId; // PRODID string loaded from calendar file |
165 | Event::List mEventsRelate; // events with relations |
166 | Todo::List mTodosRelate; // todos with relations |
167 | Compat *mCompat; |
168 | }; |
169 | //@endcond |
170 | |
171 | inline icaltimetype ICalFormatImpl::writeICalUtcDateTime(const KDateTime &dt) |
172 | { |
173 | return writeICalDateTime(dt.toUtc()); |
174 | } |
175 | |
176 | ICalFormatImpl::ICalFormatImpl(ICalFormat *parent) |
177 | : d(new Private(this, parent)) |
178 | { |
179 | } |
180 | |
181 | ICalFormatImpl::~ICalFormatImpl() |
182 | { |
183 | delete d; |
184 | } |
185 | |
186 | QString ICalFormatImpl::loadedProductId() const |
187 | { |
188 | return d->mLoadedProductId; |
189 | } |
190 | |
191 | icalcomponent *ICalFormatImpl::writeIncidence(const IncidenceBase::Ptr &incidence, |
192 | iTIPMethod method, |
193 | ICalTimeZones *tzList, |
194 | ICalTimeZones *tzUsedList) |
195 | { |
196 | ToComponentVisitor v(this, method, tzList, tzUsedList); |
197 | if (incidence->accept(v, incidence)) { |
198 | return v.component(); |
199 | } else { |
200 | return 0; |
201 | } |
202 | } |
203 | |
204 | icalcomponent *ICalFormatImpl::writeTodo(const Todo::Ptr &todo, ICalTimeZones *tzlist, |
205 | ICalTimeZones *tzUsedList) |
206 | { |
207 | QString tmpStr; |
208 | QStringList tmpStrList; |
209 | |
210 | icalcomponent *vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); |
211 | |
212 | writeIncidence(vtodo, todo.staticCast<Incidence>(), tzlist, tzUsedList); |
213 | |
214 | // due date |
215 | icalproperty *prop; |
216 | if (todo->hasDueDate()) { |
217 | icaltimetype due; |
218 | if (todo->allDay()) { |
219 | due = writeICalDate(todo->dtDue(true).date()); |
220 | prop = icalproperty_new_due(due); |
221 | } else { |
222 | prop = writeICalDateTimeProperty( |
223 | ICAL_DUE_PROPERTY, todo->dtDue(true), tzlist, tzUsedList); |
224 | } |
225 | icalcomponent_add_property(vtodo, prop); |
226 | } |
227 | |
228 | // start time |
229 | if (todo->hasStartDate()) { |
230 | icaltimetype start; |
231 | if (todo->allDay()) { |
232 | start = writeICalDate(todo->dtStart(true).date()); |
233 | prop = icalproperty_new_dtstart(start); |
234 | } else { |
235 | prop = writeICalDateTimeProperty( |
236 | ICAL_DTSTART_PROPERTY, todo->dtStart(true), tzlist, tzUsedList); |
237 | } |
238 | icalcomponent_add_property(vtodo, prop); |
239 | } |
240 | |
241 | // completion date (UTC) |
242 | if (todo->isCompleted()) { |
243 | if (!todo->hasCompletedDate()) { |
244 | // If the todo was created by KOrganizer<2.2 it does not have |
245 | // a correct completion date. Set one now. |
246 | todo->setCompleted(KDateTime::currentUtcDateTime()); |
247 | } |
248 | icaltimetype completed = writeICalUtcDateTime(todo->completed()); |
249 | icalcomponent_add_property( |
250 | vtodo, icalproperty_new_completed(completed)); |
251 | } |
252 | |
253 | icalcomponent_add_property( |
254 | vtodo, icalproperty_new_percentcomplete(todo->percentComplete())); |
255 | |
256 | if (todo->isCompleted()) { |
257 | if (icalcomponent_count_properties(vtodo, ICAL_STATUS_PROPERTY)) { |
258 | icalproperty *p = icalcomponent_get_first_property(vtodo, ICAL_STATUS_PROPERTY); |
259 | icalcomponent_remove_property(vtodo, p); |
260 | icalproperty_free(p); |
261 | } |
262 | icalcomponent_add_property(vtodo, icalproperty_new_status(ICAL_STATUS_COMPLETED)); |
263 | } |
264 | |
265 | if (todo->recurs() && todo->dtDue().isValid()) { |
266 | // dtDue( first = true ) returns the dtRecurrence() |
267 | prop = writeICalDateTimeProperty(ICAL_X_PROPERTY, todo->dtDue(), tzlist, tzUsedList); |
268 | icalproperty_set_x_name(prop, "X-KDE-LIBKCAL-DTRECURRENCE" ); |
269 | icalcomponent_add_property(vtodo, prop); |
270 | } |
271 | |
272 | return vtodo; |
273 | } |
274 | |
275 | icalcomponent *ICalFormatImpl::writeEvent(const Event::Ptr &event, |
276 | ICalTimeZones *tzlist, |
277 | ICalTimeZones *tzUsedList) |
278 | { |
279 | icalcomponent *vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); |
280 | |
281 | writeIncidence(vevent, event.staticCast<Incidence>(), tzlist, tzUsedList); |
282 | |
283 | // start time |
284 | icalproperty *prop; |
285 | icaltimetype start; |
286 | |
287 | KDateTime dt = event->dtStart(); |
288 | if (dt.isValid()) { |
289 | if (event->allDay()) { |
290 | start = writeICalDate(event->dtStart().date()); |
291 | prop = icalproperty_new_dtstart(start); |
292 | } else { |
293 | prop = writeICalDateTimeProperty( |
294 | ICAL_DTSTART_PROPERTY, event->dtStart(), tzlist, tzUsedList); |
295 | } |
296 | icalcomponent_add_property(vevent, prop); |
297 | } |
298 | |
299 | if (event->hasEndDate()) { |
300 | // End time. |
301 | // RFC2445 says that if DTEND is present, it has to be greater than DTSTART. |
302 | icaltimetype end; |
303 | KDateTime dt = event->dtEnd(); |
304 | if (event->allDay()) { |
305 | #if !defined(KCALCORE_FOR_MEEGO) |
306 | // +1 day because end date is non-inclusive. |
307 | end = writeICalDate(dt.date().addDays(1)); |
308 | #else |
309 | end = writeICalDate(dt.date()); |
310 | #endif |
311 | icalcomponent_add_property(vevent, icalproperty_new_dtend(end)); |
312 | } else { |
313 | if (dt != event->dtStart()) { |
314 | icalcomponent_add_property( |
315 | vevent, writeICalDateTimeProperty( |
316 | ICAL_DTEND_PROPERTY, dt, tzlist, tzUsedList)); |
317 | } |
318 | } |
319 | } |
320 | |
321 | // TODO: resources |
322 | #if 0 |
323 | // resources |
324 | QStringList tmpStrList = anEvent->resources(); |
325 | QString tmpStr = tmpStrList.join(";" ); |
326 | if (!tmpStr.isEmpty()) { |
327 | addPropValue(vevent, VCResourcesProp, tmpStr.toUtf8()); |
328 | } |
329 | |
330 | #endif |
331 | |
332 | // Transparency |
333 | switch (event->transparency()) { |
334 | case Event::Transparent: |
335 | icalcomponent_add_property( |
336 | vevent, |
337 | icalproperty_new_transp(ICAL_TRANSP_TRANSPARENT)); |
338 | break; |
339 | case Event::Opaque: |
340 | icalcomponent_add_property( |
341 | vevent, |
342 | icalproperty_new_transp(ICAL_TRANSP_OPAQUE)); |
343 | break; |
344 | } |
345 | |
346 | return vevent; |
347 | } |
348 | |
349 | icalcomponent *ICalFormatImpl::writeFreeBusy(const FreeBusy::Ptr &freebusy, |
350 | iTIPMethod method) |
351 | { |
352 | icalcomponent *vfreebusy = icalcomponent_new(ICAL_VFREEBUSY_COMPONENT); |
353 | |
354 | d->writeIncidenceBase(vfreebusy, freebusy.staticCast<IncidenceBase>()); |
355 | |
356 | icalcomponent_add_property( |
357 | vfreebusy, icalproperty_new_dtstart(writeICalUtcDateTime(freebusy->dtStart()))); |
358 | |
359 | icalcomponent_add_property( |
360 | vfreebusy, icalproperty_new_dtend(writeICalUtcDateTime(freebusy->dtEnd()))); |
361 | |
362 | #ifdef USE_ICAL_1_0 |
363 | Q_UNUSED(method); |
364 | icalcomponent_add_property( |
365 | vfreebusy, icalproperty_new_uid(freebusy->uid().toUtf8())); |
366 | #else |
367 | if (method == iTIPRequest) { |
368 | icalcomponent_add_property( |
369 | vfreebusy, icalproperty_new_uid(freebusy->uid().toUtf8())); |
370 | } |
371 | #endif |
372 | |
373 | //Loops through all the periods in the freebusy object |
374 | Period::List list = freebusy->busyPeriods(); |
375 | icalperiodtype period = icalperiodtype_null_period(); |
376 | for (int i = 0, count = list.count(); i < count; ++i) { |
377 | period.start = writeICalUtcDateTime(list[i].start()); |
378 | if (list[i].hasDuration()) { |
379 | period.duration = writeICalDuration(list[i].duration()); |
380 | } else { |
381 | period.end = writeICalUtcDateTime(list[i].end()); |
382 | } |
383 | icalcomponent_add_property( |
384 | vfreebusy, icalproperty_new_freebusy(period)); |
385 | } |
386 | |
387 | return vfreebusy; |
388 | } |
389 | |
390 | icalcomponent *ICalFormatImpl::writeJournal(const Journal::Ptr &journal, |
391 | ICalTimeZones *tzlist, |
392 | ICalTimeZones *tzUsedList) |
393 | { |
394 | icalcomponent *vjournal = icalcomponent_new(ICAL_VJOURNAL_COMPONENT); |
395 | |
396 | writeIncidence(vjournal, journal.staticCast<Incidence>(), tzlist, tzUsedList); |
397 | |
398 | // start time |
399 | icalproperty *prop; |
400 | KDateTime dt = journal->dtStart(); |
401 | if (dt.isValid()) { |
402 | icaltimetype start; |
403 | if (journal->allDay()) { |
404 | start = writeICalDate(dt.date()); |
405 | prop = icalproperty_new_dtstart(start); |
406 | } else { |
407 | prop = writeICalDateTimeProperty( |
408 | ICAL_DTSTART_PROPERTY, dt, tzlist, tzUsedList); |
409 | } |
410 | icalcomponent_add_property(vjournal, prop); |
411 | } |
412 | |
413 | return vjournal; |
414 | } |
415 | |
416 | void ICalFormatImpl::writeIncidence(icalcomponent *parent, |
417 | const Incidence::Ptr &incidence, |
418 | ICalTimeZones *tzlist, |
419 | ICalTimeZones *tzUsedList) |
420 | { |
421 | if (incidence->schedulingID() != incidence->uid()) { |
422 | // We need to store the UID in here. The rawSchedulingID will |
423 | // go into the iCal UID component |
424 | incidence->setCustomProperty("LIBKCAL" , "ID" , incidence->uid()); |
425 | } else { |
426 | incidence->removeCustomProperty("LIBKCAL" , "ID" ); |
427 | } |
428 | |
429 | d->writeIncidenceBase(parent, incidence.staticCast<IncidenceBase>()); |
430 | |
431 | // creation date in storage |
432 | icalcomponent_add_property( |
433 | parent, writeICalDateTimeProperty( |
434 | ICAL_CREATED_PROPERTY, incidence->created())); |
435 | |
436 | // unique id |
437 | // If the scheduling ID is different from the real UID, the real |
438 | // one is stored on X-REALID above |
439 | if (!incidence->schedulingID().isEmpty()) { |
440 | icalcomponent_add_property( |
441 | parent, icalproperty_new_uid(incidence->schedulingID().toUtf8())); |
442 | } |
443 | |
444 | // revision |
445 | if (incidence->revision() > 0) { // 0 is default, so don't write that out |
446 | icalcomponent_add_property( |
447 | parent, icalproperty_new_sequence(incidence->revision())); |
448 | } |
449 | |
450 | // last modification date |
451 | if (incidence->lastModified().isValid()) { |
452 | icalcomponent_add_property( |
453 | parent, writeICalDateTimeProperty( |
454 | ICAL_LASTMODIFIED_PROPERTY, incidence->lastModified())); |
455 | } |
456 | |
457 | // description |
458 | if (!incidence->description().isEmpty()) { |
459 | icalcomponent_add_property( |
460 | parent, writeDescription( |
461 | incidence->description(), incidence->descriptionIsRich())); |
462 | } |
463 | |
464 | // summary |
465 | if (!incidence->summary().isEmpty()) { |
466 | icalcomponent_add_property( |
467 | parent, writeSummary( |
468 | incidence->summary(), incidence->summaryIsRich())); |
469 | } |
470 | |
471 | // location |
472 | if (!incidence->location().isEmpty()) { |
473 | icalcomponent_add_property( |
474 | parent, writeLocation( |
475 | incidence->location(), incidence->locationIsRich())); |
476 | } |
477 | |
478 | // status |
479 | icalproperty_status status = ICAL_STATUS_NONE; |
480 | switch (incidence->status()) { |
481 | case Incidence::StatusTentative: |
482 | status = ICAL_STATUS_TENTATIVE; |
483 | break; |
484 | case Incidence::StatusConfirmed: |
485 | status = ICAL_STATUS_CONFIRMED; |
486 | break; |
487 | case Incidence::StatusCompleted: |
488 | status = ICAL_STATUS_COMPLETED; |
489 | break; |
490 | case Incidence::StatusNeedsAction: |
491 | status = ICAL_STATUS_NEEDSACTION; |
492 | break; |
493 | case Incidence::StatusCanceled: |
494 | status = ICAL_STATUS_CANCELLED; |
495 | break; |
496 | case Incidence::StatusInProcess: |
497 | status = ICAL_STATUS_INPROCESS; |
498 | break; |
499 | case Incidence::StatusDraft: |
500 | status = ICAL_STATUS_DRAFT; |
501 | break; |
502 | case Incidence::StatusFinal: |
503 | status = ICAL_STATUS_FINAL; |
504 | break; |
505 | case Incidence::StatusX: |
506 | { |
507 | icalproperty *p = icalproperty_new_status(ICAL_STATUS_X); |
508 | icalvalue_set_x(icalproperty_get_value(p), incidence->customStatus().toUtf8()); |
509 | icalcomponent_add_property(parent, p); |
510 | break; |
511 | } |
512 | case Incidence::StatusNone: |
513 | default: |
514 | break; |
515 | } |
516 | if (status != ICAL_STATUS_NONE) { |
517 | icalcomponent_add_property(parent, icalproperty_new_status(status)); |
518 | } |
519 | |
520 | // secrecy |
521 | icalproperty_class secClass; |
522 | switch (incidence->secrecy()) { |
523 | case Incidence::SecrecyPublic: |
524 | secClass = ICAL_CLASS_PUBLIC; |
525 | break; |
526 | case Incidence::SecrecyConfidential: |
527 | secClass = ICAL_CLASS_CONFIDENTIAL; |
528 | break; |
529 | case Incidence::SecrecyPrivate: |
530 | default: |
531 | secClass = ICAL_CLASS_PRIVATE; |
532 | break; |
533 | } |
534 | if (secClass != ICAL_CLASS_PUBLIC) { |
535 | icalcomponent_add_property(parent, icalproperty_new_class(secClass)); |
536 | } |
537 | |
538 | // geo |
539 | if (incidence->hasGeo()) { |
540 | icalgeotype geo; |
541 | geo.lat = incidence->geoLatitude(); |
542 | geo.lon = incidence->geoLongitude(); |
543 | icalcomponent_add_property(parent, icalproperty_new_geo(geo)); |
544 | } |
545 | |
546 | // priority |
547 | if (incidence->priority() > 0) { // 0 is undefined priority |
548 | icalcomponent_add_property( |
549 | parent, icalproperty_new_priority(incidence->priority())); |
550 | } |
551 | |
552 | // categories |
553 | QString categories = incidence->categories().join(QLatin1String("," )); |
554 | if (!categories.isEmpty()) { |
555 | icalcomponent_add_property( |
556 | parent, icalproperty_new_categories(categories.toUtf8())); |
557 | } |
558 | |
559 | // related event |
560 | if (!incidence->relatedTo().isEmpty()) { |
561 | icalcomponent_add_property( |
562 | parent, icalproperty_new_relatedto(incidence->relatedTo().toUtf8())); |
563 | } |
564 | |
565 | // recurrenceid |
566 | if (incidence->hasRecurrenceId()) { |
567 | icalproperty *p = writeICalDateTimeProperty( |
568 | ICAL_RECURRENCEID_PROPERTY, incidence->recurrenceId(), tzlist, tzUsedList); |
569 | if (incidence->thisAndFuture()) { |
570 | icalproperty_add_parameter( |
571 | p, icalparameter_new_range(ICAL_RANGE_THISANDFUTURE)); |
572 | } |
573 | icalcomponent_add_property(parent, p); |
574 | } |
575 | |
576 | RecurrenceRule::List rrules(incidence->recurrence()->rRules()); |
577 | RecurrenceRule::List::ConstIterator rit; |
578 | for (rit = rrules.constBegin(); rit != rrules.constEnd(); ++rit) { |
579 | icalcomponent_add_property( |
580 | parent, icalproperty_new_rrule(writeRecurrenceRule((*rit)))); |
581 | } |
582 | |
583 | RecurrenceRule::List exrules(incidence->recurrence()->exRules()); |
584 | RecurrenceRule::List::ConstIterator exit; |
585 | for (exit = exrules.constBegin(); exit != exrules.constEnd(); ++exit) { |
586 | icalcomponent_add_property( |
587 | parent, icalproperty_new_exrule(writeRecurrenceRule((*exit)))); |
588 | } |
589 | |
590 | DateList dateList = incidence->recurrence()->exDates(); |
591 | DateList::ConstIterator exIt; |
592 | for (exIt = dateList.constBegin(); exIt != dateList.constEnd(); ++exIt) { |
593 | icalcomponent_add_property( |
594 | parent, icalproperty_new_exdate(writeICalDate(*exIt))); |
595 | } |
596 | |
597 | DateTimeList dateTimeList = incidence->recurrence()->exDateTimes(); |
598 | DateTimeList::ConstIterator extIt; |
599 | for (extIt = dateTimeList.constBegin(); extIt != dateTimeList.constEnd(); ++extIt) { |
600 | icalcomponent_add_property( |
601 | parent, writeICalDateTimeProperty(ICAL_EXDATE_PROPERTY, *extIt, tzlist, tzUsedList)); |
602 | } |
603 | |
604 | dateList = incidence->recurrence()->rDates(); |
605 | DateList::ConstIterator rdIt; |
606 | for (rdIt = dateList.constBegin(); rdIt != dateList.constEnd(); ++rdIt) { |
607 | icalcomponent_add_property( |
608 | parent, icalproperty_new_rdate(writeICalDatePeriod(*rdIt))); |
609 | } |
610 | dateTimeList = incidence->recurrence()->rDateTimes(); |
611 | DateTimeList::ConstIterator rdtIt; |
612 | for (rdtIt = dateTimeList.constBegin(); rdtIt != dateTimeList.constEnd(); ++rdtIt) { |
613 | icalcomponent_add_property( |
614 | parent, writeICalDateTimeProperty(ICAL_RDATE_PROPERTY, *rdtIt, tzlist, tzUsedList)); |
615 | } |
616 | |
617 | // attachments |
618 | Attachment::List attachments = incidence->attachments(); |
619 | Attachment::List::ConstIterator atIt; |
620 | for (atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt) { |
621 | icalcomponent_add_property(parent, writeAttachment(*atIt)); |
622 | } |
623 | |
624 | // alarms |
625 | Alarm::List::ConstIterator alarmIt; |
626 | for (alarmIt = incidence->alarms().constBegin(); |
627 | alarmIt != incidence->alarms().constEnd(); ++alarmIt) { |
628 | icalcomponent_add_component(parent, writeAlarm(*alarmIt)); |
629 | } |
630 | |
631 | // duration |
632 | if (incidence->hasDuration()) { |
633 | icaldurationtype duration; |
634 | duration = writeICalDuration(incidence->duration()); |
635 | icalcomponent_add_property(parent, icalproperty_new_duration(duration)); |
636 | } |
637 | } |
638 | |
639 | //@cond PRIVATE |
640 | void ICalFormatImpl::Private::writeIncidenceBase(icalcomponent *parent, |
641 | IncidenceBase::Ptr incidenceBase) |
642 | { |
643 | // organizer stuff |
644 | if (!incidenceBase->organizer()->isEmpty()) { |
645 | icalproperty *p = mImpl->writeOrganizer(incidenceBase->organizer()); |
646 | if (p) { |
647 | icalcomponent_add_property(parent, p); |
648 | } |
649 | } |
650 | |
651 | icalcomponent_add_property( |
652 | parent, icalproperty_new_dtstamp(writeICalUtcDateTime(incidenceBase->lastModified()))); |
653 | |
654 | // attendees |
655 | if (incidenceBase->attendeeCount() > 0) { |
656 | Attendee::List::ConstIterator it; |
657 | for (it = incidenceBase->attendees().constBegin(); |
658 | it != incidenceBase->attendees().constEnd(); ++it) { |
659 | icalproperty *p = mImpl->writeAttendee(*it); |
660 | if (p) { |
661 | icalcomponent_add_property(parent, p); |
662 | } |
663 | } |
664 | } |
665 | |
666 | //contacts |
667 | QStringList contacts = incidenceBase->contacts(); |
668 | for (QStringList::const_iterator it = contacts.constBegin(); it != contacts.constEnd(); ++it) { |
669 | icalcomponent_add_property(parent, icalproperty_new_contact((*it).toUtf8())); |
670 | } |
671 | |
672 | // comments |
673 | QStringList = incidenceBase->comments(); |
674 | for (QStringList::const_iterator it = comments.constBegin(); it != comments.constEnd(); ++it) { |
675 | icalcomponent_add_property(parent, icalproperty_new_comment((*it).toUtf8())); |
676 | } |
677 | |
678 | // url |
679 | const QUrl url = incidenceBase->url(); |
680 | if (url.isValid()) { |
681 | icalcomponent_add_property(parent, icalproperty_new_url(url.toString().toUtf8())); |
682 | } |
683 | |
684 | // custom properties |
685 | writeCustomProperties(parent, incidenceBase.data()); |
686 | } |
687 | |
688 | void ICalFormatImpl::Private::writeCustomProperties(icalcomponent *parent, |
689 | CustomProperties *properties) |
690 | { |
691 | const QMap<QByteArray, QString> custom = properties->customProperties(); |
692 | for (QMap<QByteArray, QString>::ConstIterator c = custom.begin(); c != custom.end(); ++c) { |
693 | if (c.key().startsWith("X-KDE-VOLATILE" )) { //krazy:exclude=strings |
694 | // We don't write these properties to disk to disk |
695 | continue; |
696 | } |
697 | icalproperty *p = icalproperty_new_x(c.value().toUtf8()); |
698 | QString parameters = properties->nonKDECustomPropertyParameters(c.key()); |
699 | |
700 | // Minimalist parameter handler: extract icalparameter's out of |
701 | // the given input text (not really parsing as such) |
702 | if (!parameters.isEmpty()) { |
703 | QStringList sl = parameters.split(QLatin1Char(';')); |
704 | foreach(const QString ¶meter, sl) { |
705 | icalparameter *param = icalparameter_new_from_string(parameter.toUtf8()); |
706 | if (param) { |
707 | icalproperty_add_parameter(p, param); |
708 | } |
709 | } |
710 | } |
711 | |
712 | icalproperty_set_x_name(p, c.key()); |
713 | icalcomponent_add_property(parent, p); |
714 | } |
715 | } |
716 | //@endcond |
717 | |
718 | icalproperty *ICalFormatImpl::writeOrganizer(const Person::Ptr &organizer) |
719 | { |
720 | if (organizer->email().isEmpty()) { |
721 | return 0; |
722 | } |
723 | |
724 | icalproperty *p = icalproperty_new_organizer(QByteArray(QByteArray("MAILTO:" ) + organizer->email().toUtf8())); |
725 | |
726 | if (!organizer->name().isEmpty()) { |
727 | icalproperty_add_parameter( |
728 | p, icalparameter_new_cn(quoteForParam(organizer->name()).toUtf8())); |
729 | } |
730 | // TODO: Write dir, sent-by and language |
731 | |
732 | return p; |
733 | } |
734 | |
735 | icalproperty *ICalFormatImpl::writeDescription(const QString &description, bool isRich) |
736 | { |
737 | icalproperty *p = icalproperty_new_description(description.toUtf8()); |
738 | if (isRich) { |
739 | icalproperty_add_parameter(p, icalparameter_new_from_string("X-KDE-TEXTFORMAT=HTML" )); |
740 | } |
741 | return p; |
742 | } |
743 | |
744 | icalproperty *ICalFormatImpl::writeSummary(const QString &summary, bool isRich) |
745 | { |
746 | icalproperty *p = icalproperty_new_summary(summary.toUtf8()); |
747 | if (isRich) { |
748 | icalproperty_add_parameter(p, icalparameter_new_from_string("X-KDE-TEXTFORMAT=HTML" )); |
749 | } |
750 | return p; |
751 | } |
752 | |
753 | icalproperty *ICalFormatImpl::writeLocation(const QString &location, bool isRich) |
754 | { |
755 | icalproperty *p = icalproperty_new_location(location.toUtf8()); |
756 | if (isRich) { |
757 | icalproperty_add_parameter(p, icalparameter_new_from_string("X-KDE-TEXTFORMAT=HTML" )); |
758 | } |
759 | return p; |
760 | } |
761 | |
762 | icalproperty *ICalFormatImpl::writeAttendee(const Attendee::Ptr &attendee) |
763 | { |
764 | if (attendee->email().isEmpty()) { |
765 | return 0; |
766 | } |
767 | |
768 | icalproperty *p = |
769 | icalproperty_new_attendee(QByteArray(QByteArray("mailto:" ) + attendee->email().toUtf8())); |
770 | |
771 | if (!attendee->name().isEmpty()) { |
772 | icalproperty_add_parameter( |
773 | p, icalparameter_new_cn(quoteForParam(attendee->name()).toUtf8())); |
774 | } |
775 | |
776 | icalproperty_add_parameter( |
777 | p, icalparameter_new_rsvp(attendee->RSVP() ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE)); |
778 | |
779 | icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION; |
780 | switch (attendee->status()) { |
781 | default: |
782 | case Attendee::NeedsAction: |
783 | status = ICAL_PARTSTAT_NEEDSACTION; |
784 | break; |
785 | case Attendee::Accepted: |
786 | status = ICAL_PARTSTAT_ACCEPTED; |
787 | break; |
788 | case Attendee::Declined: |
789 | status = ICAL_PARTSTAT_DECLINED; |
790 | break; |
791 | case Attendee::Tentative: |
792 | status = ICAL_PARTSTAT_TENTATIVE; |
793 | break; |
794 | case Attendee::Delegated: |
795 | status = ICAL_PARTSTAT_DELEGATED; |
796 | break; |
797 | case Attendee::Completed: |
798 | status = ICAL_PARTSTAT_COMPLETED; |
799 | break; |
800 | case Attendee::InProcess: |
801 | status = ICAL_PARTSTAT_INPROCESS; |
802 | break; |
803 | } |
804 | icalproperty_add_parameter(p, icalparameter_new_partstat(status)); |
805 | |
806 | icalparameter_role role = ICAL_ROLE_REQPARTICIPANT; |
807 | switch (attendee->role()) { |
808 | case Attendee::Chair: |
809 | role = ICAL_ROLE_CHAIR; |
810 | break; |
811 | default: |
812 | case Attendee::ReqParticipant: |
813 | role = ICAL_ROLE_REQPARTICIPANT; |
814 | break; |
815 | case Attendee::OptParticipant: |
816 | role = ICAL_ROLE_OPTPARTICIPANT; |
817 | break; |
818 | case Attendee::NonParticipant: |
819 | role = ICAL_ROLE_NONPARTICIPANT; |
820 | break; |
821 | } |
822 | icalproperty_add_parameter(p, icalparameter_new_role(role)); |
823 | |
824 | icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL; |
825 | switch (attendee->cuType()) { |
826 | case Attendee::Unknown: |
827 | cutype = ICAL_CUTYPE_UNKNOWN; |
828 | break; |
829 | default: |
830 | case Attendee::Individual: |
831 | cutype = ICAL_CUTYPE_INDIVIDUAL; |
832 | break; |
833 | case Attendee::Group: |
834 | cutype = ICAL_CUTYPE_GROUP; |
835 | break; |
836 | case Attendee::Resource: |
837 | cutype = ICAL_CUTYPE_RESOURCE; |
838 | break; |
839 | case Attendee::Room: |
840 | cutype = ICAL_CUTYPE_ROOM; |
841 | break; |
842 | } |
843 | icalproperty_add_parameter(p, icalparameter_new_cutype(cutype)); |
844 | |
845 | if (!attendee->uid().isEmpty()) { |
846 | icalparameter *icalparameter_uid = icalparameter_new_x(attendee->uid().toUtf8()); |
847 | |
848 | icalparameter_set_xname(icalparameter_uid, "X-UID" ); |
849 | icalproperty_add_parameter(p, icalparameter_uid); |
850 | } |
851 | |
852 | if (!attendee->delegate().isEmpty()) { |
853 | icalparameter *icalparameter_delegate = |
854 | icalparameter_new_delegatedto(attendee->delegate().toUtf8()); |
855 | icalproperty_add_parameter(p, icalparameter_delegate); |
856 | } |
857 | |
858 | if (!attendee->delegator().isEmpty()) { |
859 | icalparameter *icalparameter_delegator = |
860 | icalparameter_new_delegatedfrom(attendee->delegator().toUtf8()); |
861 | icalproperty_add_parameter(p, icalparameter_delegator); |
862 | } |
863 | |
864 | return p; |
865 | } |
866 | |
867 | icalproperty *ICalFormatImpl::writeAttachment(const Attachment::Ptr &att) |
868 | { |
869 | icalattach *attach; |
870 | if (att->isUri()) { |
871 | attach = icalattach_new_from_url(att->uri().toUtf8().data()); |
872 | } else { |
873 | #ifdef USE_ICAL_0_46 |
874 | attach = icalattach_new_from_data((const char *)att->data().data(), 0, 0); |
875 | #else |
876 | attach = icalattach_new_from_data((unsigned char *)att->data().data(), 0, 0); |
877 | #endif |
878 | } |
879 | icalproperty *p = icalproperty_new_attach(attach); |
880 | |
881 | icalattach_unref(attach); |
882 | |
883 | if (!att->mimeType().isEmpty()) { |
884 | icalproperty_add_parameter( |
885 | p, icalparameter_new_fmttype(att->mimeType().toUtf8().data())); |
886 | } |
887 | |
888 | if (att->isBinary()) { |
889 | icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_BINARY)); |
890 | icalproperty_add_parameter(p, icalparameter_new_encoding(ICAL_ENCODING_BASE64)); |
891 | } |
892 | |
893 | if (att->showInline()) { |
894 | icalparameter *icalparameter_inline = icalparameter_new_x("inline" ); |
895 | icalparameter_set_xname(icalparameter_inline, "X-CONTENT-DISPOSITION" ); |
896 | icalproperty_add_parameter(p, icalparameter_inline); |
897 | } |
898 | |
899 | if (!att->label().isEmpty()) { |
900 | icalparameter *icalparameter_label = icalparameter_new_x(att->label().toUtf8()); |
901 | icalparameter_set_xname(icalparameter_label, "X-LABEL" ); |
902 | icalproperty_add_parameter(p, icalparameter_label); |
903 | } |
904 | |
905 | if (att->isLocal()) { |
906 | icalparameter *icalparameter_local = icalparameter_new_x("local" ); |
907 | icalparameter_set_xname(icalparameter_local, "X-KONTACT-TYPE" ); |
908 | icalproperty_add_parameter(p, icalparameter_local); |
909 | } |
910 | |
911 | return p; |
912 | } |
913 | |
914 | icalrecurrencetype ICalFormatImpl::writeRecurrenceRule(RecurrenceRule *recur) |
915 | { |
916 | icalrecurrencetype r; |
917 | icalrecurrencetype_clear(&r); |
918 | |
919 | switch (recur->recurrenceType()) { |
920 | case RecurrenceRule::rSecondly: |
921 | r.freq = ICAL_SECONDLY_RECURRENCE; |
922 | break; |
923 | case RecurrenceRule::rMinutely: |
924 | r.freq = ICAL_MINUTELY_RECURRENCE; |
925 | break; |
926 | case RecurrenceRule::rHourly: |
927 | r.freq = ICAL_HOURLY_RECURRENCE; |
928 | break; |
929 | case RecurrenceRule::rDaily: |
930 | r.freq = ICAL_DAILY_RECURRENCE; |
931 | break; |
932 | case RecurrenceRule::rWeekly: |
933 | r.freq = ICAL_WEEKLY_RECURRENCE; |
934 | break; |
935 | case RecurrenceRule::rMonthly: |
936 | r.freq = ICAL_MONTHLY_RECURRENCE; |
937 | break; |
938 | case RecurrenceRule::rYearly: |
939 | r.freq = ICAL_YEARLY_RECURRENCE; |
940 | break; |
941 | default: |
942 | r.freq = ICAL_NO_RECURRENCE; |
943 | kDebug() << "no recurrence" ; |
944 | break; |
945 | } |
946 | |
947 | int index = 0; |
948 | QList<int> bys; |
949 | QList<int>::ConstIterator it; |
950 | |
951 | // Now write out the BY* parts: |
952 | bys = recur->bySeconds(); |
953 | index = 0; |
954 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
955 | r.by_second[index++] = *it; |
956 | r.by_second[index++] = static_cast<short>(*it); |
957 | } |
958 | |
959 | bys = recur->byMinutes(); |
960 | index = 0; |
961 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
962 | r.by_minute[index++] = *it; |
963 | r.by_minute[index++] = static_cast<short>(*it); |
964 | } |
965 | |
966 | bys = recur->byHours(); |
967 | index = 0; |
968 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
969 | r.by_hour[index++] = *it; |
970 | r.by_hour[index++] = static_cast<short>(*it); |
971 | } |
972 | |
973 | bys = recur->byMonthDays(); |
974 | index = 0; |
975 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
976 | short dShort = static_cast<short>((*it) * 8); |
977 | r.by_month_day[index++] = static_cast<short>(icalrecurrencetype_day_position(dShort)); |
978 | } |
979 | |
980 | bys = recur->byYearDays(); |
981 | index = 0; |
982 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
983 | r.by_year_day[index++] = static_cast<short>(*it); |
984 | } |
985 | |
986 | bys = recur->byWeekNumbers(); |
987 | index = 0; |
988 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
989 | r.by_week_no[index++] = static_cast<short>(*it); |
990 | } |
991 | |
992 | bys = recur->byMonths(); |
993 | index = 0; |
994 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
995 | r.by_month[index++] = static_cast<short>(*it); |
996 | } |
997 | |
998 | bys = recur->bySetPos(); |
999 | index = 0; |
1000 | for (it = bys.constBegin(); it != bys.constEnd(); ++it) { |
1001 | r.by_set_pos[index++] = static_cast<short>(*it); |
1002 | } |
1003 | |
1004 | QList<RecurrenceRule::WDayPos> byd = recur->byDays(); |
1005 | int day; |
1006 | index = 0; |
1007 | for (QList<RecurrenceRule::WDayPos>::ConstIterator dit = byd.constBegin(); |
1008 | dit != byd.constEnd(); ++dit) { |
1009 | day = (*dit).day() % 7 + 1; // convert from Monday=1 to Sunday=1 |
1010 | if ((*dit).pos() < 0) { |
1011 | day += (-(*dit).pos()) * 8; |
1012 | day = -day; |
1013 | } else { |
1014 | day += (*dit).pos() * 8; |
1015 | } |
1016 | r.by_day[index++] = static_cast<short>(day); |
1017 | } |
1018 | |
1019 | r.week_start = |
1020 | static_cast<icalrecurrencetype_weekday>(recur->weekStart() % 7 + 1); |
1021 | |
1022 | if (recur->frequency() > 1) { |
1023 | // Dont' write out INTERVAL=1, because that's the default anyway |
1024 | r.interval = static_cast<short>(recur->frequency()); |
1025 | } |
1026 | |
1027 | if (recur->duration() > 0) { |
1028 | r.count = recur->duration(); |
1029 | } else if (recur->duration() == -1) { |
1030 | r.count = 0; |
1031 | } else { |
1032 | if (recur->allDay()) { |
1033 | r.until = writeICalDate(recur->endDt().date()); |
1034 | } else { |
1035 | r.until = writeICalUtcDateTime(recur->endDt()); |
1036 | } |
1037 | } |
1038 | |
1039 | return r; |
1040 | } |
1041 | |
1042 | icalcomponent *ICalFormatImpl::writeAlarm(const Alarm::Ptr &alarm) |
1043 | { |
1044 | if (alarm->enabled()) { |
1045 | alarm->setCustomProperty(APP_NAME_FOR_XPROPERTIES, ENABLED_ALARM_XPROPERTY, QLatin1String("TRUE" )); |
1046 | } else { |
1047 | alarm->setCustomProperty(APP_NAME_FOR_XPROPERTIES, ENABLED_ALARM_XPROPERTY, QLatin1String("FALSE" )); |
1048 | } |
1049 | |
1050 | icalcomponent *a = icalcomponent_new(ICAL_VALARM_COMPONENT); |
1051 | |
1052 | icalproperty_action action; |
1053 | icalattach *attach = 0; |
1054 | |
1055 | switch (alarm->type()) { |
1056 | case Alarm::Procedure: |
1057 | action = ICAL_ACTION_PROCEDURE; |
1058 | attach = icalattach_new_from_url( |
1059 | QFile::encodeName(alarm->programFile()).data()); |
1060 | icalcomponent_add_property(a, icalproperty_new_attach(attach)); |
1061 | if (!alarm->programArguments().isEmpty()) { |
1062 | icalcomponent_add_property( |
1063 | a, icalproperty_new_description(alarm->programArguments().toUtf8())); |
1064 | } |
1065 | break; |
1066 | case Alarm::Audio: |
1067 | action = ICAL_ACTION_AUDIO; |
1068 | if (!alarm->audioFile().isEmpty()) { |
1069 | attach = icalattach_new_from_url( |
1070 | QFile::encodeName(alarm->audioFile()).data()); |
1071 | icalcomponent_add_property(a, icalproperty_new_attach(attach)); |
1072 | } |
1073 | break; |
1074 | case Alarm::Email: |
1075 | { |
1076 | action = ICAL_ACTION_EMAIL; |
1077 | const Person::List addresses = alarm->mailAddresses(); |
1078 | for (Person::List::ConstIterator ad = addresses.constBegin(); |
1079 | ad != addresses.constEnd(); ++ad) { |
1080 | if (!(*ad)->email().isEmpty()) { |
1081 | icalproperty *p = icalproperty_new_attendee(QByteArray(QByteArray("MAILTO:" ) + (*ad)->email().toUtf8())); |
1082 | if (!(*ad)->name().isEmpty()) { |
1083 | icalproperty_add_parameter( |
1084 | p, icalparameter_new_cn(quoteForParam((*ad)->name()).toUtf8())); |
1085 | } |
1086 | icalcomponent_add_property(a, p); |
1087 | } |
1088 | } |
1089 | icalcomponent_add_property( |
1090 | a, icalproperty_new_summary(alarm->mailSubject().toUtf8())); |
1091 | icalcomponent_add_property( |
1092 | a, icalproperty_new_description(alarm->mailText().toUtf8())); |
1093 | QStringList attachments = alarm->mailAttachments(); |
1094 | if (attachments.count() > 0) { |
1095 | for (QStringList::const_iterator at = attachments.constBegin(); |
1096 | at != attachments.constEnd(); ++at) { |
1097 | attach = icalattach_new_from_url(QFile::encodeName(*at).data()); |
1098 | icalcomponent_add_property(a, icalproperty_new_attach(attach)); |
1099 | } |
1100 | } |
1101 | break; |
1102 | } |
1103 | case Alarm::Display: |
1104 | action = ICAL_ACTION_DISPLAY; |
1105 | icalcomponent_add_property( |
1106 | a, icalproperty_new_description(alarm->text().toUtf8())); |
1107 | break; |
1108 | case Alarm::Invalid: |
1109 | default: |
1110 | kDebug() << "Unknown type of alarm" ; |
1111 | action = ICAL_ACTION_NONE; |
1112 | break; |
1113 | } |
1114 | icalcomponent_add_property(a, icalproperty_new_action(action)); |
1115 | |
1116 | // Trigger time |
1117 | icaltriggertype trigger; |
1118 | if (alarm->hasTime()) { |
1119 | trigger.time = writeICalUtcDateTime(alarm->time()); |
1120 | trigger.duration = icaldurationtype_null_duration(); |
1121 | } else { |
1122 | trigger.time = icaltime_null_time(); |
1123 | Duration offset; |
1124 | if (alarm->hasStartOffset()) { |
1125 | offset = alarm->startOffset(); |
1126 | } else { |
1127 | offset = alarm->endOffset(); |
1128 | } |
1129 | trigger.duration = writeICalDuration(offset); |
1130 | } |
1131 | icalproperty *p = icalproperty_new_trigger(trigger); |
1132 | if (alarm->hasEndOffset()) { |
1133 | icalproperty_add_parameter(p, icalparameter_new_related(ICAL_RELATED_END)); |
1134 | } |
1135 | icalcomponent_add_property(a, p); |
1136 | |
1137 | // Repeat count and duration |
1138 | if (alarm->repeatCount()) { |
1139 | icalcomponent_add_property( |
1140 | a, icalproperty_new_repeat(alarm->repeatCount())); |
1141 | icalcomponent_add_property( |
1142 | a, icalproperty_new_duration(writeICalDuration(alarm->snoozeTime()))); |
1143 | } |
1144 | |
1145 | // Custom properties |
1146 | const QMap<QByteArray, QString> custom = alarm->customProperties(); |
1147 | for (QMap<QByteArray, QString>::ConstIterator c = custom.begin(); c != custom.end(); ++c) { |
1148 | icalproperty *p = icalproperty_new_x(c.value().toUtf8()); |
1149 | icalproperty_set_x_name(p, c.key()); |
1150 | icalcomponent_add_property(a, p); |
1151 | } |
1152 | |
1153 | icalattach_unref(attach); |
1154 | |
1155 | return a; |
1156 | } |
1157 | |
1158 | Todo::Ptr ICalFormatImpl::readTodo(icalcomponent *vtodo, ICalTimeZones *tzlist) |
1159 | { |
1160 | Todo::Ptr todo(new Todo); |
1161 | |
1162 | readIncidence(vtodo, todo, tzlist); |
1163 | |
1164 | icalproperty *p = icalcomponent_get_first_property(vtodo, ICAL_ANY_PROPERTY); |
1165 | |
1166 | while (p) { |
1167 | icalproperty_kind kind = icalproperty_isa(p); |
1168 | switch (kind) { |
1169 | case ICAL_DUE_PROPERTY: |
1170 | { // due date/time |
1171 | KDateTime kdt = readICalDateTimeProperty(p, tzlist); |
1172 | todo->setDtDue(kdt, true); |
1173 | todo->setAllDay(kdt.isDateOnly()); |
1174 | break; |
1175 | } |
1176 | case ICAL_COMPLETED_PROPERTY: // completion date/time |
1177 | todo->setCompleted(readICalDateTimeProperty(p, tzlist)); |
1178 | break; |
1179 | |
1180 | case ICAL_PERCENTCOMPLETE_PROPERTY: // Percent completed |
1181 | todo->setPercentComplete(icalproperty_get_percentcomplete(p)); |
1182 | break; |
1183 | |
1184 | case ICAL_RELATEDTO_PROPERTY: // related todo (parent) |
1185 | todo->setRelatedTo(QString::fromUtf8(icalproperty_get_relatedto(p))); |
1186 | d->mTodosRelate.append(todo); |
1187 | break; |
1188 | |
1189 | case ICAL_DTSTART_PROPERTY: |
1190 | // Flag that todo has start date. Value is read in by readIncidence(). |
1191 | if (todo->comments().filter(QLatin1String("NoStartDate" )).count()) { |
1192 | todo->setDtStart(KDateTime()); |
1193 | } else { |
1194 | todo->setHasStartDate(true); |
1195 | } |
1196 | break; |
1197 | case ICAL_X_PROPERTY: |
1198 | { |
1199 | const KDateTime dateTime = readICalDateTimeProperty(p, tzlist); |
1200 | if (dateTime.isValid()) { |
1201 | todo->setDtRecurrence(dateTime); |
1202 | } else { |
1203 | kDebug() << "Invalid dateTime" ; |
1204 | } |
1205 | } |
1206 | break; |
1207 | default: |
1208 | // TODO: do something about unknown properties? |
1209 | break; |
1210 | } |
1211 | |
1212 | p = icalcomponent_get_next_property(vtodo, ICAL_ANY_PROPERTY); |
1213 | } |
1214 | |
1215 | if (d->mCompat) { |
1216 | d->mCompat->fixEmptySummary(todo); |
1217 | } |
1218 | |
1219 | todo->resetDirtyFields(); |
1220 | return todo; |
1221 | } |
1222 | |
1223 | Event::Ptr ICalFormatImpl::readEvent(icalcomponent *vevent, ICalTimeZones *tzlist) |
1224 | { |
1225 | Event::Ptr event(new Event); |
1226 | |
1227 | readIncidence(vevent, event, tzlist); |
1228 | |
1229 | icalproperty *p = icalcomponent_get_first_property(vevent, ICAL_ANY_PROPERTY); |
1230 | |
1231 | bool dtEndProcessed = false; |
1232 | |
1233 | while (p) { |
1234 | icalproperty_kind kind = icalproperty_isa(p); |
1235 | switch (kind) { |
1236 | case ICAL_DTEND_PROPERTY: |
1237 | { // end date and time |
1238 | KDateTime kdt = readICalDateTimeProperty(p, tzlist); |
1239 | if (kdt.isDateOnly()) { |
1240 | // End date is non-inclusive |
1241 | QDate endDate = kdt.date().addDays(-1); |
1242 | if (d->mCompat) { |
1243 | d->mCompat->fixFloatingEnd(endDate); |
1244 | } |
1245 | if (endDate < event->dtStart().date()) { |
1246 | endDate = event->dtStart().date(); |
1247 | } |
1248 | event->setDtEnd(KDateTime(endDate, event->dtStart().timeSpec())); |
1249 | } else { |
1250 | event->setDtEnd(kdt); |
1251 | event->setAllDay(false); |
1252 | } |
1253 | dtEndProcessed = true; |
1254 | break; |
1255 | } |
1256 | case ICAL_RELATEDTO_PROPERTY: // related event (parent) |
1257 | event->setRelatedTo(QString::fromUtf8(icalproperty_get_relatedto(p))); |
1258 | d->mEventsRelate.append(event); |
1259 | break; |
1260 | |
1261 | case ICAL_TRANSP_PROPERTY: // Transparency |
1262 | { |
1263 | icalproperty_transp transparency = icalproperty_get_transp(p); |
1264 | if (transparency == ICAL_TRANSP_TRANSPARENT) { |
1265 | event->setTransparency(Event::Transparent); |
1266 | } else { |
1267 | event->setTransparency(Event::Opaque); |
1268 | } |
1269 | break; |
1270 | } |
1271 | |
1272 | default: |
1273 | // TODO: do something about unknown properties? |
1274 | break; |
1275 | } |
1276 | |
1277 | p = icalcomponent_get_next_property(vevent, ICAL_ANY_PROPERTY); |
1278 | } |
1279 | |
1280 | // according to rfc2445 the dtend shouldn't be written when it equals |
1281 | // start date. so assign one equal to start date. |
1282 | if (!dtEndProcessed && !event->hasDuration()) { |
1283 | event->setDtEnd(event->dtStart()); |
1284 | event->setHasEndDate(false); |
1285 | } |
1286 | |
1287 | QString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT" ); |
1288 | if (!msade.isEmpty()) { |
1289 | bool allDay = (msade == QLatin1String("TRUE" )); |
1290 | event->setAllDay(allDay); |
1291 | } |
1292 | |
1293 | if (d->mCompat) { |
1294 | d->mCompat->fixEmptySummary(event); |
1295 | } |
1296 | |
1297 | event->resetDirtyFields(); |
1298 | return event; |
1299 | } |
1300 | |
1301 | FreeBusy::Ptr ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy) |
1302 | { |
1303 | FreeBusy::Ptr freebusy(new FreeBusy); |
1304 | |
1305 | d->readIncidenceBase(vfreebusy, freebusy); |
1306 | |
1307 | icalproperty *p = icalcomponent_get_first_property(vfreebusy, ICAL_ANY_PROPERTY); |
1308 | |
1309 | FreeBusyPeriod::List periods; |
1310 | |
1311 | while (p) { |
1312 | icalproperty_kind kind = icalproperty_isa(p); |
1313 | switch (kind) { |
1314 | case ICAL_DTSTART_PROPERTY: // start date and time (UTC) |
1315 | freebusy->setDtStart(readICalUtcDateTimeProperty(p)); |
1316 | break; |
1317 | |
1318 | case ICAL_DTEND_PROPERTY: // end Date and Time (UTC) |
1319 | freebusy->setDtEnd(readICalUtcDateTimeProperty(p)); |
1320 | break; |
1321 | |
1322 | case ICAL_FREEBUSY_PROPERTY: //Any FreeBusy Times (UTC) |
1323 | { |
1324 | icalperiodtype icalperiod = icalproperty_get_freebusy(p); |
1325 | KDateTime period_start = readICalUtcDateTime(p, icalperiod.start); |
1326 | FreeBusyPeriod period; |
1327 | if (!icaltime_is_null_time(icalperiod.end)) { |
1328 | KDateTime period_end = readICalUtcDateTime(p, icalperiod.end); |
1329 | period = FreeBusyPeriod(period_start, period_end); |
1330 | } else { |
1331 | Duration duration(readICalDuration(icalperiod.duration)); |
1332 | period = FreeBusyPeriod(period_start, duration); |
1333 | } |
1334 | |
1335 | icalparameter *param = icalproperty_get_first_parameter(p, ICAL_X_PARAMETER); |
1336 | while (param) { |
1337 | if (strncmp(icalparameter_get_xname(param), "X-SUMMARY" , 9) == 0) { |
1338 | period.setSummary(QString::fromUtf8( |
1339 | KCodecs::base64Decode(icalparameter_get_xvalue(param)))); |
1340 | } |
1341 | if (strncmp(icalparameter_get_xname(param), "X-LOCATION" , 10) == 0) { |
1342 | period.setLocation(QString::fromUtf8( |
1343 | KCodecs::base64Decode(icalparameter_get_xvalue(param)))); |
1344 | } |
1345 | param = icalproperty_get_next_parameter(p, ICAL_X_PARAMETER); |
1346 | } |
1347 | |
1348 | periods.append(period); |
1349 | break; |
1350 | } |
1351 | |
1352 | default: |
1353 | // TODO: do something about unknown properties? |
1354 | break; |
1355 | } |
1356 | p = icalcomponent_get_next_property(vfreebusy, ICAL_ANY_PROPERTY); |
1357 | } |
1358 | freebusy->addPeriods(periods); |
1359 | |
1360 | freebusy->resetDirtyFields(); |
1361 | return freebusy; |
1362 | } |
1363 | |
1364 | Journal::Ptr ICalFormatImpl::readJournal(icalcomponent *vjournal, |
1365 | ICalTimeZones *tzlist) |
1366 | { |
1367 | Journal::Ptr journal(new Journal); |
1368 | readIncidence(vjournal, journal, tzlist); |
1369 | |
1370 | journal->resetDirtyFields(); |
1371 | return journal; |
1372 | } |
1373 | |
1374 | Attendee::Ptr ICalFormatImpl::readAttendee(icalproperty *attendee) |
1375 | { |
1376 | // the following is a hack to support broken calendars (like WebCalendar 1.0.x) |
1377 | // that include non-RFC-compliant attendees. Otherwise libical 0.42 asserts. |
1378 | if (!icalproperty_get_value(attendee)) { |
1379 | return Attendee::Ptr(); |
1380 | } |
1381 | |
1382 | icalparameter *p = 0; |
1383 | |
1384 | QString email = QString::fromUtf8(icalproperty_get_attendee(attendee)); |
1385 | if (email.startsWith(QLatin1String("mailto:" ), Qt::CaseInsensitive)) { |
1386 | email = email.mid(7); |
1387 | } |
1388 | |
1389 | // libical may return everything after ATTENDEE tag if the rest is |
1390 | // not meaningful. Verify the address to filter out these cases. |
1391 | if (!Person::isValidEmail(email)) { |
1392 | return Attendee::Ptr(); |
1393 | } |
1394 | |
1395 | QString name; |
1396 | QString uid; |
1397 | p = icalproperty_get_first_parameter(attendee, ICAL_CN_PARAMETER); |
1398 | if (p) { |
1399 | name = QString::fromUtf8(icalparameter_get_cn(p)); |
1400 | } else { |
1401 | } |
1402 | |
1403 | bool rsvp = false; |
1404 | p = icalproperty_get_first_parameter(attendee, ICAL_RSVP_PARAMETER); |
1405 | if (p) { |
1406 | icalparameter_rsvp rsvpParameter = icalparameter_get_rsvp(p); |
1407 | if (rsvpParameter == ICAL_RSVP_TRUE) { |
1408 | rsvp = true; |
1409 | } |
1410 | } |
1411 | |
1412 | Attendee::PartStat status = Attendee::NeedsAction; |
1413 | p = icalproperty_get_first_parameter(attendee, ICAL_PARTSTAT_PARAMETER); |
1414 | if (p) { |
1415 | icalparameter_partstat partStatParameter = icalparameter_get_partstat(p); |
1416 | switch (partStatParameter) { |
1417 | default: |
1418 | case ICAL_PARTSTAT_NEEDSACTION: |
1419 | status = Attendee::NeedsAction; |
1420 | break; |
1421 | case ICAL_PARTSTAT_ACCEPTED: |
1422 | status = Attendee::Accepted; |
1423 | break; |
1424 | case ICAL_PARTSTAT_DECLINED: |
1425 | status = Attendee::Declined; |
1426 | break; |
1427 | case ICAL_PARTSTAT_TENTATIVE: |
1428 | status = Attendee::Tentative; |
1429 | break; |
1430 | case ICAL_PARTSTAT_DELEGATED: |
1431 | status = Attendee::Delegated; |
1432 | break; |
1433 | case ICAL_PARTSTAT_COMPLETED: |
1434 | status = Attendee::Completed; |
1435 | break; |
1436 | case ICAL_PARTSTAT_INPROCESS: |
1437 | status = Attendee::InProcess; |
1438 | break; |
1439 | } |
1440 | } |
1441 | |
1442 | Attendee::Role role = Attendee::ReqParticipant; |
1443 | p = icalproperty_get_first_parameter(attendee, ICAL_ROLE_PARAMETER); |
1444 | if (p) { |
1445 | icalparameter_role roleParameter = icalparameter_get_role(p); |
1446 | switch (roleParameter) { |
1447 | case ICAL_ROLE_CHAIR: |
1448 | role = Attendee::Chair; |
1449 | break; |
1450 | default: |
1451 | case ICAL_ROLE_REQPARTICIPANT: |
1452 | role = Attendee::ReqParticipant; |
1453 | break; |
1454 | case ICAL_ROLE_OPTPARTICIPANT: |
1455 | role = Attendee::OptParticipant; |
1456 | break; |
1457 | case ICAL_ROLE_NONPARTICIPANT: |
1458 | role = Attendee::NonParticipant; |
1459 | break; |
1460 | } |
1461 | } |
1462 | |
1463 | Attendee::CuType cuType = Attendee::Individual; |
1464 | p = icalproperty_get_first_parameter( attendee, ICAL_CUTYPE_PARAMETER ); |
1465 | if (p) { |
1466 | icalparameter_cutype cutypeParameter = icalparameter_get_cutype(p); |
1467 | switch (cutypeParameter) { |
1468 | case ICAL_CUTYPE_X: |
1469 | case ICAL_CUTYPE_UNKNOWN: |
1470 | cuType = Attendee::Unknown; |
1471 | break; |
1472 | default: |
1473 | case ICAL_CUTYPE_NONE: |
1474 | case ICAL_CUTYPE_INDIVIDUAL: |
1475 | cuType = Attendee::Individual; |
1476 | break; |
1477 | case ICAL_CUTYPE_GROUP: |
1478 | cuType = Attendee::Group; |
1479 | break; |
1480 | case ICAL_CUTYPE_RESOURCE: |
1481 | cuType = Attendee::Resource; |
1482 | break; |
1483 | case ICAL_CUTYPE_ROOM: |
1484 | cuType = Attendee::Room; |
1485 | break; |
1486 | } |
1487 | } |
1488 | |
1489 | p = icalproperty_get_first_parameter(attendee, ICAL_X_PARAMETER); |
1490 | QMap<QByteArray, QString> custom; |
1491 | while (p) { |
1492 | QString xname = QString::fromLatin1(icalparameter_get_xname(p)).toUpper(); |
1493 | QString xvalue = QString::fromUtf8(icalparameter_get_xvalue(p)); |
1494 | if (xname == QLatin1String("X-UID" )) { |
1495 | uid = xvalue; |
1496 | } else { |
1497 | custom[xname.toUtf8()] = xvalue; |
1498 | } |
1499 | p = icalproperty_get_next_parameter(attendee, ICAL_X_PARAMETER); |
1500 | } |
1501 | |
1502 | Attendee::Ptr a(new Attendee(name, email, rsvp, status, role, uid)); |
1503 | a->setCuType(cuType); |
1504 | a->customProperties().setCustomProperties(custom); |
1505 | |
1506 | p = icalproperty_get_first_parameter(attendee, ICAL_DELEGATEDTO_PARAMETER); |
1507 | if (p) { |
1508 | a->setDelegate(QLatin1String(icalparameter_get_delegatedto(p))); |
1509 | } |
1510 | |
1511 | p = icalproperty_get_first_parameter(attendee, ICAL_DELEGATEDFROM_PARAMETER); |
1512 | if (p) { |
1513 | a->setDelegator(QLatin1String(icalparameter_get_delegatedfrom(p))); |
1514 | } |
1515 | |
1516 | return a; |
1517 | } |
1518 | |
1519 | Person::Ptr ICalFormatImpl::readOrganizer(icalproperty *organizer) |
1520 | { |
1521 | QString email = QString::fromUtf8(icalproperty_get_organizer(organizer)); |
1522 | if (email.startsWith(QLatin1String("mailto:" ), Qt::CaseInsensitive)) { |
1523 | email = email.mid(7); |
1524 | } |
1525 | QString cn; |
1526 | |
1527 | icalparameter *p = icalproperty_get_first_parameter(organizer, ICAL_CN_PARAMETER); |
1528 | |
1529 | if (p) { |
1530 | cn = QString::fromUtf8(icalparameter_get_cn(p)); |
1531 | } |
1532 | Person::Ptr org(new Person(cn, email)); |
1533 | // TODO: Treat sent-by, dir and language here, too |
1534 | return org; |
1535 | } |
1536 | |
1537 | Attachment::Ptr ICalFormatImpl::readAttachment(icalproperty *attach) |
1538 | { |
1539 | Attachment::Ptr attachment; |
1540 | |
1541 | QByteArray p; |
1542 | icalvalue *value = icalproperty_get_value(attach); |
1543 | |
1544 | switch (icalvalue_isa(value)) { |
1545 | case ICAL_ATTACH_VALUE: |
1546 | { |
1547 | icalattach *a = icalproperty_get_attach(attach); |
1548 | if (!icalattach_get_is_url(a)) { |
1549 | p = QByteArray(reinterpret_cast<const char *>(icalattach_get_data(a))); |
1550 | if (!p.isEmpty()) { |
1551 | attachment = Attachment::Ptr(new Attachment(p)); |
1552 | } |
1553 | } else { |
1554 | p = icalattach_get_url(a); |
1555 | if (!p.isEmpty()) { |
1556 | attachment = Attachment::Ptr(new Attachment(QString::fromUtf8(p))); |
1557 | } |
1558 | } |
1559 | break; |
1560 | } |
1561 | case ICAL_BINARY_VALUE: |
1562 | { |
1563 | icalattach *a = icalproperty_get_attach(attach); |
1564 | p = QByteArray(reinterpret_cast<const char *>(icalattach_get_data(a))); |
1565 | if (!p.isEmpty()) { |
1566 | attachment = Attachment::Ptr(new Attachment(p)); |
1567 | } |
1568 | break; |
1569 | } |
1570 | case ICAL_URI_VALUE: |
1571 | p = icalvalue_get_uri(value); |
1572 | attachment = Attachment::Ptr(new Attachment(QString::fromUtf8(p))); |
1573 | break; |
1574 | default: |
1575 | break; |
1576 | } |
1577 | |
1578 | if (attachment) { |
1579 | icalparameter *p = |
1580 | icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER); |
1581 | if (p) { |
1582 | attachment->setMimeType(QLatin1String(icalparameter_get_fmttype(p))); |
1583 | } |
1584 | |
1585 | p = icalproperty_get_first_parameter(attach, ICAL_X_PARAMETER); |
1586 | while (p) { |
1587 | QString xname = QString::fromLatin1(icalparameter_get_xname(p)).toUpper(); |
1588 | QString xvalue = QString::fromUtf8(icalparameter_get_xvalue(p)); |
1589 | if (xname == QLatin1String("X-CONTENT-DISPOSITION" )) { |
1590 | attachment->setShowInline(xvalue.toLower() == QLatin1String("inline" )); |
1591 | } |
1592 | if (xname == QLatin1String("X-LABEL" )) { |
1593 | attachment->setLabel(xvalue); |
1594 | } |
1595 | if (xname == QLatin1String("X-KONTACT-TYPE" )) { |
1596 | attachment->setLocal(xvalue.toLower() == QLatin1String("local" )); |
1597 | } |
1598 | p = icalproperty_get_next_parameter(attach, ICAL_X_PARAMETER); |
1599 | } |
1600 | |
1601 | p = icalproperty_get_first_parameter(attach, ICAL_X_PARAMETER); |
1602 | while (p) { |
1603 | if (strncmp(icalparameter_get_xname(p), "X-LABEL" , 7) == 0) { |
1604 | attachment->setLabel(QString::fromUtf8(icalparameter_get_xvalue(p))); |
1605 | } |
1606 | p = icalproperty_get_next_parameter(attach, ICAL_X_PARAMETER); |
1607 | } |
1608 | } |
1609 | |
1610 | return attachment; |
1611 | } |
1612 | |
1613 | void ICalFormatImpl::readIncidence(icalcomponent *parent, |
1614 | Incidence::Ptr incidence, |
1615 | ICalTimeZones *tzlist) |
1616 | { |
1617 | d->readIncidenceBase(parent, incidence); |
1618 | |
1619 | icalproperty *p = icalcomponent_get_first_property(parent, ICAL_ANY_PROPERTY); |
1620 | |
1621 | const char *text; |
1622 | int intvalue, inttext; |
1623 | icaldurationtype icalduration; |
1624 | KDateTime kdt; |
1625 | KDateTime dtstamp; |
1626 | |
1627 | QStringList categories; |
1628 | |
1629 | while (p) { |
1630 | icalproperty_kind kind = icalproperty_isa(p); |
1631 | switch (kind) { |
1632 | case ICAL_CREATED_PROPERTY: |
1633 | incidence->setCreated(readICalDateTimeProperty(p, tzlist)); |
1634 | break; |
1635 | |
1636 | case ICAL_DTSTAMP_PROPERTY: |
1637 | dtstamp = readICalDateTimeProperty(p, tzlist); |
1638 | break; |
1639 | |
1640 | case ICAL_SEQUENCE_PROPERTY: // sequence |
1641 | intvalue = icalproperty_get_sequence(p); |
1642 | incidence->setRevision(intvalue); |
1643 | break; |
1644 | |
1645 | case ICAL_LASTMODIFIED_PROPERTY: // last modification UTC date/time |
1646 | incidence->setLastModified(readICalDateTimeProperty(p, tzlist)); |
1647 | break; |
1648 | |
1649 | case ICAL_DTSTART_PROPERTY: // start date and time |
1650 | kdt = readICalDateTimeProperty(p, tzlist); |
1651 | incidence->setDtStart(kdt); |
1652 | incidence->setAllDay(kdt.isDateOnly()); |
1653 | break; |
1654 | |
1655 | case ICAL_DURATION_PROPERTY: // start date and time |
1656 | icalduration = icalproperty_get_duration(p); |
1657 | incidence->setDuration(readICalDuration(icalduration)); |
1658 | break; |
1659 | |
1660 | case ICAL_DESCRIPTION_PROPERTY: // description |
1661 | { |
1662 | QString textStr = QString::fromUtf8(icalproperty_get_description(p)); |
1663 | if (!textStr.isEmpty()) { |
1664 | QString valStr = QString::fromUtf8( |
1665 | icalproperty_get_parameter_as_string(p, "X-KDE-TEXTFORMAT" )); |
1666 | if (!valStr.compare(QLatin1String("HTML" ), Qt::CaseInsensitive)) { |
1667 | incidence->setDescription(textStr, true); |
1668 | } else { |
1669 | incidence->setDescription(textStr, false); |
1670 | } |
1671 | } |
1672 | } |
1673 | break; |
1674 | |
1675 | case ICAL_SUMMARY_PROPERTY: // summary |
1676 | { |
1677 | QString textStr = QString::fromUtf8(icalproperty_get_summary(p)); |
1678 | if (!textStr.isEmpty()) { |
1679 | QString valStr = QString::fromUtf8( |
1680 | icalproperty_get_parameter_as_string(p, "X-KDE-TEXTFORMAT" )); |
1681 | if (!valStr.compare(QLatin1String("HTML" ), Qt::CaseInsensitive)) { |
1682 | incidence->setSummary(textStr, true); |
1683 | } else { |
1684 | incidence->setSummary(textStr, false); |
1685 | } |
1686 | } |
1687 | } |
1688 | break; |
1689 | |
1690 | case ICAL_LOCATION_PROPERTY: // location |
1691 | { |
1692 | if (!icalproperty_get_value(p)) { |
1693 | //Fix for #191472. This is a pre-crash guard in case libical was |
1694 | //compiled in superstrict mode (--enable-icalerrors-are-fatal) |
1695 | //TODO: pre-crash guard other property getters too. |
1696 | break; |
1697 | } |
1698 | QString textStr = QString::fromUtf8(icalproperty_get_location(p)); |
1699 | if (!textStr.isEmpty()) { |
1700 | QString valStr = QString::fromUtf8( |
1701 | icalproperty_get_parameter_as_string(p, "X-KDE-TEXTFORMAT" )); |
1702 | if (!valStr.compare(QLatin1String("HTML" ), Qt::CaseInsensitive)) { |
1703 | incidence->setLocation(textStr, true); |
1704 | } else { |
1705 | incidence->setLocation(textStr, false); |
1706 | } |
1707 | } |
1708 | } |
1709 | break; |
1710 | |
1711 | case ICAL_STATUS_PROPERTY: // status |
1712 | { |
1713 | Incidence::Status stat; |
1714 | switch (icalproperty_get_status(p)) { |
1715 | case ICAL_STATUS_TENTATIVE: |
1716 | stat = Incidence::StatusTentative; |
1717 | break; |
1718 | case ICAL_STATUS_CONFIRMED: |
1719 | stat = Incidence::StatusConfirmed; |
1720 | break; |
1721 | case ICAL_STATUS_COMPLETED: |
1722 | stat = Incidence::StatusCompleted; |
1723 | break; |
1724 | case ICAL_STATUS_NEEDSACTION: |
1725 | stat = Incidence::StatusNeedsAction; |
1726 | break; |
1727 | case ICAL_STATUS_CANCELLED: |
1728 | stat = Incidence::StatusCanceled; |
1729 | break; |
1730 | case ICAL_STATUS_INPROCESS: |
1731 | stat = Incidence::StatusInProcess; |
1732 | break; |
1733 | case ICAL_STATUS_DRAFT: |
1734 | stat = Incidence::StatusDraft; |
1735 | break; |
1736 | case ICAL_STATUS_FINAL: |
1737 | stat = Incidence::StatusFinal; |
1738 | break; |
1739 | case ICAL_STATUS_X: |
1740 | incidence->setCustomStatus( |
1741 | QString::fromUtf8(icalvalue_get_x(icalproperty_get_value(p)))); |
1742 | stat = Incidence::StatusX; |
1743 | break; |
1744 | case ICAL_STATUS_NONE: |
1745 | default: |
1746 | stat = Incidence::StatusNone; |
1747 | break; |
1748 | } |
1749 | if (stat != Incidence::StatusX) { |
1750 | incidence->setStatus(stat); |
1751 | } |
1752 | break; |
1753 | } |
1754 | |
1755 | case ICAL_GEO_PROPERTY: // geo |
1756 | { |
1757 | icalgeotype geo = icalproperty_get_geo(p); |
1758 | incidence->setGeoLatitude(geo.lat); |
1759 | incidence->setGeoLongitude(geo.lon); |
1760 | incidence->setHasGeo(true); |
1761 | break; |
1762 | } |
1763 | |
1764 | case ICAL_PRIORITY_PROPERTY: // priority |
1765 | intvalue = icalproperty_get_priority(p); |
1766 | if (d->mCompat) { |
1767 | intvalue = d->mCompat->fixPriority(intvalue); |
1768 | } |
1769 | incidence->setPriority(intvalue); |
1770 | break; |
1771 | |
1772 | case ICAL_CATEGORIES_PROPERTY: // categories |
1773 | { |
1774 | // We have always supported multiple CATEGORIES properties per component |
1775 | // even though the RFC seems to indicate only 1 is permitted. |
1776 | // We can't change that -- in order to retain backwards compatibility. |
1777 | text = icalproperty_get_categories(p); |
1778 | const QString val = QString::fromUtf8(text); |
1779 | foreach(const QString &cat, val.split(QLatin1Char(','), QString::SkipEmptyParts)) { |
1780 | // ensure no duplicates |
1781 | if (!categories.contains(cat)) { |
1782 | categories.append(cat); |
1783 | } |
1784 | } |
1785 | break; |
1786 | } |
1787 | |
1788 | case ICAL_RECURRENCEID_PROPERTY: // recurrenceId |
1789 | kdt = readICalDateTimeProperty(p, tzlist); |
1790 | if (kdt.isValid()) { |
1791 | incidence->setRecurrenceId(kdt); |
1792 | const icalparameter *param = |
1793 | icalproperty_get_first_parameter(p, ICAL_RANGE_PARAMETER); |
1794 | if (param && icalparameter_get_range(param) == ICAL_RANGE_THISANDFUTURE) { |
1795 | incidence->setThisAndFuture(true); |
1796 | } |
1797 | } |
1798 | break; |
1799 | |
1800 | case ICAL_RRULE_PROPERTY: |
1801 | readRecurrenceRule(p, incidence); |
1802 | break; |
1803 | |
1804 | case ICAL_RDATE_PROPERTY: |
1805 | kdt = readICalDateTimeProperty(p, tzlist); |
1806 | if (kdt.isValid()) { |
1807 | if (kdt.isDateOnly()) { |
1808 | incidence->recurrence()->addRDate(kdt.date()); |
1809 | } else { |
1810 | incidence->recurrence()->addRDateTime(kdt); |
1811 | } |
1812 | } else { |
1813 | // TODO: RDates as period are not yet implemented! |
1814 | } |
1815 | break; |
1816 | |
1817 | case ICAL_EXRULE_PROPERTY: |
1818 | readExceptionRule(p, incidence); |
1819 | break; |
1820 | |
1821 | case ICAL_EXDATE_PROPERTY: |
1822 | kdt = readICalDateTimeProperty(p, tzlist); |
1823 | if (kdt.isDateOnly()) { |
1824 | incidence->recurrence()->addExDate(kdt.date()); |
1825 | } else { |
1826 | incidence->recurrence()->addExDateTime(kdt); |
1827 | } |
1828 | break; |
1829 | |
1830 | case ICAL_CLASS_PROPERTY: |
1831 | inttext = icalproperty_get_class(p); |
1832 | if (inttext == ICAL_CLASS_PUBLIC) { |
1833 | incidence->setSecrecy(Incidence::SecrecyPublic); |
1834 | } else if (inttext == ICAL_CLASS_CONFIDENTIAL) { |
1835 | incidence->setSecrecy(Incidence::SecrecyConfidential); |
1836 | } else { |
1837 | incidence->setSecrecy(Incidence::SecrecyPrivate); |
1838 | } |
1839 | break; |
1840 | |
1841 | case ICAL_ATTACH_PROPERTY: // attachments |
1842 | incidence->addAttachment(readAttachment(p)); |
1843 | break; |
1844 | |
1845 | default: |
1846 | // TODO: do something about unknown properties? |
1847 | break; |
1848 | } |
1849 | |
1850 | p = icalcomponent_get_next_property(parent, ICAL_ANY_PROPERTY); |
1851 | } |
1852 | |
1853 | // Set the scheduling ID |
1854 | const QString uid = incidence->customProperty("LIBKCAL" , "ID" ); |
1855 | if (!uid.isNull()) { |
1856 | // The UID stored in incidencebase is actually the scheduling ID |
1857 | // It has to be stored in the iCal UID component for compatibility |
1858 | // with other iCal applications |
1859 | incidence->setSchedulingID(incidence->uid(), uid); |
1860 | } |
1861 | |
1862 | // Now that recurrence and exception stuff is completely set up, |
1863 | // do any backwards compatibility adjustments. |
1864 | if (incidence->recurs() && d->mCompat) { |
1865 | d->mCompat->fixRecurrence(incidence); |
1866 | } |
1867 | |
1868 | // add categories |
1869 | incidence->setCategories(categories); |
1870 | |
1871 | // iterate through all alarms |
1872 | for (icalcomponent *alarm = icalcomponent_get_first_component(parent, ICAL_VALARM_COMPONENT); |
1873 | alarm; |
1874 | alarm = icalcomponent_get_next_component(parent, ICAL_VALARM_COMPONENT)) { |
1875 | readAlarm(alarm, incidence, tzlist); |
1876 | } |
1877 | |
1878 | if (d->mCompat) { |
1879 | // Fix incorrect alarm settings by other applications (like outloook 9) |
1880 | d->mCompat->fixAlarms(incidence); |
1881 | d->mCompat->setCreatedToDtStamp(incidence, dtstamp); |
1882 | } |
1883 | } |
1884 | |
1885 | //@cond PRIVATE |
1886 | void ICalFormatImpl::Private::readIncidenceBase(icalcomponent *parent, |
1887 | IncidenceBase::Ptr incidenceBase) |
1888 | { |
1889 | icalproperty *p = icalcomponent_get_first_property(parent, ICAL_ANY_PROPERTY); |
1890 | bool uidProcessed = false; |
1891 | while (p) { |
1892 | icalproperty_kind kind = icalproperty_isa(p); |
1893 | switch (kind) { |
1894 | case ICAL_UID_PROPERTY: // unique id |
1895 | uidProcessed = true; |
1896 | incidenceBase->setUid(QString::fromUtf8(icalproperty_get_uid(p))); |
1897 | break; |
1898 | |
1899 | case ICAL_ORGANIZER_PROPERTY: // organizer |
1900 | incidenceBase->setOrganizer(mImpl->readOrganizer(p)); |
1901 | break; |
1902 | |
1903 | case ICAL_ATTENDEE_PROPERTY: // attendee |
1904 | incidenceBase->addAttendee(mImpl->readAttendee(p)); |
1905 | break; |
1906 | |
1907 | case ICAL_COMMENT_PROPERTY: |
1908 | incidenceBase->addComment( |
1909 | QString::fromUtf8(icalproperty_get_comment(p))); |
1910 | break; |
1911 | |
1912 | case ICAL_CONTACT_PROPERTY: |
1913 | incidenceBase->addContact( |
1914 | QString::fromUtf8(icalproperty_get_contact(p))); |
1915 | break; |
1916 | |
1917 | case ICAL_URL_PROPERTY: |
1918 | incidenceBase->setUrl( |
1919 | QUrl(QString::fromUtf8(icalproperty_get_url(p)))); |
1920 | break; |
1921 | |
1922 | default: |
1923 | break; |
1924 | } |
1925 | |
1926 | p = icalcomponent_get_next_property(parent, ICAL_ANY_PROPERTY); |
1927 | } |
1928 | |
1929 | if (!uidProcessed) { |
1930 | kWarning() << "The incidence didn't have any UID! Report a bug " |
1931 | << "to the application that generated this file." |
1932 | << endl; |
1933 | |
1934 | // Our in-memory incidence has a random uid generated in Event's ctor. |
1935 | // Make it empty so it matches what's in the file: |
1936 | incidenceBase->setUid(QString()); |
1937 | |
1938 | // Otherwise, next time we read the file, this function will return |
1939 | // an event with another random uid and we will have two events in the calendar. |
1940 | } |
1941 | |
1942 | // custom properties |
1943 | readCustomProperties(parent, incidenceBase.data()); |
1944 | } |
1945 | |
1946 | void ICalFormatImpl::Private::readCustomProperties(icalcomponent *parent, |
1947 | CustomProperties *properties) |
1948 | { |
1949 | QByteArray property; |
1950 | QString value, parameters; |
1951 | icalproperty *p = icalcomponent_get_first_property(parent, ICAL_X_PROPERTY); |
1952 | icalparameter *param; |
1953 | |
1954 | while (p) { |
1955 | QString nvalue = QString::fromUtf8(icalproperty_get_x(p)); |
1956 | if (nvalue.isEmpty()) { |
1957 | icalvalue *value = icalproperty_get_value(p); |
1958 | if (icalvalue_isa(value) == ICAL_TEXT_VALUE) { |
1959 | // Calling icalvalue_get_text( value ) on a datetime value crashes. |
1960 | nvalue = QString::fromUtf8(icalvalue_get_text(value)); |
1961 | } else { |
1962 | p = icalcomponent_get_next_property(parent, ICAL_X_PROPERTY); |
1963 | continue; |
1964 | } |
1965 | } |
1966 | const char *name = icalproperty_get_x_name(p); |
1967 | QByteArray nproperty(name); |
1968 | if (property != nproperty) { |
1969 | // New property |
1970 | if (!property.isEmpty()) { |
1971 | properties->setNonKDECustomProperty(property, value, parameters); |
1972 | } |
1973 | property = name; |
1974 | value = nvalue; |
1975 | QStringList parametervalues; |
1976 | for (param = icalproperty_get_first_parameter(p, ICAL_ANY_PARAMETER); |
1977 | param; |
1978 | param = icalproperty_get_next_parameter(p, ICAL_ANY_PARAMETER)) { |
1979 | // 'c' is owned by ical library => all we need to do is just use it |
1980 | const char *c = icalparameter_as_ical_string(param); |
1981 | parametervalues.push_back(QLatin1String(c)); |
1982 | } |
1983 | parameters = parametervalues.join(QLatin1String(";" )); |
1984 | } else { |
1985 | value = value.append(QLatin1String("," )).append(nvalue); |
1986 | } |
1987 | p = icalcomponent_get_next_property(parent, ICAL_X_PROPERTY); |
1988 | } |
1989 | if (!property.isEmpty()) { |
1990 | properties->setNonKDECustomProperty(property, value, parameters); |
1991 | } |
1992 | } |
1993 | //@endcond |
1994 | |
1995 | void ICalFormatImpl::readRecurrenceRule(icalproperty *rrule, Incidence::Ptr incidence) |
1996 | { |
1997 | Recurrence *recur = incidence->recurrence(); |
1998 | |
1999 | struct icalrecurrencetype r = icalproperty_get_rrule(rrule); |
2000 | // dumpIcalRecurrence(r); |
2001 | |
2002 | RecurrenceRule *recurrule = new RecurrenceRule(/*incidence*/); |
2003 | recurrule->setStartDt(incidence->dtStart()); |
2004 | readRecurrence(r, recurrule); |
2005 | recur->addRRule(recurrule); |
2006 | } |
2007 | |
2008 | void ICalFormatImpl::readExceptionRule(icalproperty *rrule, Incidence::Ptr incidence) |
2009 | { |
2010 | struct icalrecurrencetype r = icalproperty_get_exrule(rrule); |
2011 | // dumpIcalRecurrence(r); |
2012 | |
2013 | RecurrenceRule *recurrule = new RecurrenceRule(/*incidence*/); |
2014 | recurrule->setStartDt(incidence->dtStart()); |
2015 | readRecurrence(r, recurrule); |
2016 | |
2017 | Recurrence *recur = incidence->recurrence(); |
2018 | recur->addExRule(recurrule); |
2019 | } |
2020 | |
2021 | void ICalFormatImpl::readRecurrence(const struct icalrecurrencetype &r, RecurrenceRule *recur) |
2022 | { |
2023 | // Generate the RRULE string |
2024 | recur->setRRule( |
2025 | QLatin1String(icalrecurrencetype_as_string(const_cast<struct icalrecurrencetype*>(&r)))); |
2026 | // Period |
2027 | switch (r.freq) { |
2028 | case ICAL_SECONDLY_RECURRENCE: |
2029 | recur->setRecurrenceType(RecurrenceRule::rSecondly); |
2030 | break; |
2031 | case ICAL_MINUTELY_RECURRENCE: |
2032 | recur->setRecurrenceType(RecurrenceRule::rMinutely); |
2033 | break; |
2034 | case ICAL_HOURLY_RECURRENCE: |
2035 | recur->setRecurrenceType(RecurrenceRule::rHourly); |
2036 | break; |
2037 | case ICAL_DAILY_RECURRENCE: |
2038 | recur->setRecurrenceType(RecurrenceRule::rDaily); |
2039 | break; |
2040 | case ICAL_WEEKLY_RECURRENCE: |
2041 | recur->setRecurrenceType(RecurrenceRule::rWeekly); |
2042 | break; |
2043 | case ICAL_MONTHLY_RECURRENCE: |
2044 | recur->setRecurrenceType(RecurrenceRule::rMonthly); |
2045 | break; |
2046 | case ICAL_YEARLY_RECURRENCE: |
2047 | recur->setRecurrenceType(RecurrenceRule::rYearly); |
2048 | break; |
2049 | case ICAL_NO_RECURRENCE: |
2050 | default: |
2051 | recur->setRecurrenceType(RecurrenceRule::rNone); |
2052 | } |
2053 | // Frequency |
2054 | recur->setFrequency(r.interval); |
2055 | |
2056 | // Duration & End Date |
2057 | if (!icaltime_is_null_time(r.until)) { |
2058 | icaltimetype t = r.until; |
2059 | recur->setEndDt(readICalUtcDateTime(0, t)); |
2060 | } else { |
2061 | if (r.count == 0) { |
2062 | recur->setDuration(-1); |
2063 | } else { |
2064 | recur->setDuration(r.count); |
2065 | } |
2066 | } |
2067 | |
2068 | // Week start setting |
2069 | short wkst = static_cast<short>((r.week_start + 5) % 7 + 1); |
2070 | recur->setWeekStart(wkst); |
2071 | |
2072 | // And now all BY* |
2073 | QList<int> lst; |
2074 | int i; |
2075 | int index = 0; |
2076 | |
2077 | //@cond PRIVATE |
2078 | #define readSetByList( rrulecomp, setfunc ) \ |
2079 | index = 0; \ |
2080 | lst.clear(); \ |
2081 | while ( ( i = r.rrulecomp[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { \ |
2082 | lst.append( i ); \ |
2083 | } \ |
2084 | if ( !lst.isEmpty() ) { \ |
2085 | recur->setfunc( lst ); \ |
2086 | } |
2087 | //@endcond |
2088 | |
2089 | // BYSECOND, MINUTE and HOUR, MONTHDAY, YEARDAY, WEEKNUMBER, MONTH |
2090 | // and SETPOS are standard int lists, so we can treat them with the |
2091 | // same macro |
2092 | readSetByList(by_second, setBySeconds); |
2093 | readSetByList(by_minute, setByMinutes); |
2094 | readSetByList(by_hour, setByHours); |
2095 | readSetByList(by_month_day, setByMonthDays); |
2096 | readSetByList(by_year_day, setByYearDays); |
2097 | readSetByList(by_week_no, setByWeekNumbers); |
2098 | readSetByList(by_month, setByMonths); |
2099 | readSetByList(by_set_pos, setBySetPos); |
2100 | #undef readSetByList |
2101 | |
2102 | // BYDAY is a special case, since it's not an int list |
2103 | QList<RecurrenceRule::WDayPos> wdlst; |
2104 | short day; |
2105 | index=0; |
2106 | while ((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { |
2107 | RecurrenceRule::WDayPos pos; |
2108 | pos.setDay(static_cast<short>((icalrecurrencetype_day_day_of_week(day) + 5) % 7 + 1)); |
2109 | pos.setPos(icalrecurrencetype_day_position(day)); |
2110 | wdlst.append(pos); |
2111 | } |
2112 | if (!wdlst.isEmpty()) { |
2113 | recur->setByDays(wdlst); |
2114 | } |
2115 | |
2116 | // TODO: Store all X- fields of the RRULE inside the recurrence (so they are |
2117 | // preserved |
2118 | } |
2119 | |
2120 | void ICalFormatImpl::readAlarm(icalcomponent *alarm, |
2121 | Incidence::Ptr incidence, |
2122 | ICalTimeZones *tzlist) |
2123 | { |
2124 | Alarm::Ptr ialarm = incidence->newAlarm(); |
2125 | ialarm->setRepeatCount(0); |
2126 | ialarm->setEnabled(true); |
2127 | |
2128 | // Determine the alarm's action type |
2129 | icalproperty *p = icalcomponent_get_first_property(alarm, ICAL_ACTION_PROPERTY); |
2130 | Alarm::Type type = Alarm::Display; |
2131 | icalproperty_action action = ICAL_ACTION_DISPLAY; |
2132 | if (!p) { |
2133 | kDebug() << "Unknown type of alarm, using default" ; |
2134 | // TODO: do something about unknown alarm type? |
2135 | } else { |
2136 | |
2137 | action = icalproperty_get_action(p); |
2138 | switch (action) { |
2139 | case ICAL_ACTION_DISPLAY: |
2140 | type = Alarm::Display; |
2141 | break; |
2142 | case ICAL_ACTION_AUDIO: |
2143 | type = Alarm::Audio; |
2144 | break; |
2145 | case ICAL_ACTION_PROCEDURE: |
2146 | type = Alarm::Procedure; |
2147 | break; |
2148 | case ICAL_ACTION_EMAIL: |
2149 | type = Alarm::Email; |
2150 | break; |
2151 | default: |
2152 | break; |
2153 | // TODO: do something about invalid alarm type? |
2154 | } |
2155 | } |
2156 | ialarm->setType(type); |
2157 | |
2158 | p = icalcomponent_get_first_property(alarm, ICAL_ANY_PROPERTY); |
2159 | while (p) { |
2160 | icalproperty_kind kind = icalproperty_isa(p); |
2161 | |
2162 | switch (kind) { |
2163 | case ICAL_TRIGGER_PROPERTY: |
2164 | { |
2165 | icaltriggertype trigger = icalproperty_get_trigger(p); |
2166 | if (!icaltime_is_null_time(trigger.time)) { |
2167 | //set the trigger to a specific time (which is not in rfc2445, btw) |
2168 | ialarm->setTime(readICalUtcDateTime(p, trigger.time, tzlist)); |
2169 | } else { |
2170 | //set the trigger to an offset from the incidence start or end time. |
2171 | if (!icaldurationtype_is_bad_duration(trigger.duration)) { |
2172 | Duration duration(readICalDuration(trigger.duration)); |
2173 | icalparameter *param = |
2174 | icalproperty_get_first_parameter(p, ICAL_RELATED_PARAMETER); |
2175 | if (param && icalparameter_get_related(param) == ICAL_RELATED_END) { |
2176 | ialarm->setEndOffset(duration); |
2177 | } else { |
2178 | ialarm->setStartOffset(duration); |
2179 | } |
2180 | } else { |
2181 | // a bad duration was encountered, just set a 0 duration from start |
2182 | ialarm->setStartOffset(Duration(0)); |
2183 | } |
2184 | } |
2185 | break; |
2186 | } |
2187 | case ICAL_DURATION_PROPERTY: |
2188 | { |
2189 | icaldurationtype duration = icalproperty_get_duration(p); |
2190 | ialarm->setSnoozeTime(readICalDuration(duration)); |
2191 | break; |
2192 | } |
2193 | case ICAL_REPEAT_PROPERTY: |
2194 | ialarm->setRepeatCount(icalproperty_get_repeat(p)); |
2195 | break; |
2196 | |
2197 | case ICAL_DESCRIPTION_PROPERTY: |
2198 | { // Only in DISPLAY and EMAIL and PROCEDURE alarms |
2199 | QString description = QString::fromUtf8(icalproperty_get_description(p)); |
2200 | switch (action) { |
2201 | case ICAL_ACTION_DISPLAY: |
2202 | ialarm->setText(description); |
2203 | break; |
2204 | case ICAL_ACTION_PROCEDURE: |
2205 | ialarm->setProgramArguments(description); |
2206 | break; |
2207 | case ICAL_ACTION_EMAIL: |
2208 | ialarm->setMailText(description); |
2209 | break; |
2210 | default: |
2211 | break; |
2212 | } |
2213 | break; |
2214 | } |
2215 | case ICAL_SUMMARY_PROPERTY: |
2216 | // Only in EMAIL alarm |
2217 | ialarm->setMailSubject(QString::fromUtf8(icalproperty_get_summary(p))); |
2218 | break; |
2219 | |
2220 | case ICAL_ATTENDEE_PROPERTY: |
2221 | { // Only in EMAIL alarm |
2222 | QString email = QString::fromUtf8(icalproperty_get_attendee(p)); |
2223 | if (email.startsWith(QLatin1String("mailto:" ), Qt::CaseInsensitive)) { |
2224 | email = email.mid(7); |
2225 | } |
2226 | QString name; |
2227 | icalparameter *param = icalproperty_get_first_parameter(p, ICAL_CN_PARAMETER); |
2228 | if (param) { |
2229 | name = QString::fromUtf8(icalparameter_get_cn(param)); |
2230 | } |
2231 | ialarm->addMailAddress(Person::Ptr(new Person(name, email))); |
2232 | break; |
2233 | } |
2234 | |
2235 | case ICAL_ATTACH_PROPERTY: |
2236 | { // Only in AUDIO and EMAIL and PROCEDURE alarms |
2237 | Attachment::Ptr attach = readAttachment(p); |
2238 | if (attach && attach->isUri()) { |
2239 | switch (action) { |
2240 | case ICAL_ACTION_AUDIO: |
2241 | ialarm->setAudioFile(attach->uri()); |
2242 | break; |
2243 | case ICAL_ACTION_PROCEDURE: |
2244 | ialarm->setProgramFile(attach->uri()); |
2245 | break; |
2246 | case ICAL_ACTION_EMAIL: |
2247 | ialarm->addMailAttachment(attach->uri()); |
2248 | break; |
2249 | default: |
2250 | break; |
2251 | } |
2252 | } else { |
2253 | kDebug() << "Alarm attachments currently only support URIs," |
2254 | << "but no binary data" ; |
2255 | } |
2256 | break; |
2257 | } |
2258 | default: |
2259 | break; |
2260 | } |
2261 | p = icalcomponent_get_next_property(alarm, ICAL_ANY_PROPERTY); |
2262 | } |
2263 | |
2264 | // custom properties |
2265 | d->readCustomProperties(alarm, ialarm.data()); |
2266 | |
2267 | QString locationRadius = ialarm->nonKDECustomProperty("X-LOCATION-RADIUS" ); |
2268 | if (!locationRadius.isEmpty()) { |
2269 | ialarm->setLocationRadius(locationRadius.toInt()); |
2270 | ialarm->setHasLocationRadius(true); |
2271 | } |
2272 | |
2273 | if (ialarm->customProperty(APP_NAME_FOR_XPROPERTIES, |
2274 | ENABLED_ALARM_XPROPERTY) == QLatin1String("FALSE" )) { |
2275 | ialarm->setEnabled(false); |
2276 | } |
2277 | // TODO: check for consistency of alarm properties |
2278 | } |
2279 | |
2280 | icaldatetimeperiodtype ICalFormatImpl::writeICalDatePeriod(const QDate &date) |
2281 | { |
2282 | icaldatetimeperiodtype t; |
2283 | t.time = writeICalDate(date); |
2284 | t.period = icalperiodtype_null_period(); |
2285 | return t; |
2286 | } |
2287 | |
2288 | icaltimetype ICalFormatImpl::writeICalDate(const QDate &date) |
2289 | { |
2290 | icaltimetype t = icaltime_null_time(); |
2291 | |
2292 | t.year = date.year(); |
2293 | t.month = date.month(); |
2294 | t.day = date.day(); |
2295 | |
2296 | t.hour = 0; |
2297 | t.minute = 0; |
2298 | t.second = 0; |
2299 | |
2300 | t.is_date = 1; |
2301 | t.is_utc = 0; |
2302 | t.zone = 0; |
2303 | |
2304 | return t; |
2305 | } |
2306 | |
2307 | icaltimetype ICalFormatImpl::writeICalDateTime(const KDateTime &datetime) |
2308 | { |
2309 | icaltimetype t = icaltime_null_time(); |
2310 | |
2311 | t.year = datetime.date().year(); |
2312 | t.month = datetime.date().month(); |
2313 | t.day = datetime.date().day(); |
2314 | |
2315 | t.is_date = datetime.isDateOnly() ? 1 : 0; |
2316 | |
2317 | if (!t.is_date) { |
2318 | t.hour = datetime.time().hour(); |
2319 | t.minute = datetime.time().minute(); |
2320 | t.second = datetime.time().second(); |
2321 | } |
2322 | t.zone = 0; // zone is NOT set |
2323 | t.is_utc = datetime.isUtc() ? 1 : 0; |
2324 | |
2325 | // _dumpIcaltime( t ); |
2326 | |
2327 | return t; |
2328 | } |
2329 | |
2330 | icalproperty *ICalFormatImpl::writeICalDateTimeProperty(const icalproperty_kind type, |
2331 | const KDateTime &dt, |
2332 | ICalTimeZones *tzlist, |
2333 | ICalTimeZones *tzUsedList) |
2334 | { |
2335 | icaltimetype t; |
2336 | |
2337 | switch (type) { |
2338 | case ICAL_DTSTAMP_PROPERTY: |
2339 | case ICAL_CREATED_PROPERTY: |
2340 | case ICAL_LASTMODIFIED_PROPERTY: |
2341 | t = writeICalDateTime(dt.toUtc()); |
2342 | break; |
2343 | default: |
2344 | t = writeICalDateTime(dt); |
2345 | break; |
2346 | } |
2347 | |
2348 | icalproperty *p; |
2349 | switch (type) { |
2350 | case ICAL_DTSTAMP_PROPERTY: |
2351 | p = icalproperty_new_dtstamp(t); |
2352 | break; |
2353 | case ICAL_CREATED_PROPERTY: |
2354 | p = icalproperty_new_created(t); |
2355 | break; |
2356 | case ICAL_LASTMODIFIED_PROPERTY: |
2357 | p = icalproperty_new_lastmodified(t); |
2358 | break; |
2359 | case ICAL_DTSTART_PROPERTY: // start date and time |
2360 | p = icalproperty_new_dtstart(t); |
2361 | break; |
2362 | case ICAL_DTEND_PROPERTY: // end date and time |
2363 | p = icalproperty_new_dtend(t); |
2364 | break; |
2365 | case ICAL_DUE_PROPERTY: |
2366 | p = icalproperty_new_due(t); |
2367 | break; |
2368 | case ICAL_RECURRENCEID_PROPERTY: |
2369 | p = icalproperty_new_recurrenceid(t); |
2370 | break; |
2371 | case ICAL_EXDATE_PROPERTY: |
2372 | p = icalproperty_new_exdate(t); |
2373 | break; |
2374 | case ICAL_X_PROPERTY: |
2375 | { |
2376 | p = icalproperty_new_x("" ); |
2377 | icaltimetype timeType = writeICalDateTime(dt); |
2378 | icalvalue *text = icalvalue_new_datetime(timeType); |
2379 | icalproperty_set_value(p, text); |
2380 | } |
2381 | break; |
2382 | default: |
2383 | { |
2384 | icaldatetimeperiodtype tp; |
2385 | tp.time = t; |
2386 | tp.period = icalperiodtype_null_period(); |
2387 | switch (type) { |
2388 | case ICAL_RDATE_PROPERTY: |
2389 | p = icalproperty_new_rdate(tp); |
2390 | break; |
2391 | default: |
2392 | return 0; |
2393 | } |
2394 | } |
2395 | } |
2396 | |
2397 | KTimeZone ktz; |
2398 | if (!t.is_utc) { |
2399 | ktz = dt.timeZone(); |
2400 | } |
2401 | |
2402 | if (ktz.isValid()) { |
2403 | if (tzlist) { |
2404 | ICalTimeZone tz = tzlist->zone(ktz.name()); |
2405 | if (!tz.isValid()) { |
2406 | // The time zone isn't in the list of known zones for the calendar |
2407 | // - add it to the calendar's zone list |
2408 | ICalTimeZone tznew(ktz); |
2409 | tzlist->add(tznew); |
2410 | tz = tznew; |
2411 | } |
2412 | if (tzUsedList) { |
2413 | tzUsedList->add(tz); |
2414 | } |
2415 | } |
2416 | icalproperty_add_parameter( |
2417 | p, icalparameter_new_tzid(ktz.name().toUtf8())); |
2418 | } |
2419 | return p; |
2420 | } |
2421 | |
2422 | KDateTime ICalFormatImpl::readICalDateTime(icalproperty *p, |
2423 | const icaltimetype &t, |
2424 | ICalTimeZones *tzlist, |
2425 | bool utc) |
2426 | { |
2427 | // kDebug(); |
2428 | // _dumpIcaltime( t ); |
2429 | |
2430 | KDateTime::Spec timeSpec; |
2431 | if (t.is_utc || t.zone == icaltimezone_get_utc_timezone()) { |
2432 | timeSpec = KDateTime::UTC; // the time zone is UTC |
2433 | utc = false; // no need to convert to UTC |
2434 | } else { |
2435 | if (!tzlist) { |
2436 | utc = true; // should be UTC, but it isn't |
2437 | } |
2438 | icalparameter *param = |
2439 | p ? icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) : 0; |
2440 | const char *tzid = param ? icalparameter_get_tzid(param) : 0; |
2441 | if (!tzid) { |
2442 | timeSpec = KDateTime::ClockTime; |
2443 | } else { |
2444 | QString tzidStr = QString::fromUtf8(tzid); |
2445 | ICalTimeZone tz; |
2446 | if (tzlist) { |
2447 | tz = tzlist->zone(tzidStr); |
2448 | } |
2449 | if (!tz.isValid()) { |
2450 | // The time zone is not in the existing list for the calendar. |
2451 | // Try to read it from the system or libical databases. |
2452 | ICalTimeZoneSource tzsource; |
2453 | ICalTimeZone newtz = tzsource.standardZone(tzidStr); |
2454 | if (newtz.isValid() && tzlist) { |
2455 | tzlist->add(newtz); |
2456 | } |
2457 | tz = newtz; |
2458 | } |
2459 | timeSpec = tz.isValid() ? KDateTime::Spec(tz) : KDateTime::LocalZone; |
2460 | } |
2461 | } |
2462 | KDateTime result; |
2463 | if (t.is_date) { |
2464 | result = KDateTime(QDate(t.year, t.month, t.day), timeSpec); |
2465 | } else { |
2466 | result = KDateTime(QDate(t.year, t.month, t.day), |
2467 | QTime(t.hour, t.minute, t.second), timeSpec); |
2468 | } |
2469 | return utc ? result.toUtc() : result; |
2470 | } |
2471 | |
2472 | QDate ICalFormatImpl::readICalDate(icaltimetype t) |
2473 | { |
2474 | return QDate(t.year, t.month, t.day); |
2475 | } |
2476 | |
2477 | KDateTime ICalFormatImpl::readICalDateTimeProperty(icalproperty *p, |
2478 | ICalTimeZones *tzlist, |
2479 | bool utc) |
2480 | { |
2481 | icaldatetimeperiodtype tp; |
2482 | icalproperty_kind kind = icalproperty_isa(p); |
2483 | switch (kind) { |
2484 | case ICAL_CREATED_PROPERTY: // UTC date/time |
2485 | tp.time = icalproperty_get_created(p); |
2486 | utc = true; |
2487 | break; |
2488 | case ICAL_DTSTAMP_PROPERTY: // UTC date/time |
2489 | tp.time = icalproperty_get_dtstamp(p); |
2490 | utc = true; |
2491 | break; |
2492 | case ICAL_LASTMODIFIED_PROPERTY: // last modification UTC date/time |
2493 | tp.time = icalproperty_get_lastmodified(p); |
2494 | utc = true; |
2495 | break; |
2496 | case ICAL_DTSTART_PROPERTY: // start date and time (UTC for freebusy) |
2497 | tp.time = icalproperty_get_dtstart(p); |
2498 | break; |
2499 | case ICAL_DTEND_PROPERTY: // end date and time (UTC for freebusy) |
2500 | tp.time = icalproperty_get_dtend(p); |
2501 | break; |
2502 | case ICAL_DUE_PROPERTY: // due date/time |
2503 | tp.time = icalproperty_get_due(p); |
2504 | break; |
2505 | case ICAL_COMPLETED_PROPERTY: // UTC completion date/time |
2506 | tp.time = icalproperty_get_completed(p); |
2507 | utc = true; |
2508 | break; |
2509 | case ICAL_RECURRENCEID_PROPERTY: |
2510 | tp.time = icalproperty_get_recurrenceid(p); |
2511 | break; |
2512 | case ICAL_EXDATE_PROPERTY: |
2513 | tp.time = icalproperty_get_exdate(p); |
2514 | break; |
2515 | case ICAL_X_PROPERTY: |
2516 | { |
2517 | const char *name = icalproperty_get_x_name(p); |
2518 | if (QLatin1String(name) == QLatin1String("X-KDE-LIBKCAL-DTRECURRENCE" )) { |
2519 | const char *value = icalvalue_as_ical_string(icalproperty_get_value(p)); |
2520 | icalvalue *v = icalvalue_new_from_string(ICAL_DATETIME_VALUE, value); |
2521 | tp.time = icalvalue_get_datetime(v); |
2522 | icalvalue_free(v); |
2523 | break; |
2524 | } |
2525 | } |
2526 | default: |
2527 | switch (kind) { |
2528 | case ICAL_RDATE_PROPERTY: |
2529 | tp = icalproperty_get_rdate(p); |
2530 | break; |
2531 | default: |
2532 | return KDateTime(); |
2533 | } |
2534 | if (!icaltime_is_valid_time(tp.time)) { |
2535 | return KDateTime(); // a time period was found (not implemented yet) |
2536 | } |
2537 | break; |
2538 | } |
2539 | if (tp.time.is_date) { |
2540 | return KDateTime(readICalDate(tp.time), KDateTime::Spec::ClockTime()); |
2541 | } else { |
2542 | return readICalDateTime(p, tp.time, tzlist, utc); |
2543 | } |
2544 | } |
2545 | |
2546 | icaldurationtype ICalFormatImpl::writeICalDuration(const Duration &duration) |
2547 | { |
2548 | // should be able to use icaldurationtype_from_int(), except we know |
2549 | // that some older tools do not properly support weeks. So we never |
2550 | // set a week duration, only days |
2551 | |
2552 | icaldurationtype d; |
2553 | |
2554 | int value = duration.value(); |
2555 | d.is_neg = (value < 0) ? 1 : 0; |
2556 | if (value < 0) { |
2557 | value = -value; |
2558 | } |
2559 | // RFC2445 states that an ical duration value must be |
2560 | // EITHER weeks OR days/time, not both. |
2561 | if (duration.isDaily()) { |
2562 | if (!(value % 7)) { |
2563 | d.weeks = value / 7; |
2564 | d.days = 0; |
2565 | } else { |
2566 | d.weeks = 0; |
2567 | d.days = value; |
2568 | } |
2569 | d.hours = d.minutes = d.seconds = 0; |
2570 | } else { |
2571 | if (!(value % gSecondsPerWeek)) { |
2572 | d.weeks = value / gSecondsPerWeek; |
2573 | d.days = d.hours = d.minutes = d.seconds = 0; |
2574 | } else { |
2575 | d.weeks = 0; |
2576 | d.days = value / gSecondsPerDay; |
2577 | value %= gSecondsPerDay; |
2578 | d.hours = value / gSecondsPerHour; |
2579 | value %= gSecondsPerHour; |
2580 | d.minutes = value / gSecondsPerMinute; |
2581 | value %= gSecondsPerMinute; |
2582 | d.seconds = value; |
2583 | } |
2584 | } |
2585 | |
2586 | return d; |
2587 | } |
2588 | |
2589 | Duration ICalFormatImpl::readICalDuration(icaldurationtype d) |
2590 | { |
2591 | int days = d.weeks * 7; |
2592 | days += d.days; |
2593 | int seconds = d.hours * gSecondsPerHour; |
2594 | seconds += d.minutes * gSecondsPerMinute; |
2595 | seconds += d.seconds; |
2596 | if (seconds) { |
2597 | seconds += days * gSecondsPerDay; |
2598 | if (d.is_neg) { |
2599 | seconds = -seconds; |
2600 | } |
2601 | return Duration(seconds, Duration::Seconds); |
2602 | } else { |
2603 | if (d.is_neg) { |
2604 | days = -days; |
2605 | } |
2606 | return Duration(days, Duration::Days); |
2607 | } |
2608 | } |
2609 | |
2610 | icalcomponent *ICalFormatImpl::createCalendarComponent(const Calendar::Ptr &cal) |
2611 | { |
2612 | icalcomponent *calendar; |
2613 | |
2614 | // Root component |
2615 | calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT); |
2616 | |
2617 | icalproperty *p; |
2618 | |
2619 | // Product Identifier |
2620 | p = icalproperty_new_prodid(CalFormat::productId().toUtf8()); |
2621 | icalcomponent_add_property(calendar, p); |
2622 | |
2623 | // iCalendar version (2.0) |
2624 | p = icalproperty_new_version(const_cast<char *>(_ICAL_VERSION)); |
2625 | icalcomponent_add_property(calendar, p); |
2626 | |
2627 | // Implementation Version |
2628 | p = icalproperty_new_x(_ICAL_IMPLEMENTATION_VERSION); |
2629 | icalproperty_set_x_name(p, IMPLEMENTATION_VERSION_XPROPERTY); |
2630 | icalcomponent_add_property(calendar, p); |
2631 | |
2632 | // Add time zone |
2633 | // NOTE: Commented out since relevant timezones are added by the caller. |
2634 | // Previously we got some timezones listed twice in the ical file. |
2635 | /* |
2636 | if ( cal && cal->timeZones() ) { |
2637 | const ICalTimeZones::ZoneMap zmaps = cal->timeZones()->zones(); |
2638 | for ( ICalTimeZones::ZoneMap::ConstIterator it=zmaps.constBegin(); |
2639 | it != zmaps.constEnd(); ++it ) { |
2640 | icaltimezone *icaltz = (*it).icalTimezone(); |
2641 | if ( !icaltz ) { |
2642 | kError() << "bad time zone"; |
2643 | } else { |
2644 | icalcomponent *tz = icalcomponent_new_clone( icaltimezone_get_component( icaltz ) ); |
2645 | icalcomponent_add_component( calendar, tz ); |
2646 | icaltimezone_free( icaltz, 1 ); |
2647 | } |
2648 | } |
2649 | } |
2650 | */ |
2651 | // Custom properties |
2652 | if (cal != 0) { |
2653 | d->writeCustomProperties(calendar, cal.data()); |
2654 | } |
2655 | |
2656 | return calendar; |
2657 | } |
2658 | |
2659 | // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. |
2660 | // and break it down from its tree-like format into the dictionary format |
2661 | // that is used internally in the ICalFormatImpl. |
2662 | bool ICalFormatImpl::populate(const Calendar::Ptr &cal, icalcomponent *calendar, |
2663 | bool deleted, const QString ¬ebook) |
2664 | { |
2665 | Q_UNUSED(notebook); |
2666 | |
2667 | // kDebug()<<"Populate called"; |
2668 | |
2669 | // this function will populate the caldict dictionary and other event |
2670 | // lists. It turns vevents into Events and then inserts them. |
2671 | |
2672 | if (!calendar) { |
2673 | kWarning() << "Populate called with empty calendar" ; |
2674 | return false; |
2675 | } |
2676 | |
2677 | // TODO: check for METHOD |
2678 | |
2679 | icalproperty *p; |
2680 | |
2681 | p = icalcomponent_get_first_property(calendar, ICAL_X_PROPERTY); |
2682 | QString implementationVersion; |
2683 | |
2684 | while (p) { |
2685 | const char *name = icalproperty_get_x_name(p); |
2686 | QByteArray nproperty(name); |
2687 | if (nproperty == QByteArray(IMPLEMENTATION_VERSION_XPROPERTY)) { |
2688 | QString nvalue = QString::fromUtf8(icalproperty_get_x(p)); |
2689 | if (nvalue.isEmpty()) { |
2690 | icalvalue *value = icalproperty_get_value(p); |
2691 | if (icalvalue_isa(value) == ICAL_TEXT_VALUE) { |
2692 | nvalue = QString::fromUtf8(icalvalue_get_text(value)); |
2693 | } |
2694 | } |
2695 | implementationVersion = nvalue; |
2696 | icalcomponent_remove_property(calendar, p); |
2697 | icalproperty_free(p); |
2698 | } |
2699 | p = icalcomponent_get_next_property(calendar, ICAL_X_PROPERTY); |
2700 | } |
2701 | |
2702 | p = icalcomponent_get_first_property(calendar, ICAL_PRODID_PROPERTY); |
2703 | if (!p) { |
2704 | kDebug() << "No PRODID property found" ; |
2705 | d->mLoadedProductId = QLatin1String("" ); |
2706 | } else { |
2707 | d->mLoadedProductId = QString::fromUtf8(icalproperty_get_prodid(p)); |
2708 | |
2709 | delete d->mCompat; |
2710 | d->mCompat = CompatFactory::createCompat(d->mLoadedProductId, implementationVersion); |
2711 | } |
2712 | |
2713 | p = icalcomponent_get_first_property(calendar, ICAL_VERSION_PROPERTY); |
2714 | if (!p) { |
2715 | kDebug() << "No VERSION property found" ; |
2716 | d->mParent->setException(new Exception(Exception::CalVersionUnknown)); |
2717 | return false; |
2718 | } else { |
2719 | const char *version = icalproperty_get_version(p); |
2720 | if (!version) { |
2721 | kDebug() << "No VERSION property found" ; |
2722 | d->mParent->setException(new Exception(Exception::VersionPropertyMissing)); |
2723 | |
2724 | return false; |
2725 | } |
2726 | if (strcmp(version, "1.0" ) == 0) { |
2727 | kDebug() << "Expected iCalendar, got vCalendar" ; |
2728 | d->mParent->setException(new Exception(Exception::CalVersion1)); |
2729 | return false; |
2730 | } else if (strcmp(version, "2.0" ) != 0) { |
2731 | kDebug() << "Expected iCalendar, got unknown format" ; |
2732 | d->mParent->setException(new Exception( |
2733 | Exception::CalVersionUnknown)); |
2734 | return false; |
2735 | } |
2736 | } |
2737 | |
2738 | // Populate the calendar's time zone collection with all VTIMEZONE components |
2739 | ICalTimeZones *tzlist = cal->timeZones(); |
2740 | ICalTimeZoneSource tzs; |
2741 | tzs.parse(calendar, *tzlist); |
2742 | |
2743 | // custom properties |
2744 | d->readCustomProperties(calendar, cal.data()); |
2745 | |
2746 | // Store all events with a relatedTo property in a list for post-processing |
2747 | d->mEventsRelate.clear(); |
2748 | d->mTodosRelate.clear(); |
2749 | // TODO: make sure that only actually added events go to this lists. |
2750 | |
2751 | icalcomponent *c; |
2752 | |
2753 | c = icalcomponent_get_first_component(calendar, ICAL_VTODO_COMPONENT); |
2754 | while (c) { |
2755 | Todo::Ptr todo = readTodo(c, tzlist); |
2756 | if (todo) { |
2757 | // kDebug() << "todo is not zero and deleted is " << deleted; |
2758 | Todo::Ptr old = cal->todo(todo->uid(), todo->recurrenceId()); |
2759 | if (old) { |
2760 | if (old->uid().isEmpty()) { |
2761 | kWarning() << "Skipping invalid VTODO" ; |
2762 | c = icalcomponent_get_next_component(calendar, ICAL_VTODO_COMPONENT); |
2763 | continue; |
2764 | } |
2765 | // kDebug() << "Found an old todo with uid " << old->uid(); |
2766 | if (deleted) { |
2767 | // kDebug() << "Todo " << todo->uid() << " already deleted"; |
2768 | cal->deleteTodo(old); // move old to deleted |
2769 | removeAllICal(d->mTodosRelate, old); |
2770 | } else if (todo->revision() > old->revision()) { |
2771 | // kDebug() << "Replacing old todo " << old.data() << " with this one " << todo.data(); |
2772 | cal->deleteTodo(old); // move old to deleted |
2773 | removeAllICal(d->mTodosRelate, old); |
2774 | cal->addTodo(todo); // and replace it with this one |
2775 | } |
2776 | } else if (deleted) { |
2777 | // kDebug() << "Todo " << todo->uid() << " already deleted"; |
2778 | old = cal->deletedTodo(todo->uid(), todo->recurrenceId()); |
2779 | if (!old) { |
2780 | cal->addTodo(todo); // add this one |
2781 | cal->deleteTodo(todo); // and move it to deleted |
2782 | } |
2783 | } else { |
2784 | // kDebug() << "Adding todo " << todo.data() << todo->uid(); |
2785 | cal->addTodo(todo); // just add this one |
2786 | } |
2787 | } |
2788 | c = icalcomponent_get_next_component(calendar, ICAL_VTODO_COMPONENT); |
2789 | } |
2790 | |
2791 | // Iterate through all events |
2792 | c = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT); |
2793 | while (c) { |
2794 | Event::Ptr event = readEvent(c, tzlist); |
2795 | if (event) { |
2796 | // kDebug() << "event is not zero and deleted is " << deleted; |
2797 | Event::Ptr old = cal->event(event->uid(), event->recurrenceId()); |
2798 | if (old) { |
2799 | if (old->uid().isEmpty()) { |
2800 | kWarning() << "Skipping invalid VEVENT" ; |
2801 | c = icalcomponent_get_next_component(calendar, ICAL_VEVENT_COMPONENT); |
2802 | continue; |
2803 | } |
2804 | // kDebug() << "Found an old event with uid " << old->uid(); |
2805 | if (deleted) { |
2806 | // kDebug() << "Event " << event->uid() << " already deleted"; |
2807 | cal->deleteEvent(old); // move old to deleted |
2808 | removeAllICal(d->mEventsRelate, old); |
2809 | } else if (event->revision() > old->revision()) { |
2810 | // kDebug() << "Replacing old event " << old.data() << " with this one " << event.data(); |
2811 | cal->deleteEvent(old); // move old to deleted |
2812 | removeAllICal(d->mEventsRelate, old); |
2813 | cal->addEvent(event); // and replace it with this one |
2814 | } |
2815 | } else if (deleted) { |
2816 | // kDebug() << "Event " << event->uid() << " already deleted"; |
2817 | old = cal->deletedEvent(event->uid(), event->recurrenceId()); |
2818 | if (!old) { |
2819 | cal->addEvent(event); // add this one |
2820 | cal->deleteEvent(event); // and move it to deleted |
2821 | } |
2822 | } else { |
2823 | // kDebug() << "Adding event " << event.data() << event->uid(); |
2824 | cal->addEvent(event); // just add this one |
2825 | } |
2826 | } |
2827 | c = icalcomponent_get_next_component(calendar, ICAL_VEVENT_COMPONENT); |
2828 | } |
2829 | |
2830 | // Iterate through all journals |
2831 | c = icalcomponent_get_first_component(calendar, ICAL_VJOURNAL_COMPONENT); |
2832 | while (c) { |
2833 | Journal::Ptr journal = readJournal(c, tzlist); |
2834 | if (journal) { |
2835 | Journal::Ptr old = cal->journal(journal->uid(), journal->recurrenceId()); |
2836 | if (old) { |
2837 | if (deleted) { |
2838 | cal->deleteJournal(old); // move old to deleted |
2839 | } else if (journal->revision() > old->revision()) { |
2840 | cal->deleteJournal(old); // move old to deleted |
2841 | cal->addJournal(journal); // and replace it with this one |
2842 | } |
2843 | } else if (deleted) { |
2844 | old = cal->deletedJournal(journal->uid(), journal->recurrenceId()); |
2845 | if (!old) { |
2846 | cal->addJournal(journal); // add this one |
2847 | cal->deleteJournal(journal); // and move it to deleted |
2848 | } |
2849 | } else { |
2850 | cal->addJournal(journal); // just add this one |
2851 | } |
2852 | } |
2853 | c = icalcomponent_get_next_component(calendar, ICAL_VJOURNAL_COMPONENT); |
2854 | } |
2855 | |
2856 | // TODO: Remove any previous time zones no longer referenced in the calendar |
2857 | |
2858 | return true; |
2859 | } |
2860 | |
2861 | QString ICalFormatImpl::(icalcomponent *c) |
2862 | { |
2863 | QString errorMessage; |
2864 | |
2865 | icalproperty *error; |
2866 | error = icalcomponent_get_first_property(c, ICAL_XLICERROR_PROPERTY); |
2867 | while (error) { |
2868 | errorMessage += QLatin1String(icalproperty_get_xlicerror(error)); |
2869 | errorMessage += QLatin1Char('\n'); |
2870 | error = icalcomponent_get_next_property(c, ICAL_XLICERROR_PROPERTY); |
2871 | } |
2872 | |
2873 | return errorMessage; |
2874 | } |
2875 | |
2876 | /* |
2877 | void ICalFormatImpl::dumpIcalRecurrence( const icalrecurrencetype &r ) |
2878 | { |
2879 | int i; |
2880 | |
2881 | kDebug() << " Freq:" << int( r.freq ); |
2882 | kDebug() << " Until:" << icaltime_as_ical_string( r.until ); |
2883 | kDebug() << " Count:" << r.count; |
2884 | if ( r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { |
2885 | int index = 0; |
2886 | QString out = " By Day: "; |
2887 | while ( ( i = r.by_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { |
2888 | out.append( QString::number( i ) + ' ' ); |
2889 | } |
2890 | kDebug() << out; |
2891 | } |
2892 | if ( r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { |
2893 | int index = 0; |
2894 | QString out = " By Month Day: "; |
2895 | while ( ( i = r.by_month_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { |
2896 | out.append( QString::number( i ) + ' ' ); |
2897 | } |
2898 | kDebug() << out; |
2899 | } |
2900 | if ( r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { |
2901 | int index = 0; |
2902 | QString out = " By Year Day: "; |
2903 | while ( ( i = r.by_year_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { |
2904 | out.append( QString::number( i ) + ' ' ); |
2905 | } |
2906 | kDebug() << out; |
2907 | } |
2908 | if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX ) { |
2909 | int index = 0; |
2910 | QString out = " By Month: "; |
2911 | while ( ( i = r.by_month[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { |
2912 | out.append( QString::number( i ) + ' ' ); |
2913 | } |
2914 | kDebug() << out; |
2915 | } |
2916 | if ( r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX ) { |
2917 | int index = 0; |
2918 | QString out = " By Set Pos: "; |
2919 | while ( ( i = r.by_set_pos[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { |
2920 | kDebug() << "=========" << i; |
2921 | out.append( QString::number( i ) + ' ' ); |
2922 | } |
2923 | kDebug() << out; |
2924 | } |
2925 | } |
2926 | */ |
2927 | |
2928 | icalcomponent *ICalFormatImpl::createScheduleComponent(const IncidenceBase::Ptr &incidence, |
2929 | iTIPMethod method) |
2930 | { |
2931 | icalcomponent *message = createCalendarComponent(); |
2932 | |
2933 | // Create VTIMEZONE components for this incidence |
2934 | ICalTimeZones zones; |
2935 | if (incidence) { |
2936 | const KDateTime kd1 = incidence->dateTime(IncidenceBase::RoleStartTimeZone); |
2937 | const KDateTime kd2 = incidence->dateTime(IncidenceBase::RoleEndTimeZone); |
2938 | |
2939 | if (kd1.isValid() && kd1.timeZone() != KTimeZone::utc()) { |
2940 | zones.add(ICalTimeZone(kd1.timeZone())); |
2941 | } |
2942 | |
2943 | if (kd2.isValid() && kd2.timeZone() != KTimeZone::utc()) { |
2944 | zones.add(ICalTimeZone(kd2.timeZone())); |
2945 | } |
2946 | |
2947 | const ICalTimeZones::ZoneMap zmaps = zones.zones(); |
2948 | for (ICalTimeZones::ZoneMap::ConstIterator it=zmaps.constBegin(); |
2949 | it != zmaps.constEnd(); ++it) { |
2950 | icaltimezone *icaltz = (*it).icalTimezone(); |
2951 | if (!icaltz) { |
2952 | kError() << "bad time zone" ; |
2953 | } else { |
2954 | icalcomponent *tz = icalcomponent_new_clone(icaltimezone_get_component(icaltz)); |
2955 | icalcomponent_add_component(message, tz); |
2956 | icaltimezone_free(icaltz, 1); |
2957 | } |
2958 | } |
2959 | } else { |
2960 | kDebug() << "No incidence" ; |
2961 | return message; |
2962 | } |
2963 | |
2964 | icalproperty_method icalmethod = ICAL_METHOD_NONE; |
2965 | |
2966 | switch (method) { |
2967 | case iTIPPublish: |
2968 | icalmethod = ICAL_METHOD_PUBLISH; |
2969 | break; |
2970 | case iTIPRequest: |
2971 | icalmethod = ICAL_METHOD_REQUEST; |
2972 | break; |
2973 | case iTIPRefresh: |
2974 | icalmethod = ICAL_METHOD_REFRESH; |
2975 | break; |
2976 | case iTIPCancel: |
2977 | icalmethod = ICAL_METHOD_CANCEL; |
2978 | break; |
2979 | case iTIPAdd: |
2980 | icalmethod = ICAL_METHOD_ADD; |
2981 | break; |
2982 | case iTIPReply: |
2983 | icalmethod = ICAL_METHOD_REPLY; |
2984 | break; |
2985 | case iTIPCounter: |
2986 | icalmethod = ICAL_METHOD_COUNTER; |
2987 | break; |
2988 | case iTIPDeclineCounter: |
2989 | icalmethod = ICAL_METHOD_DECLINECOUNTER; |
2990 | break; |
2991 | default: |
2992 | kDebug() << "Unknown method" ; |
2993 | return message; |
2994 | } |
2995 | |
2996 | icalcomponent_add_property(message, icalproperty_new_method(icalmethod)); |
2997 | |
2998 | icalcomponent *inc = writeIncidence(incidence, method); |
2999 | |
3000 | if (method != KCalCore::iTIPNoMethod) { |
3001 | //Not very nice, but since dtstamp changes semantics if used in scheduling, we have to adapt |
3002 | icalcomponent_set_dtstamp( |
3003 | inc, writeICalUtcDateTime(KDateTime::currentUtcDateTime())); |
3004 | } |
3005 | |
3006 | /* |
3007 | * RFC 2446 states in section 3.4.3 ( REPLY to a VTODO ), that |
3008 | * a REQUEST-STATUS property has to be present. For the other two, event and |
3009 | * free busy, it can be there, but is optional. Until we do more |
3010 | * fine grained handling, assume all is well. Note that this is the |
3011 | * status of the _request_, not the attendee. Just to avoid confusion. |
3012 | * - till |
3013 | */ |
3014 | if (icalmethod == ICAL_METHOD_REPLY) { |
3015 | struct icalreqstattype rst; |
3016 | rst.code = ICAL_2_0_SUCCESS_STATUS; |
3017 | rst.desc = 0; |
3018 | rst.debug = 0; |
3019 | icalcomponent_add_property(inc, icalproperty_new_requeststatus(rst)); |
3020 | } |
3021 | icalcomponent_add_component(message, inc); |
3022 | |
3023 | return message; |
3024 | } |
3025 | |