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