1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #include "nsStringStream.h"
5 #include "nsComponentManagerUtils.h"
6
7 #include "calICSService.h"
8 #include "calTimezone.h"
9 #include "calDateTime.h"
10 #include "calDuration.h"
11 #include "calIErrors.h"
12 #include "calUtils.h"
13
14 extern "C" {
15 #include "ical.h"
16 }
17
~calIcalProperty()18 calIcalProperty::~calIcalProperty() {
19 if (!mParent) {
20 icalproperty_free(mProperty);
21 }
22 }
23
24 NS_IMPL_CLASSINFO(calIcalProperty, nullptr, 0, CAL_ICALPROPERTY_CID)
NS_IMPL_ISUPPORTS_CI(calIcalProperty,calIIcalProperty,calIIcalPropertyLibical)25 NS_IMPL_ISUPPORTS_CI(calIcalProperty, calIIcalProperty, calIIcalPropertyLibical)
26
27 NS_IMETHODIMP_(icalproperty*)
28 calIcalProperty::GetLibicalProperty() { return mProperty; }
29
NS_IMETHODIMP_(icalcomponent *)30 NS_IMETHODIMP_(icalcomponent*)
31 calIcalProperty::GetLibicalComponent() {
32 return mParent->GetLibicalComponent();
33 }
34
35 NS_IMETHODIMP
GetIcalString(nsACString & str)36 calIcalProperty::GetIcalString(nsACString& str) {
37 char const* icalstr = icalproperty_as_ical_string(mProperty);
38 if (icalstr == 0) {
39 #ifdef DEBUG
40 fprintf(stderr, "Error getting ical string: %d (%s)\n", icalerrno,
41 icalerror_strerror(icalerrno));
42 #endif
43 return static_cast<nsresult>(calIErrors::ICS_ERROR_BASE + icalerrno);
44 }
45 str.Assign(icalstr);
46 return NS_OK;
47 }
48
49 NS_IMETHODIMP
ToString(nsACString & aResult)50 calIcalProperty::ToString(nsACString& aResult) {
51 return GetIcalString(aResult);
52 }
53
54 NS_IMETHODIMP
GetValue(nsACString & str)55 calIcalProperty::GetValue(nsACString& str) {
56 icalvalue* value = icalproperty_get_value(mProperty);
57 icalvalue_kind valuekind = icalvalue_isa(value);
58
59 const char* icalstr;
60 if (valuekind == ICAL_TEXT_VALUE) {
61 icalstr = icalvalue_get_text(value);
62 } else if (valuekind == ICAL_X_VALUE) {
63 icalstr = icalvalue_get_x(value);
64 } else if (valuekind == ICAL_ATTACH_VALUE) {
65 icalattach* attach = icalvalue_get_attach(value);
66 if (icalattach_get_is_url(attach)) {
67 icalstr = icalattach_get_url(attach);
68 } else {
69 icalstr = (const char*)icalattach_get_data(attach);
70 }
71 } else {
72 icalstr = icalproperty_get_value_as_string(mProperty);
73 }
74
75 if (!icalstr) {
76 if (icalerrno == ICAL_BADARG_ERROR) {
77 str.Truncate();
78 // Set string to null, because we don't have a value
79 // (which is something different then an empty value)
80 str.SetIsVoid(true);
81 return NS_OK;
82 }
83
84 if (icalerrno == ICAL_NO_ERROR) {
85 str.Truncate();
86 return NS_OK;
87 }
88
89 #ifdef DEBUG
90 fprintf(stderr, "Error getting string value: %d (%s)\n", icalerrno,
91 icalerror_strerror(icalerrno));
92 #endif
93 return NS_ERROR_FAILURE;
94 }
95
96 str.Assign(icalstr);
97 return NS_OK;
98 }
99
100 NS_IMETHODIMP
SetValue(const nsACString & str)101 calIcalProperty::SetValue(const nsACString& str) {
102 icalvalue_kind kind =
103 icalproperty_kind_to_value_kind(icalproperty_isa(mProperty));
104 if (kind == ICAL_TEXT_VALUE) {
105 icalvalue* v = icalvalue_new_text(PromiseFlatCString(str).get());
106 icalproperty_set_value(mProperty, v);
107 } else if (kind == ICAL_X_VALUE) {
108 icalvalue* v = icalvalue_new_x(PromiseFlatCString(str).get());
109 icalproperty_set_value(mProperty, v);
110 } else if (kind == ICAL_ATTACH_VALUE) {
111 icalattach* v = icalattach_new_from_data(PromiseFlatCString(str).get(),
112 nullptr, nullptr);
113 icalproperty_set_attach(mProperty, v);
114 } else {
115 icalproperty_set_value_from_string(mProperty, PromiseFlatCString(str).get(),
116 icalvalue_kind_to_string(kind));
117 }
118 return NS_OK;
119 }
120
121 NS_IMETHODIMP
GetValueAsIcalString(nsACString & str)122 calIcalProperty::GetValueAsIcalString(nsACString& str) {
123 const char* icalstr = icalproperty_get_value_as_string(mProperty);
124 if (!icalstr) {
125 if (icalerrno == ICAL_BADARG_ERROR) {
126 str.Truncate();
127 // Set string to null, because we don't have a value
128 // (which is something different then an empty value)
129 str.SetIsVoid(true);
130 return NS_OK;
131 }
132
133 #ifdef DEBUG
134 fprintf(stderr, "Error getting string value: %d (%s)\n", icalerrno,
135 icalerror_strerror(icalerrno));
136 #endif
137 return NS_ERROR_FAILURE;
138 }
139
140 str.Assign(icalstr);
141 return NS_OK;
142 }
143
144 NS_IMETHODIMP
SetValueAsIcalString(const nsACString & str)145 calIcalProperty::SetValueAsIcalString(const nsACString& str) {
146 const char* kindstr = icalvalue_kind_to_string(
147 icalproperty_kind_to_value_kind(icalproperty_isa(mProperty)));
148 icalproperty_set_value_from_string(mProperty, PromiseFlatCString(str).get(),
149 kindstr);
150 return NS_OK;
151 }
152
153 NS_IMETHODIMP
GetPropertyName(nsACString & name)154 calIcalProperty::GetPropertyName(nsACString& name) {
155 const char* icalstr = icalproperty_get_property_name(mProperty);
156 if (!icalstr) {
157 #ifdef DEBUG
158 fprintf(stderr, "Error getting property name: %d (%s)\n", icalerrno,
159 icalerror_strerror(icalerrno));
160 #endif
161 return NS_ERROR_FAILURE;
162 }
163 name.Assign(icalstr);
164 return NS_OK;
165 }
166
FindParameter(icalproperty * prop,const nsACString & param,icalparameter_kind kind)167 static icalparameter* FindParameter(icalproperty* prop, const nsACString& param,
168 icalparameter_kind kind) {
169 for (icalparameter* icalparam = icalproperty_get_first_parameter(prop, kind);
170 icalparam; icalparam = icalproperty_get_next_parameter(prop, kind)) {
171 if (param.Equals(icalparameter_get_xname(icalparam))) return icalparam;
172 }
173 return nullptr;
174 }
175
176 NS_IMETHODIMP
GetParameter(const nsACString & param,nsACString & value)177 calIcalProperty::GetParameter(const nsACString& param, nsACString& value) {
178 // More ridiculous parameter/X-PARAMETER handling.
179 icalparameter_kind paramkind =
180 icalparameter_string_to_kind(PromiseFlatCString(param).get());
181
182 if (paramkind == ICAL_NO_PARAMETER) return NS_ERROR_INVALID_ARG;
183
184 const char* icalstr = nullptr;
185 if (paramkind == ICAL_X_PARAMETER) {
186 icalparameter* icalparam =
187 FindParameter(mProperty, param, ICAL_X_PARAMETER);
188 if (icalparam) icalstr = icalparameter_get_xvalue(icalparam);
189 } else if (paramkind == ICAL_IANA_PARAMETER) {
190 icalparameter* icalparam =
191 FindParameter(mProperty, param, ICAL_IANA_PARAMETER);
192 if (icalparam) icalstr = icalparameter_get_iana_value(icalparam);
193 } else {
194 icalstr = icalproperty_get_parameter_as_string(
195 mProperty, PromiseFlatCString(param).get());
196 }
197
198 if (!icalstr) {
199 value.Truncate();
200 value.SetIsVoid(true);
201 } else {
202 value.Assign(icalstr);
203 }
204 return NS_OK;
205 }
206
207 NS_IMETHODIMP
SetParameter(const nsACString & param,const nsACString & value)208 calIcalProperty::SetParameter(const nsACString& param,
209 const nsACString& value) {
210 icalparameter_kind paramkind =
211 icalparameter_string_to_kind(PromiseFlatCString(param).get());
212
213 if (paramkind == ICAL_NO_PARAMETER) return NS_ERROR_INVALID_ARG;
214
215 // Because libical's support for manipulating parameters is weak, and
216 // X-PARAMETERS doubly so, we walk the list looking for an existing one of
217 // that name, and reset its value if found.
218 if (paramkind == ICAL_X_PARAMETER) {
219 icalparameter* icalparam =
220 FindParameter(mProperty, param, ICAL_X_PARAMETER);
221 if (icalparam) {
222 icalparameter_set_xvalue(icalparam, PromiseFlatCString(value).get());
223 return NS_OK;
224 }
225 // If not found, fall through to adding a new parameter below.
226 } else if (paramkind == ICAL_IANA_PARAMETER) {
227 icalparameter* icalparam =
228 FindParameter(mProperty, param, ICAL_IANA_PARAMETER);
229 if (icalparam) {
230 icalparameter_set_iana_value(icalparam, PromiseFlatCString(value).get());
231 return NS_OK;
232 }
233 // If not found, fall through to adding a new parameter below.
234 } else {
235 // We could try getting an existing parameter here and resetting its
236 // value, but this is easier and I don't care that much about parameter
237 // performance at this point.
238 RemoveParameter(param);
239 }
240
241 icalparameter* icalparam = icalparameter_new_from_value_string(
242 paramkind, PromiseFlatCString(value).get());
243 if (!icalparam) return NS_ERROR_OUT_OF_MEMORY;
244
245 // You might ask me "why does libical not do this for us?" and I would
246 // just nod knowingly but sadly at you in return.
247 //
248 // You might also, if you were not too distracted by the first question,
249 // ask why we have icalproperty_set_x_name but icalparameter_set_xname.
250 // More nodding would ensue.
251 if (paramkind == ICAL_X_PARAMETER)
252 icalparameter_set_xname(icalparam, PromiseFlatCString(param).get());
253 else if (paramkind == ICAL_IANA_PARAMETER)
254 icalparameter_set_iana_name(icalparam, PromiseFlatCString(param).get());
255
256 icalproperty_add_parameter(mProperty, icalparam);
257 // XXX check ical errno
258 return NS_OK;
259 }
260
FillParameterName(icalparameter * icalparam,nsACString & name)261 static nsresult FillParameterName(icalparameter* icalparam, nsACString& name) {
262 const char* propname = nullptr;
263 if (icalparam) {
264 icalparameter_kind paramkind = icalparameter_isa(icalparam);
265 if (paramkind == ICAL_X_PARAMETER)
266 propname = icalparameter_get_xname(icalparam);
267 else if (paramkind == ICAL_IANA_PARAMETER)
268 propname = icalparameter_get_iana_name(icalparam);
269 else if (paramkind != ICAL_NO_PARAMETER)
270 propname = icalparameter_kind_to_string(paramkind);
271 }
272
273 if (propname) {
274 name.Assign(propname);
275 } else {
276 name.Truncate();
277 name.SetIsVoid(true);
278 }
279
280 return NS_OK;
281 }
282
283 NS_IMETHODIMP
GetFirstParameterName(nsACString & name)284 calIcalProperty::GetFirstParameterName(nsACString& name) {
285 icalparameter* icalparam =
286 icalproperty_get_first_parameter(mProperty, ICAL_ANY_PARAMETER);
287 return FillParameterName(icalparam, name);
288 }
289
290 NS_IMETHODIMP
GetNextParameterName(nsACString & name)291 calIcalProperty::GetNextParameterName(nsACString& name) {
292 icalparameter* icalparam =
293 icalproperty_get_next_parameter(mProperty, ICAL_ANY_PARAMETER);
294 return FillParameterName(icalparam, name);
295 }
296
297 NS_IMETHODIMP
RemoveParameter(const nsACString & param)298 calIcalProperty::RemoveParameter(const nsACString& param) {
299 icalproperty_remove_parameter_by_name(mProperty,
300 PromiseFlatCString(param).get());
301 // XXX check ical errno
302 return NS_OK;
303 }
304
305 NS_IMETHODIMP
ClearXParameters()306 calIcalProperty::ClearXParameters() {
307 int oldcount, paramcount = 0;
308 do {
309 oldcount = paramcount;
310 icalproperty_remove_parameter(mProperty, ICAL_X_PARAMETER);
311 paramcount = icalproperty_count_parameters(mProperty);
312 } while (oldcount != paramcount);
313 return NS_OK;
314 }
315
316 NS_IMETHODIMP
GetValueAsDatetime(calIDateTime ** dtp)317 calIcalProperty::GetValueAsDatetime(calIDateTime** dtp) {
318 NS_ENSURE_ARG_POINTER(dtp);
319 return getDatetime_(toIcalComponent(mParent), mProperty, dtp);
320 }
321
getDatetime_(calIcalComponent * parent,icalproperty * prop,calIDateTime ** dtp)322 nsresult calIcalProperty::getDatetime_(calIcalComponent* parent,
323 icalproperty* prop, calIDateTime** dtp) {
324 icalvalue* const val = icalproperty_get_value(prop);
325 icalvalue_kind const valkind = icalvalue_isa(val);
326 if (valkind != ICAL_DATETIME_VALUE && valkind != ICAL_DATE_VALUE) {
327 return NS_ERROR_UNEXPECTED;
328 }
329 icaltimetype itt = icalvalue_get_datetime(val);
330
331 char const* tzid_ = nullptr;
332 if (!itt.is_utc) {
333 if (itt.zone) {
334 tzid_ = icaltimezone_get_tzid(const_cast<icaltimezone*>(itt.zone));
335 } else {
336 // Need to get the tzid param. Unfortunately, libical tends to return raw
337 // ics strings, with quotes and everything. That's not what we want. Need
338 // to work around.
339 icalparameter* const tzparam =
340 icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER);
341 if (tzparam) {
342 tzid_ = icalparameter_get_xvalue(tzparam);
343 }
344 }
345 }
346
347 nsCOMPtr<calITimezone> tz;
348 if (tzid_) {
349 nsDependentCString const tzid(tzid_);
350 calIcalComponent* comp = nullptr;
351 if (parent) {
352 comp = parent->getParentVCalendarOrThis();
353 }
354 // look up parent if timezone is already referenced:
355 if (comp) {
356 comp->mReferencedTimezones.Get(tzid, getter_AddRefs(tz));
357 }
358 if (!tz) {
359 if (parent) {
360 // passed tz provider has precedence over timezone service:
361 calITimezoneProvider* const tzProvider = parent->getTzProvider();
362 if (tzProvider) {
363 tzProvider->GetTimezone(tzid, getter_AddRefs(tz));
364 NS_ASSERTION(tz, tzid_);
365 }
366 }
367 if (!tz) {
368 // look up tz in tz service.
369 // this hides errors from incorrect ics files, which could state
370 // a TZID that is not present in the ics file.
371 // The other way round, it makes this product more error tolerant.
372 nsresult rv =
373 cal::getTimezoneService()->GetTimezone(tzid, getter_AddRefs(tz));
374
375 if (NS_FAILED(rv) || !tz) {
376 icaltimezone const* zone = itt.zone;
377 if (!zone && comp) {
378 // look up parent VCALENDAR for VTIMEZONE:
379 zone = icalcomponent_get_timezone(comp->mComponent, tzid_);
380 NS_ASSERTION(zone, tzid_);
381 }
382 if (zone) {
383 // We need to decouple this (inner) VTIMEZONE from the parent
384 // VCALENDAR to avoid running into circular references (referenced
385 // timezones):
386 icaltimezone* const clonedZone = icaltimezone_new();
387 CAL_ENSURE_MEMORY(clonedZone);
388 icalcomponent* const clonedZoneComp = icalcomponent_new_clone(
389 icaltimezone_get_component(const_cast<icaltimezone*>(zone)));
390 if (!clonedZoneComp) {
391 icaltimezone_free(clonedZone, 1 /* free struct */);
392 CAL_ENSURE_MEMORY(clonedZoneComp);
393 }
394 if (!icaltimezone_set_component(clonedZone, clonedZoneComp)) {
395 icaltimezone_free(clonedZone, 1 /* free struct */);
396 return NS_ERROR_INVALID_ARG;
397 }
398 nsCOMPtr<calIIcalComponent> const tzComp(
399 new calIcalComponent(clonedZone, clonedZoneComp));
400 CAL_ENSURE_MEMORY(tzComp);
401 tz = new calTimezone(tzid, tzComp);
402 CAL_ENSURE_MEMORY(tz);
403 } else { // install phantom timezone, so the data could be repaired:
404 tz = new calTimezone(tzid, nullptr);
405 CAL_ENSURE_MEMORY(tz);
406 }
407 }
408 }
409 if (comp && tz) {
410 // assure timezone is known:
411 comp->AddTimezoneReference(tz);
412 }
413 }
414 if (tz) {
415 // correct itt which would else appear floating:
416 itt.zone = cal::getIcalTimezone(tz);
417 itt.is_utc = 0;
418 } else {
419 cal::logMissingTimezone(tzid_);
420 }
421 }
422 *dtp = new calDateTime(&itt, tz);
423 CAL_ENSURE_MEMORY(*dtp);
424 NS_ADDREF(*dtp);
425 return NS_OK;
426 }
427
~calIcalComponent()428 calIcalComponent::~calIcalComponent() {
429 if (!mParent) {
430 // We free either a plain icalcomponent or a icaltimezone.
431 // In the latter case icaltimezone_free frees the VTIMEZONE component.
432 if (mTimezone) {
433 icaltimezone_free(mTimezone, 1 /* free struct */);
434 } else {
435 icalcomponent_free(mComponent);
436 }
437 }
438 }
439 NS_IMETHODIMP
GetIcalComponent(JS::MutableHandleValue)440 calIcalComponent::GetIcalComponent(JS::MutableHandleValue) {
441 return NS_ERROR_NOT_IMPLEMENTED;
442 }
443
444 NS_IMETHODIMP
SetIcalComponent(JS::HandleValue)445 calIcalComponent::SetIcalComponent(JS::HandleValue) {
446 return NS_ERROR_NOT_IMPLEMENTED;
447 }
448
449 NS_IMETHODIMP
GetParent(calIIcalComponent ** parent)450 calIcalComponent::GetParent(calIIcalComponent** parent) {
451 NS_ENSURE_ARG_POINTER(parent);
452 NS_IF_ADDREF(*parent = mParent);
453 return NS_OK;
454 }
455
456 NS_IMETHODIMP
GetIcalTimezone(JS::MutableHandleValue)457 calIcalComponent::GetIcalTimezone(JS::MutableHandleValue) {
458 return NS_ERROR_NOT_IMPLEMENTED;
459 }
460
461 NS_IMETHODIMP
SetIcalTimezone(JS::HandleValue)462 calIcalComponent::SetIcalTimezone(JS::HandleValue) {
463 return NS_ERROR_NOT_IMPLEMENTED;
464 }
465
466 NS_IMETHODIMP
AddTimezoneReference(calITimezone * aTimezone)467 calIcalComponent::AddTimezoneReference(calITimezone* aTimezone) {
468 NS_ENSURE_ARG_POINTER(aTimezone);
469 nsAutoCString tzid;
470 nsresult rv = aTimezone->GetTzid(tzid);
471 NS_ENSURE_SUCCESS(rv, rv);
472 mReferencedTimezones.InsertOrUpdate(tzid, aTimezone);
473
474 return NS_OK;
475 }
476
477 NS_IMETHODIMP
GetReferencedTimezones(nsTArray<RefPtr<calITimezone>> & aTimezones)478 calIcalComponent::GetReferencedTimezones(
479 nsTArray<RefPtr<calITimezone>>& aTimezones) {
480 aTimezones.ClearAndRetainStorage();
481 uint32_t const count = mReferencedTimezones.Count();
482 if (count == 0) {
483 return NS_OK;
484 }
485 aTimezones.SetCapacity(count);
486 for (auto iter = mReferencedTimezones.ConstIter(); !iter.Done();
487 iter.Next()) {
488 aTimezones.AppendElement(iter.Data());
489 }
490 return NS_OK;
491 }
492
SetPropertyValue(icalproperty_kind kind,icalvalue * val)493 nsresult calIcalComponent::SetPropertyValue(icalproperty_kind kind,
494 icalvalue* val) {
495 ClearAllProperties(kind);
496 if (!val) return NS_OK;
497
498 icalproperty* prop = icalproperty_new(kind);
499 if (!prop) {
500 icalvalue_free(val);
501 return NS_ERROR_OUT_OF_MEMORY;
502 }
503
504 icalproperty_set_value(prop, val);
505 icalcomponent_add_property(mComponent, prop);
506 return NS_OK;
507 }
508
SetProperty(icalproperty_kind kind,icalproperty * prop)509 nsresult calIcalComponent::SetProperty(icalproperty_kind kind,
510 icalproperty* prop) {
511 ClearAllProperties(kind);
512 if (!prop) return NS_OK;
513 icalcomponent_add_property(mComponent, prop);
514 return NS_OK;
515 }
516
517 #define COMP_STRING_TO_ENUM_ATTRIBUTE(Attrname, ICALNAME, lcname) \
518 NS_IMETHODIMP \
519 calIcalComponent::Get##Attrname(nsACString& str) { \
520 int32_t val; \
521 nsresult rv = GetIntProperty(ICAL_##ICALNAME##_PROPERTY, &val); \
522 if (NS_FAILED(rv)) return rv; \
523 if (val == -1) { \
524 str.Truncate(); \
525 str.SetIsVoid(true); \
526 } else { \
527 str.Assign( \
528 icalproperty_##lcname##_to_string((icalproperty_##lcname)val)); \
529 } \
530 return NS_OK; \
531 } \
532 \
533 NS_IMETHODIMP \
534 calIcalComponent::Set##Attrname(const nsACString& str) { \
535 icalproperty* prop = nullptr; \
536 if (!str.IsVoid()) { \
537 icalproperty_##lcname val = \
538 icalproperty_string_to_##lcname(PromiseFlatCString(str).get()); \
539 prop = icalproperty_new_##lcname(val); \
540 if (!prop) return NS_ERROR_OUT_OF_MEMORY; /* XXX map errno */ \
541 } \
542 return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop); \
543 }
544
545 #define COMP_GENERAL_STRING_ATTRIBUTE(Attrname, ICALNAME) \
546 NS_IMETHODIMP \
547 calIcalComponent::Get##Attrname(nsACString& str) { \
548 return GetStringProperty(ICAL_##ICALNAME##_PROPERTY, str); \
549 } \
550 \
551 NS_IMETHODIMP \
552 calIcalComponent::Set##Attrname(const nsACString& str) { \
553 return SetStringProperty(ICAL_##ICALNAME##_PROPERTY, str); \
554 }
555
556 #define COMP_STRING_ATTRIBUTE(Attrname, ICALNAME, lcname) \
557 NS_IMETHODIMP \
558 calIcalComponent::Get##Attrname(nsACString& str) { \
559 return GetStringProperty(ICAL_##ICALNAME##_PROPERTY, str); \
560 } \
561 \
562 NS_IMETHODIMP \
563 calIcalComponent::Set##Attrname(const nsACString& str) { \
564 icalproperty* prop = \
565 icalproperty_new_##lcname(PromiseFlatCString(str).get()); \
566 return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop); \
567 }
568
569 #define COMP_GENERAL_INT_ATTRIBUTE(Attrname, ICALNAME) \
570 NS_IMETHODIMP \
571 calIcalComponent::Get##Attrname(int32_t* valp) { \
572 return GetIntProperty(ICAL_##ICALNAME##_PROPERTY, valp); \
573 } \
574 \
575 NS_IMETHODIMP \
576 calIcalComponent::Set##Attrname(int32_t val) { \
577 return SetIntProperty(ICAL_##ICALNAME##_PROPERTY, val); \
578 }
579
580 #define COMP_ENUM_ATTRIBUTE(Attrname, ICALNAME, lcname) \
581 NS_IMETHODIMP \
582 calIcalComponent::Get##Attrname(int32_t* valp) { \
583 return GetIntProperty(ICAL_##ICALNAME##_PROPERTY, valp); \
584 } \
585 \
586 NS_IMETHODIMP \
587 calIcalComponent::Set##Attrname(int32_t val) { \
588 icalproperty* prop = \
589 icalproperty_new_##lcname((icalproperty_##lcname)val); \
590 return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop); \
591 }
592
593 #define COMP_INT_ATTRIBUTE(Attrname, ICALNAME, lcname) \
594 NS_IMETHODIMP \
595 calIcalComponent::Get##Attrname(int32_t* valp) { \
596 return GetIntProperty(ICAL_##ICALNAME##_PROPERTY, valp); \
597 } \
598 \
599 NS_IMETHODIMP \
600 calIcalComponent::Set##Attrname(int32_t val) { \
601 icalproperty* prop = icalproperty_new_##lcname(val); \
602 return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop); \
603 }
604
GetStringProperty(icalproperty_kind kind,nsACString & str)605 nsresult calIcalComponent::GetStringProperty(icalproperty_kind kind,
606 nsACString& str) {
607 icalproperty* prop = icalcomponent_get_first_property(mComponent, kind);
608 if (!prop) {
609 str.Truncate();
610 str.SetIsVoid(true);
611 } else {
612 str.Assign(icalvalue_get_string(icalproperty_get_value(prop)));
613 }
614 return NS_OK;
615 }
616
SetStringProperty(icalproperty_kind kind,const nsACString & str)617 nsresult calIcalComponent::SetStringProperty(icalproperty_kind kind,
618 const nsACString& str) {
619 icalvalue* val = nullptr;
620 if (!str.IsVoid()) {
621 val = icalvalue_new_string(PromiseFlatCString(str).get());
622 if (!val) return NS_ERROR_OUT_OF_MEMORY;
623 }
624 return SetPropertyValue(kind, val);
625 }
626
GetIntProperty(icalproperty_kind kind,int32_t * valp)627 nsresult calIcalComponent::GetIntProperty(icalproperty_kind kind,
628 int32_t* valp) {
629 icalproperty* prop = icalcomponent_get_first_property(mComponent, kind);
630 if (!prop)
631 *valp = calIIcalComponent::INVALID_VALUE;
632 else
633 *valp = (int32_t)icalvalue_get_integer(icalproperty_get_value(prop));
634 return NS_OK;
635 }
636
SetIntProperty(icalproperty_kind kind,int32_t i)637 nsresult calIcalComponent::SetIntProperty(icalproperty_kind kind, int32_t i) {
638 icalvalue* val = icalvalue_new_integer(i);
639 if (!val) return NS_ERROR_OUT_OF_MEMORY;
640 return SetPropertyValue(kind, val);
641 }
642
GetDateTimeAttribute(icalproperty_kind kind,calIDateTime ** dtp)643 nsresult calIcalComponent::GetDateTimeAttribute(icalproperty_kind kind,
644 calIDateTime** dtp) {
645 NS_ENSURE_ARG_POINTER(dtp);
646 icalproperty* prop = icalcomponent_get_first_property(mComponent, kind);
647 if (!prop) {
648 *dtp = nullptr; /* invalid date */
649 return NS_OK;
650 }
651 return calIcalProperty::getDatetime_(this, prop, dtp);
652 }
653
SetDateTimeAttribute(icalproperty_kind kind,calIDateTime * dt)654 nsresult calIcalComponent::SetDateTimeAttribute(icalproperty_kind kind,
655 calIDateTime* dt) {
656 ClearAllProperties(kind);
657 bool isValid;
658 if (!dt || NS_FAILED(dt->GetIsValid(&isValid)) || !isValid) {
659 return NS_OK;
660 }
661 icalproperty* prop = icalproperty_new(kind);
662 CAL_ENSURE_MEMORY(prop);
663 nsresult rc = calIcalProperty::setDatetime_(this, prop, dt);
664 if (NS_SUCCEEDED(rc))
665 icalcomponent_add_property(mComponent, prop);
666 else
667 icalproperty_free(prop);
668 return rc;
669 }
670
671 NS_IMETHODIMP
GetParent(calIIcalComponent ** parent)672 calIcalProperty::GetParent(calIIcalComponent** parent) {
673 NS_IF_ADDREF(*parent = mParent);
674 return NS_OK;
675 }
676
677 NS_IMETHODIMP
GetIcalProperty(JS::MutableHandleValue)678 calIcalProperty::GetIcalProperty(JS::MutableHandleValue) {
679 return NS_ERROR_NOT_IMPLEMENTED;
680 }
681
682 NS_IMETHODIMP
SetIcalProperty(JS::HandleValue)683 calIcalProperty::SetIcalProperty(JS::HandleValue) {
684 return NS_ERROR_NOT_IMPLEMENTED;
685 }
686
687 NS_IMETHODIMP
SetValueAsDatetime(calIDateTime * dt)688 calIcalProperty::SetValueAsDatetime(calIDateTime* dt) {
689 NS_ENSURE_ARG_POINTER(dt);
690 return setDatetime_(toIcalComponent(mParent), mProperty, dt);
691 }
692
setDatetime_(calIcalComponent * parent,icalproperty * prop,calIDateTime * dt)693 nsresult calIcalProperty::setDatetime_(calIcalComponent* parent,
694 icalproperty* prop, calIDateTime* dt) {
695 NS_ENSURE_ARG_POINTER(prop);
696 NS_ENSURE_ARG_POINTER(dt);
697
698 nsresult rv;
699 nsCOMPtr<calIDateTimeLibical> icaldt = do_QueryInterface(dt, &rv);
700 NS_ENSURE_SUCCESS(rv, rv);
701
702 icaltimetype itt;
703 icaldt->ToIcalTime(&itt);
704
705 if (parent) {
706 if (!itt.is_utc) {
707 nsCOMPtr<calITimezone> tz;
708 rv = dt->GetTimezone(getter_AddRefs(tz));
709 NS_ENSURE_SUCCESS(rv, rv);
710 if (itt.zone) {
711 rv = parent->getParentVCalendarOrThis()->AddTimezoneReference(tz);
712 NS_ENSURE_SUCCESS(rv, rv);
713 icalparameter* const param = icalparameter_new_from_value_string(
714 ICAL_TZID_PARAMETER,
715 icaltimezone_get_tzid(const_cast<icaltimezone*>(itt.zone)));
716 icalproperty_set_parameter(prop, param);
717 } else { // either floating or phantom:
718 bool b = false;
719 if (NS_FAILED(tz->GetIsFloating(&b)) || !b) {
720 // restore the same phantom TZID:
721 nsAutoCString tzid;
722 rv = tz->GetTzid(tzid);
723 NS_ENSURE_SUCCESS(rv, rv);
724 icalparameter* const param = icalparameter_new_from_value_string(
725 ICAL_TZID_PARAMETER, tzid.get());
726 icalproperty_set_parameter(prop, param);
727 }
728 }
729 }
730 } else if (!itt.is_date && !itt.is_utc && itt.zone) {
731 // no parent to add the CTIMEZONE to: coerce DATETIMEs to UTC, DATEs to
732 // floating
733 icaltimezone_convert_time(&itt, const_cast<icaltimezone*>(itt.zone),
734 icaltimezone_get_utc_timezone());
735 itt.zone = icaltimezone_get_utc_timezone();
736 itt.is_utc = 1;
737 }
738
739 icalvalue* const val = icalvalue_new_datetime(itt);
740 CAL_ENSURE_MEMORY(val);
741 icalproperty_set_value(prop, val);
742 return NS_OK;
743 }
744
745 #define RO_COMP_DATE_ATTRIBUTE(Attrname, ICALNAME) \
746 NS_IMETHODIMP \
747 calIcalComponent::Get##Attrname(calIDateTime** dtp) { \
748 return GetDateTimeAttribute(ICAL_##ICALNAME##_PROPERTY, dtp); \
749 }
750
751 #define COMP_DATE_ATTRIBUTE(Attrname, ICALNAME) \
752 RO_COMP_DATE_ATTRIBUTE(Attrname, ICALNAME) \
753 \
754 NS_IMETHODIMP \
755 calIcalComponent::Set##Attrname(calIDateTime* dt) { \
756 return SetDateTimeAttribute(ICAL_##ICALNAME##_PROPERTY, dt); \
757 }
758
759 #define RO_COMP_DURATION_ATTRIBUTE(Attrname, ICALNAME) \
760 NS_IMETHODIMP \
761 calIcalComponent::Get##Attrname(calIDuration** dtp) { \
762 icalproperty* prop = icalcomponent_get_first_property( \
763 mComponent, ICAL_##ICALNAME##_PROPERTY); \
764 if (!prop) { \
765 *dtp = nullptr; /* invalid duration */ \
766 return NS_OK; \
767 } \
768 struct icaldurationtype idt = \
769 icalvalue_get_duration(icalproperty_get_value(prop)); \
770 *dtp = new calDuration(&idt); \
771 CAL_ENSURE_MEMORY(*dtp); \
772 NS_ADDREF(*dtp); \
773 return NS_OK; \
774 }
775
NS_IMPL_CLASSINFO(calIcalComponent,nullptr,nsIClassInfo::THREADSAFE,CAL_ICALCOMPONENT_CID)776 NS_IMPL_CLASSINFO(calIcalComponent, nullptr, nsIClassInfo::THREADSAFE,
777 CAL_ICALCOMPONENT_CID)
778 NS_IMPL_ISUPPORTS_CI(calIcalComponent, calIIcalComponent,
779 calIIcalComponentLibical)
780
781 NS_IMETHODIMP_(icalcomponent*)
782 calIcalComponent::GetLibicalComponent() { return mComponent; }
783
NS_IMETHODIMP_(icaltimezone *)784 NS_IMETHODIMP_(icaltimezone*)
785 calIcalComponent::GetLibicalTimezone() {
786 NS_ASSERTION(icalcomponent_isa(mComponent) == ICAL_VTIMEZONE_COMPONENT,
787 "no VTIMEZONE -- unexpected!");
788 if (!mTimezone &&
789 (icalcomponent_isa(mComponent) == ICAL_VTIMEZONE_COMPONENT)) {
790 // xxx todo: libical needs a parent VCALENDAR to retrieve a icaltimezone
791 NS_ASSERTION(mParent, "VTIMEZONE has no parent!");
792 if (mParent) {
793 icalproperty* const tzidProp =
794 icalcomponent_get_first_property(mComponent, ICAL_TZID_PROPERTY);
795 NS_ASSERTION(tzidProp, "no TZID property in VTIMEZONE!?");
796 if (tzidProp) {
797 mTimezone = icalcomponent_get_timezone(
798 mParent->GetLibicalComponent(),
799 icalvalue_get_string(icalproperty_get_value(tzidProp)));
800 }
801 }
802 }
803 return mTimezone;
804 }
805
806 NS_IMETHODIMP
GetFirstSubcomponent(const nsACString & kind,calIIcalComponent ** subcomp)807 calIcalComponent::GetFirstSubcomponent(const nsACString& kind,
808 calIIcalComponent** subcomp) {
809 NS_ENSURE_ARG_POINTER(subcomp);
810
811 icalcomponent_kind compkind =
812 icalcomponent_string_to_kind(PromiseFlatCString(kind).get());
813
814 // Maybe someday I'll support X-COMPONENTs
815 if (compkind == ICAL_NO_COMPONENT || compkind == ICAL_X_COMPONENT)
816 return NS_ERROR_INVALID_ARG;
817
818 icalcomponent* ical = icalcomponent_get_first_component(mComponent, compkind);
819 if (!ical) {
820 *subcomp = nullptr;
821 return NS_OK;
822 }
823
824 *subcomp = new calIcalComponent(ical, this);
825 CAL_ENSURE_MEMORY(*subcomp);
826 NS_ADDREF(*subcomp);
827 return NS_OK;
828 }
829
830 NS_IMETHODIMP
GetNextSubcomponent(const nsACString & kind,calIIcalComponent ** subcomp)831 calIcalComponent::GetNextSubcomponent(const nsACString& kind,
832 calIIcalComponent** subcomp) {
833 NS_ENSURE_ARG_POINTER(subcomp);
834
835 icalcomponent_kind compkind =
836 icalcomponent_string_to_kind(PromiseFlatCString(kind).get());
837
838 // Maybe someday I'll support X-COMPONENTs
839 if (compkind == ICAL_NO_COMPONENT || compkind == ICAL_X_COMPONENT)
840 return NS_ERROR_INVALID_ARG;
841
842 icalcomponent* ical = icalcomponent_get_next_component(mComponent, compkind);
843 if (!ical) {
844 *subcomp = nullptr;
845 return NS_OK;
846 }
847
848 *subcomp = new calIcalComponent(ical, this);
849 CAL_ENSURE_MEMORY(*subcomp);
850 NS_ADDREF(*subcomp);
851 return NS_OK;
852 }
853
854 NS_IMETHODIMP
GetComponentType(nsACString & componentType)855 calIcalComponent::GetComponentType(nsACString& componentType) {
856 componentType.Assign(
857 icalcomponent_kind_to_string(icalcomponent_isa(mComponent)));
858 return NS_OK;
859 }
860
COMP_STRING_ATTRIBUTE(Uid,UID,uid)861 COMP_STRING_ATTRIBUTE(Uid, UID, uid)
862 COMP_STRING_ATTRIBUTE(Prodid, PRODID, prodid)
863 COMP_STRING_ATTRIBUTE(Version, VERSION, version)
864 COMP_STRING_TO_ENUM_ATTRIBUTE(Method, METHOD, method)
865 COMP_STRING_TO_ENUM_ATTRIBUTE(Status, STATUS, status)
866 COMP_STRING_ATTRIBUTE(Summary, SUMMARY, summary)
867 COMP_STRING_ATTRIBUTE(Description, DESCRIPTION, description)
868 COMP_STRING_ATTRIBUTE(Location, LOCATION, location)
869 COMP_STRING_ATTRIBUTE(Categories, CATEGORIES, categories)
870 COMP_STRING_ATTRIBUTE(URL, URL, url)
871 COMP_INT_ATTRIBUTE(Priority, PRIORITY, priority)
872 RO_COMP_DURATION_ATTRIBUTE(Duration, DURATION)
873 COMP_DATE_ATTRIBUTE(StartTime, DTSTART)
874 COMP_DATE_ATTRIBUTE(EndTime, DTEND)
875 COMP_DATE_ATTRIBUTE(DueTime, DUE)
876 COMP_DATE_ATTRIBUTE(StampTime, DTSTAMP)
877 COMP_DATE_ATTRIBUTE(LastModified, LASTMODIFIED)
878 COMP_DATE_ATTRIBUTE(CreatedTime, CREATED)
879 COMP_DATE_ATTRIBUTE(CompletedTime, COMPLETED)
880 COMP_DATE_ATTRIBUTE(RecurrenceId, RECURRENCEID)
881
882 void calIcalComponent::ClearAllProperties(icalproperty_kind kind) {
883 for (icalproperty *prop = icalcomponent_get_first_property(mComponent, kind),
884 *next;
885 prop; prop = next) {
886 next = icalcomponent_get_next_property(mComponent, kind);
887 icalcomponent_remove_property(mComponent, prop);
888 icalproperty_free(prop);
889 }
890 }
891
892 NS_IMETHODIMP
SerializeToICS(nsACString & serialized)893 calIcalComponent::SerializeToICS(nsACString& serialized) {
894 char* icalstr;
895
896 nsresult rv = Serialize(&icalstr);
897 if (NS_FAILED(rv)) {
898 return rv;
899 }
900
901 serialized.Assign(icalstr);
902 return NS_OK;
903 }
904
905 NS_IMETHODIMP
ToString(nsACString & aResult)906 calIcalComponent::ToString(nsACString& aResult) {
907 return SerializeToICS(aResult);
908 }
909
910 NS_IMETHODIMP
SerializeToICSStream(nsIInputStream ** aStreamResult)911 calIcalComponent::SerializeToICSStream(nsIInputStream** aStreamResult) {
912 NS_ENSURE_ARG_POINTER(aStreamResult);
913
914 char* icalstr;
915 nsresult rv = Serialize(&icalstr);
916 NS_ENSURE_SUCCESS(rv, rv);
917
918 nsCOMPtr<nsIStringInputStream> aStringStream(
919 do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv));
920 NS_ENSURE_SUCCESS(rv, rv);
921 // copies the string into the input stream that's handed back.
922 // This copy is necessary because we don't really own icalstr;
923 // it's one of libical's ring buffers
924 rv = aStringStream->SetData(icalstr, -1);
925 NS_ENSURE_SUCCESS(rv, rv);
926
927 aStringStream.forget(aStreamResult);
928 return NS_OK;
929 }
930
Serialize(char ** icalstr)931 nsresult calIcalComponent::Serialize(char** icalstr) {
932 NS_ENSURE_ARG_POINTER(icalstr);
933
934 // add the timezone bits
935 if (icalcomponent_isa(mComponent) == ICAL_VCALENDAR_COMPONENT &&
936 mReferencedTimezones.Count() > 0) {
937 for (auto iter = mReferencedTimezones.ConstIter(); !iter.Done();
938 iter.Next()) {
939 icaltimezone* icaltz = cal::getIcalTimezone(iter.Data());
940 if (icaltz) {
941 icalcomponent* const tzcomp =
942 icalcomponent_new_clone(icaltimezone_get_component(icaltz));
943 icalcomponent_add_component(mComponent, tzcomp);
944 }
945 }
946 }
947
948 *icalstr = icalcomponent_as_ical_string(mComponent);
949 if (!*icalstr) {
950 // xxx todo: what about NS_ERROR_OUT_OF_MEMORY?
951 #ifdef DEBUG
952 fprintf(stderr, "Error serializing: %d (%s)\n", icalerrno,
953 icalerror_strerror(icalerrno));
954 #endif
955 // The return values in calIError match with libical errnos,
956 // so no need for a conversion table or anything.
957 return static_cast<nsresult>(calIErrors::ICS_ERROR_BASE + icalerrno);
958 }
959
960 return NS_OK;
961 }
962
963 NS_IMETHODIMP
Clone(calIIcalComponent ** _retval)964 calIcalComponent::Clone(calIIcalComponent** _retval) {
965 NS_ENSURE_ARG_POINTER(_retval);
966 icalcomponent* cloned = icalcomponent_new_clone(mComponent);
967 if (cloned == nullptr) return NS_ERROR_OUT_OF_MEMORY;
968 calIcalComponent* const comp =
969 new calIcalComponent(cloned, nullptr, getTzProvider());
970 if (comp == nullptr) {
971 icalcomponent_free(cloned);
972 return NS_ERROR_OUT_OF_MEMORY;
973 }
974 NS_ADDREF(*_retval = comp);
975 return NS_OK;
976 }
977
978 NS_IMETHODIMP
AddSubcomponent(calIIcalComponent * aComp)979 calIcalComponent::AddSubcomponent(calIIcalComponent* aComp) {
980 NS_ENSURE_ARG_POINTER(aComp);
981
982 /* XXX mildly unsafe assumption here.
983 * To fix it, I will:
984 * - check the object's classinfo to find out if I have one of my
985 * own objects, and if not
986 * - use comp->serializeToICS and reparse to create a copy.
987 *
988 * I should probably also return the new/reused component so that the
989 * caller has something it can poke at all live-like.
990 */
991
992 nsresult rv;
993 nsCOMPtr<calIIcalComponentLibical> icalcomp = do_QueryInterface(aComp, &rv);
994 NS_ENSURE_SUCCESS(rv, rv);
995
996 calIcalComponent* const ical = toIcalComponent(icalcomp);
997
998 nsTArray<RefPtr<calITimezone>> timezones;
999 rv = ical->GetReferencedTimezones(timezones);
1000 calIcalComponent* const vcal = getParentVCalendarOrThis();
1001 for (auto& tz : timezones) {
1002 rv = vcal->AddTimezoneReference(tz);
1003 NS_ENSURE_SUCCESS(rv, rv);
1004 }
1005
1006 if (ical->mParent) {
1007 ical->mComponent = icalcomponent_new_clone(ical->mComponent);
1008 }
1009 ical->mParent = this;
1010 icalcomponent_add_component(mComponent, ical->mComponent);
1011 return NS_OK;
1012 }
1013
1014 // NS_IMETHODIMP
1015 // IcalComponent::RemoveSubcomponent(calIIcalComponent *comp)
1016 // {
1017 // NS_ENSURE_ARG_POINTER(comp);
1018 // calIcalComponent *ical = static_cast<calIcalComponent *>(comp);
1019 // icalcomponent_remove_component(mComponent, ical->mComponent);
1020 // ical->mParent = nullptr;
1021 // return NS_OK;
1022 // }
1023
1024 NS_IMETHODIMP
GetFirstProperty(const nsACString & kind,calIIcalProperty ** prop)1025 calIcalComponent::GetFirstProperty(const nsACString& kind,
1026 calIIcalProperty** prop) {
1027 NS_ENSURE_ARG_POINTER(prop);
1028
1029 icalproperty_kind propkind =
1030 icalproperty_string_to_kind(PromiseFlatCString(kind).get());
1031
1032 if (propkind == ICAL_NO_PROPERTY) return NS_ERROR_INVALID_ARG;
1033
1034 icalproperty* icalprop = nullptr;
1035 if (propkind == ICAL_X_PROPERTY) {
1036 for (icalprop =
1037 icalcomponent_get_first_property(mComponent, ICAL_X_PROPERTY);
1038 icalprop; icalprop = icalcomponent_get_next_property(
1039 mComponent, ICAL_X_PROPERTY)) {
1040 if (kind.Equals(icalproperty_get_x_name(icalprop))) break;
1041 }
1042 } else {
1043 icalprop = icalcomponent_get_first_property(mComponent, propkind);
1044 }
1045
1046 if (!icalprop) {
1047 *prop = nullptr;
1048 return NS_OK;
1049 }
1050
1051 *prop = new calIcalProperty(icalprop, this);
1052 CAL_ENSURE_MEMORY(*prop);
1053 NS_ADDREF(*prop);
1054 return NS_OK;
1055 }
1056
1057 NS_IMETHODIMP
GetNextProperty(const nsACString & kind,calIIcalProperty ** prop)1058 calIcalComponent::GetNextProperty(const nsACString& kind,
1059 calIIcalProperty** prop) {
1060 NS_ENSURE_ARG_POINTER(prop);
1061
1062 icalproperty_kind propkind =
1063 icalproperty_string_to_kind(PromiseFlatCString(kind).get());
1064
1065 if (propkind == ICAL_NO_PROPERTY) return NS_ERROR_INVALID_ARG;
1066 icalproperty* icalprop = nullptr;
1067 if (propkind == ICAL_X_PROPERTY) {
1068 for (icalprop =
1069 icalcomponent_get_next_property(mComponent, ICAL_X_PROPERTY);
1070 icalprop; icalprop = icalcomponent_get_next_property(
1071 mComponent, ICAL_X_PROPERTY)) {
1072 if (kind.Equals(icalproperty_get_x_name(icalprop))) break;
1073 }
1074 } else {
1075 icalprop = icalcomponent_get_next_property(mComponent, propkind);
1076 }
1077
1078 if (!icalprop) {
1079 *prop = nullptr;
1080 return NS_OK;
1081 }
1082
1083 *prop = new calIcalProperty(icalprop, this);
1084 CAL_ENSURE_MEMORY(*prop);
1085 NS_ADDREF(*prop);
1086 return NS_OK;
1087 }
1088
1089 NS_IMETHODIMP
AddProperty(calIIcalProperty * aProp)1090 calIcalComponent::AddProperty(calIIcalProperty* aProp) {
1091 NS_ENSURE_ARG_POINTER(aProp);
1092 // We assume a calIcalProperty is passed in (else the cast wouldn't run and
1093 // we are about to crash), so we assume that this ICS service code has created
1094 // the property.
1095
1096 nsresult rv;
1097 nsCOMPtr<calIIcalPropertyLibical> icalprop = do_QueryInterface(aProp, &rv);
1098 NS_ENSURE_SUCCESS(rv, rv);
1099
1100 calIcalProperty* const ical = toIcalProperty(icalprop);
1101 if (ical->mParent) {
1102 ical->mProperty = icalproperty_new_clone(ical->mProperty);
1103 }
1104 ical->mParent = this;
1105 icalcomponent_add_property(mComponent, ical->mProperty);
1106
1107 nsCOMPtr<calIDateTime> dt;
1108 if (NS_SUCCEEDED(aProp->GetValueAsDatetime(getter_AddRefs(dt))) && dt) {
1109 // make sure timezone definition will be included:
1110 nsCOMPtr<calITimezone> tz;
1111 if (NS_SUCCEEDED(dt->GetTimezone(getter_AddRefs(tz))) && tz) {
1112 getParentVCalendarOrThis()->AddTimezoneReference(tz);
1113 }
1114 }
1115 return NS_OK;
1116 }
1117
1118 // If you add then remove a property/component, the referenced
1119 // timezones won't get purged out. There's currently no client code.
1120
1121 // NS_IMETHODIMP
1122 // calIcalComponent::RemoveProperty(calIIcalProperty *prop)
1123 // {
1124 // NS_ENSURE_ARG_POINTER(prop);
1125 // // XXX like AddSubcomponent, this is questionable
1126 // calIcalProperty *ical = static_cast<calIcalProperty *>(prop);
1127 // icalcomponent_remove_property(mComponent, ical->mProperty);
1128 // ical->mParent = nullptr;
1129 // return NS_OK;
1130 // }
1131
NS_IMPL_CLASSINFO(calICSService,nullptr,nsIClassInfo::THREADSAFE,CAL_ICSSERVICE_CID)1132 NS_IMPL_CLASSINFO(calICSService, nullptr, nsIClassInfo::THREADSAFE,
1133 CAL_ICSSERVICE_CID)
1134 NS_IMPL_ISUPPORTS_CI(calICSService, calIICSService)
1135
1136 calICSService::calICSService() {}
1137
1138 NS_IMETHODIMP
ParseICS(const nsACString & serialized,calITimezoneProvider * tzProvider,calIIcalComponent ** component)1139 calICSService::ParseICS(const nsACString& serialized,
1140 calITimezoneProvider* tzProvider,
1141 calIIcalComponent** component) {
1142 NS_ENSURE_ARG_POINTER(component);
1143 icalcomponent* ical =
1144 icalparser_parse_string(PromiseFlatCString(serialized).get());
1145 if (!ical) {
1146 #ifdef DEBUG
1147 fprintf(stderr, "Error parsing: '%20s': %d (%s)\n",
1148 PromiseFlatCString(serialized).get(), icalerrno,
1149 icalerror_strerror(icalerrno));
1150 #endif
1151 // The return values is calIError match with ical errors,
1152 // so no need for a conversion table or anything.
1153 return static_cast<nsresult>(calIErrors::ICS_ERROR_BASE + icalerrno);
1154 }
1155 calIcalComponent* comp = new calIcalComponent(ical, nullptr, tzProvider);
1156 if (!comp) {
1157 icalcomponent_free(ical);
1158 return NS_ERROR_OUT_OF_MEMORY;
1159 }
1160 NS_ADDREF(*component = comp);
1161 return NS_OK;
1162 }
1163
1164 NS_IMETHODIMP
Run()1165 calICSService::ParserWorker::Run() {
1166 icalcomponent* ical = icalparser_parse_string(mString.get());
1167 nsresult status = NS_OK;
1168 calIIcalComponent* comp = nullptr;
1169
1170 if (ical) {
1171 comp = new calIcalComponent(ical, nullptr, mProvider);
1172 if (!comp) {
1173 icalcomponent_free(ical);
1174 status = NS_ERROR_OUT_OF_MEMORY;
1175 }
1176 } else {
1177 status = static_cast<nsresult>(calIErrors::ICS_ERROR_BASE + icalerrno);
1178 }
1179
1180 nsCOMPtr<nsIRunnable> completer =
1181 new ParserWorkerCompleter(mWorkerThread, status, comp, mListener);
1182 mMainThread->Dispatch(completer, NS_DISPATCH_NORMAL);
1183
1184 mWorkerThread = nullptr;
1185 mMainThread = nullptr;
1186 return NS_OK;
1187 }
1188
1189 NS_IMETHODIMP
Run()1190 calICSService::ParserWorker::ParserWorkerCompleter::Run() {
1191 mListener->OnParsingComplete(mStatus, mComp);
1192
1193 nsresult rv = mWorkerThread->Shutdown();
1194 NS_ENSURE_SUCCESS(rv, rv);
1195
1196 mWorkerThread = nullptr;
1197 return NS_OK;
1198 }
1199
1200 NS_IMETHODIMP
ParseICSAsync(const nsACString & serialized,calITimezoneProvider * tzProvider,calIIcsComponentParsingListener * listener)1201 calICSService::ParseICSAsync(const nsACString& serialized,
1202 calITimezoneProvider* tzProvider,
1203 calIIcsComponentParsingListener* listener) {
1204 nsresult rv;
1205 NS_ENSURE_ARG_POINTER(listener);
1206
1207 nsCOMPtr<nsIThread> workerThread;
1208 nsCOMPtr<nsIThread> currentThread;
1209 rv = NS_GetCurrentThread(getter_AddRefs(currentThread));
1210 NS_ENSURE_SUCCESS(rv, rv);
1211 rv = NS_NewNamedThread("ICS parser", getter_AddRefs(workerThread));
1212 NS_ENSURE_SUCCESS(rv, rv);
1213
1214 nsCOMPtr<nsIRunnable> worker = new ParserWorker(
1215 currentThread, workerThread, serialized, tzProvider, listener);
1216 NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
1217
1218 rv = workerThread->Dispatch(worker, NS_DISPATCH_NORMAL);
1219 NS_ENSURE_SUCCESS(rv, rv);
1220
1221 return NS_OK;
1222 }
1223
1224 NS_IMETHODIMP
CreateIcalComponent(const nsACString & kind,calIIcalComponent ** comp)1225 calICSService::CreateIcalComponent(const nsACString& kind,
1226 calIIcalComponent** comp) {
1227 NS_ENSURE_ARG_POINTER(comp);
1228 icalcomponent_kind compkind =
1229 icalcomponent_string_to_kind(PromiseFlatCString(kind).get());
1230
1231 // Maybe someday I'll support X-COMPONENTs
1232 if (compkind == ICAL_NO_COMPONENT || compkind == ICAL_X_COMPONENT)
1233 return NS_ERROR_INVALID_ARG;
1234
1235 icalcomponent* ical = icalcomponent_new(compkind);
1236 if (!ical) return NS_ERROR_OUT_OF_MEMORY; // XXX translate
1237
1238 *comp = new calIcalComponent(ical, nullptr);
1239 if (!*comp) {
1240 icalcomponent_free(ical);
1241 return NS_ERROR_OUT_OF_MEMORY;
1242 }
1243
1244 NS_ADDREF(*comp);
1245 return NS_OK;
1246 }
1247
1248 NS_IMETHODIMP
CreateIcalProperty(const nsACString & kind,calIIcalProperty ** prop)1249 calICSService::CreateIcalProperty(const nsACString& kind,
1250 calIIcalProperty** prop) {
1251 NS_ENSURE_ARG_POINTER(prop);
1252 icalproperty_kind propkind =
1253 icalproperty_string_to_kind(PromiseFlatCString(kind).get());
1254
1255 if (propkind == ICAL_NO_PROPERTY) return NS_ERROR_INVALID_ARG;
1256
1257 icalproperty* icalprop = icalproperty_new(propkind);
1258 if (!icalprop) return NS_ERROR_OUT_OF_MEMORY; // XXX translate
1259
1260 if (propkind == ICAL_X_PROPERTY)
1261 icalproperty_set_x_name(icalprop, PromiseFlatCString(kind).get());
1262
1263 *prop = new calIcalProperty(icalprop, nullptr);
1264 CAL_ENSURE_MEMORY(*prop);
1265 NS_ADDREF(*prop);
1266 return NS_OK;
1267 }
1268
1269 NS_IMETHODIMP
CreateIcalPropertyFromString(const nsACString & str,calIIcalProperty ** prop)1270 calICSService::CreateIcalPropertyFromString(const nsACString& str,
1271 calIIcalProperty** prop) {
1272 NS_ENSURE_ARG_POINTER(prop);
1273
1274 icalproperty* icalprop =
1275 icalproperty_new_from_string(PromiseFlatCString(str).get());
1276
1277 *prop = new calIcalProperty(icalprop, nullptr);
1278 CAL_ENSURE_MEMORY(*prop);
1279 NS_ADDREF(*prop);
1280 return NS_OK;
1281 }
1282