Back to index

lightning-sunbird  0.9+nobinonly
calICSService.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Oracle Corporation code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  *  Oracle Corporation
00018  * Portions created by the Initial Developer are Copyright (C) 2004
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Mike Shaver <shaver@off.net>
00023  *   Michiel van Leeuwen <mvl@exedo.nl>
00024  *   Daniel Boelzle <daniel.boelzle@sun.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include "nsStringStream.h"
00040 #include "nsComponentManagerUtils.h"
00041 #ifndef MOZILLA_1_8_BRANCH
00042 #include "nsIClassInfoImpl.h"
00043 #endif
00044 #include "calICSService.h"
00045 #include "calTimezone.h"
00046 #include "calDateTime.h"
00047 #include "calDuration.h"
00048 #include "calIErrors.h"
00049 
00050 extern "C" {
00051 #include "ical.h"
00052 }
00053 
00054 calIcalProperty::~calIcalProperty()
00055 {
00056     if (!mParent) {
00057         icalproperty_free(mProperty);
00058     }
00059 }
00060 
00061 NS_IMPL_ISUPPORTS1(calIcalProperty, calIIcalProperty)
00062 
00063 NS_IMETHODIMP_(icalproperty *)
00064 calIcalProperty::GetIcalProperty()
00065 {
00066     return mProperty;
00067 }
00068 
00069 NS_IMETHODIMP_(icalcomponent *)
00070 calIcalProperty::GetIcalComponent()
00071 {
00072     return mParent->GetIcalComponent();
00073 }
00074 
00075 NS_IMETHODIMP
00076 calIcalProperty::GetIcalString(nsACString &str)
00077 {
00078     char const* icalstr = icalproperty_as_ical_string(mProperty);
00079     if (icalstr == 0) {
00080 #ifdef DEBUG
00081         fprintf(stderr, "Error getting ical string: %d (%s)\n",
00082                 icalerrno, icalerror_strerror(icalerrno));
00083 #endif
00084         return calIErrors::ICS_ERROR_BASE + icalerrno;
00085     }
00086     str.Assign(icalstr);
00087     return NS_OK;
00088 }
00089 
00090 NS_IMETHODIMP
00091 calIcalProperty::ToString(nsACString& aResult)
00092 {
00093     return GetIcalString(aResult);
00094 }
00095 
00096 NS_IMETHODIMP
00097 calIcalProperty::GetValue(nsACString &str)
00098 {
00099     icalvalue_kind kind = icalproperty_kind_to_value_kind(icalproperty_isa(mProperty));
00100 
00101     const char *icalstr;
00102     if (kind == ICAL_TEXT_VALUE) {
00103         icalvalue *v = icalproperty_get_value(mProperty);
00104         icalstr = icalvalue_get_text(v);
00105     } else if (kind == ICAL_X_VALUE) {
00106         icalvalue *v = icalproperty_get_value(mProperty);
00107         icalstr = icalvalue_get_x(v);
00108     } else {
00109         icalstr = icalproperty_get_value_as_string(mProperty);
00110     }
00111 
00112     if (!icalstr) {
00113         if (icalerrno == ICAL_BADARG_ERROR) {
00114             str.Truncate();
00115             // Set string to null, because we don't have a value
00116             // (which is something different then an empty value)
00117             str.SetIsVoid(PR_TRUE);
00118             return NS_OK;
00119         }
00120         
00121 #ifdef DEBUG
00122         fprintf(stderr, "Error getting string value: %d (%s)\n",
00123                 icalerrno, icalerror_strerror(icalerrno));
00124 #endif
00125         return NS_ERROR_FAILURE;
00126     }
00127 
00128     str.Assign(icalstr);
00129     return NS_OK;
00130 }
00131 
00132 NS_IMETHODIMP
00133 calIcalProperty::SetValue(const nsACString &str)
00134 {
00135     icalvalue_kind kind = icalproperty_kind_to_value_kind(icalproperty_isa(mProperty));
00136     if (kind == ICAL_TEXT_VALUE) {
00137         icalvalue *v = icalvalue_new_text(PromiseFlatCString(str).get());
00138         icalproperty_set_value(mProperty, v);
00139     } else if (kind == ICAL_X_VALUE) {
00140         icalvalue *v = icalvalue_new_x(PromiseFlatCString(str).get());
00141         icalproperty_set_value(mProperty, v);
00142     } else {
00143         icalproperty_set_value_from_string(mProperty,
00144                                            PromiseFlatCString(str).get(),
00145                                            icalvalue_kind_to_string(kind));
00146     }
00147     return NS_OK;
00148 }
00149 
00150 NS_IMETHODIMP
00151 calIcalProperty::GetValueAsIcalString(nsACString &str)
00152 {
00153     const char *icalstr = icalproperty_get_value_as_string(mProperty);
00154     if (!icalstr) {
00155         if (icalerrno == ICAL_BADARG_ERROR) {
00156             str.Truncate();
00157             // Set string to null, because we don't have a value
00158             // (which is something different then an empty value)
00159             str.SetIsVoid(PR_TRUE);
00160             return NS_OK;
00161         }
00162         
00163 #ifdef DEBUG
00164         fprintf(stderr, "Error getting string value: %d (%s)\n",
00165                 icalerrno, icalerror_strerror(icalerrno));
00166 #endif
00167         return NS_ERROR_FAILURE;
00168     }
00169 
00170     str.Assign(icalstr);
00171     return NS_OK;
00172 }
00173 
00174 NS_IMETHODIMP
00175 calIcalProperty::SetValueAsIcalString(const nsACString &str)
00176 {
00177     const char *kindstr = 
00178         icalvalue_kind_to_string(icalproperty_kind_to_value_kind(icalproperty_isa(mProperty)));
00179     icalproperty_set_value_from_string(mProperty,
00180                                        PromiseFlatCString(str).get(),
00181                                        kindstr);
00182     return NS_OK;
00183 }
00184 
00185 NS_IMETHODIMP
00186 calIcalProperty::GetPropertyName(nsACString &name)
00187 {
00188     const char *icalstr = icalproperty_get_property_name(mProperty);
00189     if (!icalstr) {
00190 #ifdef DEBUG
00191         fprintf(stderr, "Error getting property name: %d (%s)\n",
00192                 icalerrno, icalerror_strerror(icalerrno));
00193 #endif
00194         return NS_ERROR_FAILURE;
00195     }
00196     name.Assign(icalstr);
00197     return NS_OK;
00198 }
00199 
00200 static icalparameter*
00201 FindXParameter(icalproperty *prop, const nsACString &param)
00202 {
00203     for (icalparameter *icalparam =
00204              icalproperty_get_first_parameter(prop, ICAL_X_PARAMETER);
00205          icalparam;
00206          icalparam = icalproperty_get_next_parameter(prop, ICAL_X_PARAMETER)) {
00207         if (param.Equals(icalparameter_get_xname(icalparam)))
00208             return icalparam;
00209     }
00210     return nsnull;
00211 }
00212 
00213 NS_IMETHODIMP
00214 calIcalProperty::GetParameter(const nsACString &param, nsACString &value)
00215 {
00216     // More ridiculous parameter/X-PARAMETER handling.
00217     icalparameter_kind paramkind = 
00218         icalparameter_string_to_kind(PromiseFlatCString(param).get());
00219 
00220     if (paramkind == ICAL_NO_PARAMETER)
00221         return NS_ERROR_INVALID_ARG;
00222 
00223     const char *icalstr = nsnull;
00224     if (paramkind == ICAL_X_PARAMETER) {
00225         icalparameter *icalparam = FindXParameter(mProperty, param);
00226         if (icalparam)
00227             icalstr = icalparameter_get_xvalue(icalparam);
00228     } else {
00229         icalstr = icalproperty_get_parameter_as_string(mProperty,
00230                                                        PromiseFlatCString(param).get());
00231     }
00232 
00233     if (!icalstr) {
00234         value.Truncate();
00235         value.SetIsVoid(PR_TRUE);
00236     } else {
00237         value.Assign(icalstr);
00238     }
00239     return NS_OK;
00240 }
00241 
00242 NS_IMETHODIMP
00243 calIcalProperty::SetParameter(const nsACString &param, const nsACString &value)
00244 {
00245     icalparameter_kind paramkind = 
00246         icalparameter_string_to_kind(PromiseFlatCString(param).get());
00247 
00248     if (paramkind == ICAL_NO_PARAMETER)
00249         return NS_ERROR_INVALID_ARG;
00250 
00251     // Because libical's support for manipulating parameters is weak, and
00252     // X-PARAMETERS doubly so, we walk the list looking for an existing one of
00253     // that name, and reset its value if found.
00254     if (paramkind == ICAL_X_PARAMETER) {
00255         icalparameter *icalparam = FindXParameter(mProperty, param);
00256         if (icalparam) {
00257             icalparameter_set_xvalue(icalparam,
00258                                      PromiseFlatCString(value).get());
00259             return NS_OK;
00260         }
00261         // If not found, fall through to adding a new parameter below.
00262     } else {
00263         // We could try getting an existing parameter here and resetting its
00264         // value, but this is easier and I don't care that much about parameter
00265         // performance at this point.
00266         RemoveParameter(param);
00267     }
00268 
00269     icalparameter *icalparam = 
00270         icalparameter_new_from_value_string(paramkind,
00271                                             PromiseFlatCString(value).get());
00272     if (!icalparam)
00273         return NS_ERROR_OUT_OF_MEMORY;
00274 
00275     // You might ask me "why does libical not do this for us?" and I would 
00276     // just nod knowingly but sadly at you in return.
00277     //
00278     // You might also, if you were not too distracted by the first question,
00279     // ask why we have icalproperty_set_x_name but icalparameter_set_xname.
00280     // More nodding would ensue.
00281     if (paramkind == ICAL_X_PARAMETER)
00282         icalparameter_set_xname(icalparam, PromiseFlatCString(param).get());
00283     
00284     icalproperty_add_parameter(mProperty, icalparam);
00285     // XXX check ical errno
00286     return NS_OK;
00287 }
00288 
00289 static nsresult
00290 FillParameterName(icalparameter *icalparam, nsACString &name)
00291 {
00292     const char *propname = nsnull;
00293     if (icalparam) {
00294         icalparameter_kind paramkind = icalparameter_isa(icalparam);
00295         if (paramkind == ICAL_X_PARAMETER)
00296             propname = icalparameter_get_xname(icalparam);
00297         else if (paramkind != ICAL_NO_PARAMETER)
00298             propname = icalparameter_kind_to_string(paramkind);
00299     }
00300 
00301     if (propname) {
00302         name.Assign(propname);
00303     } else {
00304         name.Truncate();
00305         name.SetIsVoid(PR_TRUE);
00306     }
00307 
00308     return NS_OK;
00309 }
00310 
00311 NS_IMETHODIMP
00312 calIcalProperty::GetFirstParameterName(nsACString &name)
00313 {
00314     icalparameter *icalparam =
00315         icalproperty_get_first_parameter(mProperty,
00316                                          ICAL_ANY_PARAMETER);
00317     return FillParameterName(icalparam, name);
00318 }
00319 
00320 NS_IMETHODIMP
00321 calIcalProperty::GetNextParameterName(nsACString &name)
00322 {
00323     icalparameter *icalparam =
00324         icalproperty_get_next_parameter(mProperty,
00325                                         ICAL_ANY_PARAMETER);
00326     return FillParameterName(icalparam, name);
00327 }
00328 
00329 NS_IMETHODIMP
00330 calIcalProperty::RemoveParameter(const nsACString &param)
00331 {
00332     icalparameter_kind paramkind =
00333         icalparameter_string_to_kind(PromiseFlatCString(param).get());
00334 
00335     if (paramkind == ICAL_NO_PARAMETER || paramkind == ICAL_X_PARAMETER)
00336         return NS_ERROR_INVALID_ARG;
00337 
00338     icalproperty_remove_parameter(mProperty, paramkind);
00339     // XXX check ical errno
00340     return NS_OK;
00341 }
00342 
00343 NS_IMETHODIMP
00344 calIcalProperty::ClearXParameters()
00345 {
00346     int oldcount, paramcount = 0;
00347     do {
00348         oldcount = paramcount;
00349         icalproperty_remove_parameter(mProperty, ICAL_X_PARAMETER);
00350         paramcount = icalproperty_count_parameters(mProperty);
00351     } while (oldcount != paramcount);
00352     return NS_OK;
00353 }
00354 
00355 
00356 NS_IMETHODIMP
00357 calIcalProperty::GetValueAsDatetime(calIDateTime **dtp)
00358 {
00359     NS_ENSURE_ARG_POINTER(dtp);
00360     return getDatetime_(toIcalComponent(mParent), mProperty, dtp);
00361 }
00362 
00363 nsresult calIcalProperty::getDatetime_(calIcalComponent * parent,
00364                                        icalproperty * prop,
00365                                        calIDateTime ** dtp)
00366 {
00367     icalvalue * const val = icalproperty_get_value(prop);
00368     icalvalue_kind const valkind = icalvalue_isa(val);
00369     if (valkind != ICAL_DATETIME_VALUE && valkind != ICAL_DATE_VALUE) {
00370         return NS_ERROR_UNEXPECTED;
00371     }
00372     icaltimetype itt = icalvalue_get_datetime(val);
00373 
00374     char const* tzid_ = nsnull;
00375     if (!itt.is_utc) {
00376         if (itt.zone) {
00377             tzid_ = icaltimezone_get_tzid(const_cast<icaltimezone *>(itt.zone));
00378         } else {
00379             // Need to get the tzid param. Unfortunatly, libical tends to return raw
00380             // ics strings, with quotes and everything. That's not what we want. Need
00381             // to work around.
00382             icalparameter * const tzparam = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER);
00383             if (tzparam) {
00384                 tzid_ = icalparameter_get_xvalue(tzparam);
00385             }
00386         }
00387     }
00388 
00389     nsCOMPtr<calITimezone> tz;
00390     if (tzid_) {
00391         nsDependentCString const tzid(tzid_);
00392         calIcalComponent * comp = nsnull;
00393         if (parent) {
00394             comp = parent->getParentVCalendarOrThis();
00395         }
00396         // look up parent if timezone is already referenced:
00397         if (comp) {
00398             comp->mReferencedTimezones.Get(tzid, getter_AddRefs(tz));
00399         }
00400         if (!tz) {
00401             if (parent) {
00402                 // passed tz provider has precedence over timezone service:
00403                 calITimezoneProvider * const tzProvider = parent->getTzProvider();
00404                 if (tzProvider) {
00405                     tzProvider->GetTimezone(tzid, getter_AddRefs(tz));
00406                     NS_ASSERTION(tz, tzid_);
00407                 }
00408             }
00409             if (!tz) {
00410                 // look up tz in tz service.
00411                 // this hides errors from incorrect ics files, which could state
00412                 // a TZID that is not present in the ics file.
00413                 // The other way round, it makes this product more error tolerant.
00414                 cal::getTimezoneService()->GetTimezone(tzid, getter_AddRefs(tz));
00415 
00416                 if (!tz) {
00417                     icaltimezone const* zone = itt.zone;
00418                     if (!zone && comp) {
00419                         // look up parent VCALENDAR for VTIMEZONE:
00420                         zone = icalcomponent_get_timezone(comp->mComponent, tzid_);
00421                         NS_ASSERTION(zone, tzid_);
00422                     }
00423                     if (zone) {
00424                         // We need to decouple this (inner) VTIMEZONE from the parent VCALENDAR to avoid
00425                         // running into circular references (referenced timezones):
00426                         icaltimezone * const clonedZone = icaltimezone_new();
00427                         CAL_ENSURE_MEMORY(clonedZone);
00428                         icalcomponent * const clonedZoneComp =
00429                             icalcomponent_new_clone(icaltimezone_get_component(const_cast<icaltimezone *>(zone)));
00430                         if (!clonedZoneComp) {
00431                             icaltimezone_free(clonedZone, 1 /* free struct */);
00432                             CAL_ENSURE_MEMORY(clonedZoneComp);
00433                         }
00434                         if (!icaltimezone_set_component(clonedZone, clonedZoneComp)) {
00435                             icaltimezone_free(clonedZone, 1 /* free struct */);
00436                             return NS_ERROR_INVALID_ARG;
00437                         }
00438                         nsCOMPtr<calIIcalComponent> const tzComp(new calIcalComponent(clonedZone, clonedZoneComp));
00439                         CAL_ENSURE_MEMORY(tzComp);
00440                         tz = new calTimezone(tzid, tzComp);
00441                         CAL_ENSURE_MEMORY(tz);
00442                     }
00443                 }
00444             }
00445             if (comp && tz) {
00446                 // assure timezone is known:
00447                 comp->AddTimezoneReference(tz);
00448             }
00449         }
00450         if (tz) {
00451             // correct itt which would else appear floating:
00452             itt.zone = cal::getIcalTimezone(tz);
00453             itt.is_utc = 0;
00454         } else {
00455             cal::logMissingTimezone(tzid_);
00456         }
00457     }
00458     *dtp = new calDateTime(&itt, tz);
00459     CAL_ENSURE_MEMORY(*dtp);
00460     NS_ADDREF(*dtp);
00461     return NS_OK;
00462 }
00463 
00464 
00465 calIcalComponent::~calIcalComponent()
00466 {
00467     if (!mParent) {
00468         // We free either a plain icalcomponent or a icaltimezone.
00469         // In the latter case icaltimezone_free frees the VTIMEZONE component.
00470         if (mTimezone) {
00471             icaltimezone_free(mTimezone, 1 /* free struct */);
00472         } else {
00473             icalcomponent_free(mComponent);
00474         }
00475     }
00476 }
00477 
00478 NS_IMETHODIMP
00479 calIcalComponent::AddTimezoneReference(calITimezone *aTimezone)
00480 {
00481     NS_ENSURE_ARG_POINTER(aTimezone);
00482     nsCAutoString tzid;
00483     nsresult rv = aTimezone->GetTzid(tzid);
00484     NS_ENSURE_SUCCESS(rv, rv);
00485     if (!mReferencedTimezones.Put(tzid, aTimezone))
00486         return NS_ERROR_OUT_OF_MEMORY;
00487     return NS_OK;
00488 }
00489 
00490 PR_STATIC_CALLBACK(PLDHashOperator)
00491 TimezoneHashToTimezoneArray(nsACString const& /*tzid*/, calITimezone * tz, void * arg)
00492 {
00493     calITimezone *** const arrayPtr = static_cast<calITimezone ***>(arg);
00494     NS_ADDREF(**arrayPtr = tz);
00495     ++(*arrayPtr);
00496     return PL_DHASH_NEXT;
00497 }
00498 
00499 NS_IMETHODIMP
00500 calIcalComponent::GetReferencedTimezones(PRUint32 * aCount, calITimezone *** aTimezones)
00501 {
00502     NS_ENSURE_ARG_POINTER(aCount);
00503     NS_ENSURE_ARG_POINTER(aTimezones);
00504 
00505     PRUint32 const count = mReferencedTimezones.Count();
00506     if (count == 0) {
00507         *aCount = 0;
00508         *aTimezones = nsnull;
00509         return NS_OK;
00510     }
00511 
00512     calITimezone ** const timezones = static_cast<calITimezone **>(
00513         nsMemory::Alloc(sizeof(calITimezone *) * count));
00514     CAL_ENSURE_MEMORY(timezones);
00515     // tzptr will get used as an iterator by the enumerator function
00516     calITimezone ** tzptr = timezones;
00517     mReferencedTimezones.EnumerateRead(TimezoneHashToTimezoneArray, &tzptr);
00518 
00519     *aTimezones = timezones;
00520     *aCount = count;
00521     return NS_OK;
00522 }
00523 
00524 nsresult
00525 calIcalComponent::SetPropertyValue(icalproperty_kind kind, icalvalue *val)
00526 {
00527     ClearAllProperties(kind);
00528     if (!val)
00529         return NS_OK;
00530 
00531     icalproperty *prop = icalproperty_new(kind);
00532     if (!prop) {
00533         icalvalue_free(val);
00534         return NS_ERROR_OUT_OF_MEMORY;
00535     }
00536 
00537     icalproperty_set_value(prop, val);
00538     icalcomponent_add_property(mComponent, prop);
00539     return NS_OK;
00540 }
00541 
00542 nsresult
00543 calIcalComponent::SetProperty(icalproperty_kind kind, icalproperty *prop)
00544 {
00545     ClearAllProperties(kind);
00546     if (!prop)
00547         return NS_OK;
00548     icalcomponent_add_property(mComponent, prop);
00549     return NS_OK;
00550 }
00551 
00552 #define COMP_STRING_TO_GENERAL_ENUM_ATTRIBUTE(Attrname, ICALNAME, lcname) \
00553 NS_IMETHODIMP                                                           \
00554 calIcalComponent::Get##Attrname(nsACString &str)                        \
00555 {                                                                       \
00556     PRInt32 val;                                                        \
00557     nsresult rv = GetIntProperty(ICAL_##ICALNAME##_PROPERTY, &val);     \
00558     if (NS_FAILED(rv))                                                  \
00559         return rv;                                                      \
00560     if (val == -1) {                                                    \
00561         str.Truncate();                                                 \
00562         str.SetIsVoid(PR_TRUE);                                         \
00563     } else {                                                            \
00564         str.Assign(icalproperty_enum_to_string(val));                   \
00565     }                                                                   \
00566     return NS_OK;                                                       \
00567 }                                                                       \
00568                                                                         \
00569 NS_IMETHODIMP                                                           \
00570 calIcalComponent::Set##Attrname(const nsACString &str)                  \
00571 {                                                                       \
00572     int val = icalproperty_string_to_enum(PromiseFlatCString(str).get()); \
00573     icalvalue *ival = icalvalue_new_##lcname((icalproperty_##lcname)val); \
00574     return SetPropertyValue(ICAL_##ICALNAME##_PROPERTY, ival);          \
00575 }                                                                       \
00576 
00577 #define COMP_STRING_TO_ENUM_ATTRIBUTE(Attrname, ICALNAME, lcname)       \
00578 NS_IMETHODIMP                                                           \
00579 calIcalComponent::Get##Attrname(nsACString &str)                        \
00580 {                                                                       \
00581     PRInt32 val;                                                        \
00582     nsresult rv = GetIntProperty(ICAL_##ICALNAME##_PROPERTY, &val);     \
00583     if (NS_FAILED(rv))                                                  \
00584         return rv;                                                      \
00585     if (val == -1) {                                                    \
00586         str.Truncate();                                                 \
00587         str.SetIsVoid(PR_TRUE);                                         \
00588     } else {                                                            \
00589         str.Assign(icalproperty_##lcname##_to_string((icalproperty_##lcname)val)); \
00590     }                                                                   \
00591     return NS_OK;                                                       \
00592 }                                                                       \
00593                                                                         \
00594 NS_IMETHODIMP                                                           \
00595 calIcalComponent::Set##Attrname(const nsACString &str)                  \
00596 {                                                                       \
00597     icalproperty *prop = nsnull;                                        \
00598     if (!str.IsVoid()) {                                                \
00599         icalproperty_##lcname val =                                     \
00600             icalproperty_string_to_##lcname(PromiseFlatCString(str).get()); \
00601         prop = icalproperty_new_##lcname(val);                          \
00602         if (!prop)                                                      \
00603             return NS_ERROR_OUT_OF_MEMORY; /* XXX map errno */          \
00604     }                                                                   \
00605     return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop);               \
00606 }                                                                       \
00607 
00608 #define COMP_GENERAL_STRING_ATTRIBUTE(Attrname, ICALNAME)       \
00609 NS_IMETHODIMP                                                   \
00610 calIcalComponent::Get##Attrname(nsACString &str)                \
00611 {                                                               \
00612     return GetStringProperty(ICAL_##ICALNAME##_PROPERTY, str);  \
00613 }                                                               \
00614                                                                 \
00615 NS_IMETHODIMP                                                   \
00616 calIcalComponent::Set##Attrname(const nsACString &str)          \
00617 {                                                               \
00618     return SetStringProperty(ICAL_##ICALNAME##_PROPERTY, str);  \
00619 }
00620 
00621 #define COMP_STRING_ATTRIBUTE(Attrname, ICALNAME, lcname)       \
00622 NS_IMETHODIMP                                                   \
00623 calIcalComponent::Get##Attrname(nsACString &str)                \
00624 {                                                               \
00625     return GetStringProperty(ICAL_##ICALNAME##_PROPERTY, str);  \
00626 }                                                               \
00627                                                                 \
00628 NS_IMETHODIMP                                                   \
00629 calIcalComponent::Set##Attrname(const nsACString &str)          \
00630 {                                                               \
00631     icalproperty *prop =                                        \
00632         icalproperty_new_##lcname(PromiseFlatCString(str).get()); \
00633     return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop);       \
00634 }
00635 
00636 #define COMP_GENERAL_INT_ATTRIBUTE(Attrname, ICALNAME)          \
00637 NS_IMETHODIMP                                                   \
00638 calIcalComponent::Get##Attrname(PRInt32 *valp)                  \
00639 {                                                               \
00640     return GetIntProperty(ICAL_##ICALNAME##_PROPERTY, valp);    \
00641 }                                                               \
00642                                                                 \
00643 NS_IMETHODIMP                                                   \
00644 calIcalComponent::Set##Attrname(PRInt32 val)                    \
00645 {                                                               \
00646     return SetIntProperty(ICAL_##ICALNAME##_PROPERTY, val);     \
00647 }                                                               \
00648 
00649 #define COMP_ENUM_ATTRIBUTE(Attrname, ICALNAME, lcname)         \
00650 NS_IMETHODIMP                                                   \
00651 calIcalComponent::Get##Attrname(PRInt32 *valp)                  \
00652 {                                                               \
00653     return GetIntProperty(ICAL_##ICALNAME##_PROPERTY, valp);    \
00654 }                                                               \
00655                                                                 \
00656 NS_IMETHODIMP                                                   \
00657 calIcalComponent::Set##Attrname(PRInt32 val)                    \
00658 {                                                               \
00659     icalproperty *prop =                                        \
00660       icalproperty_new_##lcname((icalproperty_##lcname)val);    \
00661     return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop);       \
00662 }                                                               \
00663 
00664 #define COMP_INT_ATTRIBUTE(Attrname, ICALNAME, lcname)          \
00665 NS_IMETHODIMP                                                   \
00666 calIcalComponent::Get##Attrname(PRInt32 *valp)                  \
00667 {                                                               \
00668     return GetIntProperty(ICAL_##ICALNAME##_PROPERTY, valp);    \
00669 }                                                               \
00670                                                                 \
00671 NS_IMETHODIMP                                                   \
00672 calIcalComponent::Set##Attrname(PRInt32 val)                    \
00673 {                                                               \
00674     icalproperty *prop = icalproperty_new_##lcname(val);        \
00675     return SetProperty(ICAL_##ICALNAME##_PROPERTY, prop);       \
00676 }                                                               \
00677 
00678 nsresult calIcalComponent::GetStringProperty(icalproperty_kind kind, nsACString &str)
00679 {
00680     icalproperty *prop = icalcomponent_get_first_property(mComponent, kind);
00681     if (!prop) {
00682         str.Truncate();
00683         str.SetIsVoid(PR_TRUE);
00684     } else {
00685         str.Assign(icalvalue_get_string(icalproperty_get_value(prop)));
00686     }
00687     return NS_OK;
00688 }
00689 
00690 nsresult calIcalComponent::SetStringProperty(icalproperty_kind kind,
00691                                              const nsACString &str)
00692 {
00693     icalvalue *val = nsnull;
00694     if (!str.IsVoid()) {
00695         val = icalvalue_new_string(PromiseFlatCString(str).get());
00696         if (!val)
00697             return NS_ERROR_OUT_OF_MEMORY;
00698     }
00699     return SetPropertyValue(kind, val);
00700 }
00701 
00702 nsresult calIcalComponent::GetIntProperty(icalproperty_kind kind, PRInt32 *valp)
00703 {
00704     icalproperty *prop = icalcomponent_get_first_property(mComponent, kind);
00705     if (!prop)
00706         *valp = calIIcalComponent::INVALID_VALUE;
00707     else
00708         *valp = (PRInt32)icalvalue_get_integer(icalproperty_get_value(prop));
00709     return NS_OK;
00710 }
00711 
00712 nsresult calIcalComponent::SetIntProperty(icalproperty_kind kind, PRInt32 i)
00713 {
00714     icalvalue *val = icalvalue_new_integer(i);
00715     if (!val)
00716         return NS_ERROR_OUT_OF_MEMORY;
00717     return SetPropertyValue(kind, val);
00718 }
00719 
00720 nsresult calIcalComponent::GetDateTimeAttribute(icalproperty_kind kind,
00721                                                 calIDateTime ** dtp)
00722 {
00723     NS_ENSURE_ARG_POINTER(dtp);
00724     icalproperty *prop = icalcomponent_get_first_property(mComponent, kind);
00725     if (!prop) {
00726         *dtp = nsnull;  /* invalid date */
00727         return NS_OK;
00728     }
00729     return calIcalProperty::getDatetime_(this, prop, dtp);
00730 }
00731 
00732 nsresult calIcalComponent::SetDateTimeAttribute(icalproperty_kind kind,
00733                                                 calIDateTime * dt)
00734 {
00735     ClearAllProperties(kind);
00736     PRBool isValid;
00737     if (!dt || NS_FAILED(dt->GetIsValid(&isValid)) || !isValid) {
00738         return NS_OK;
00739     }
00740     icalproperty *prop = icalproperty_new(kind);
00741     CAL_ENSURE_MEMORY(prop);
00742     nsresult rc = calIcalProperty::setDatetime_(this, prop, dt);
00743     if (NS_SUCCEEDED(rc))
00744         icalcomponent_add_property(mComponent, prop);
00745     else
00746         icalproperty_free(prop);
00747     return rc;
00748 }
00749 
00750 NS_IMETHODIMP
00751 calIcalProperty::SetValueAsDatetime(calIDateTime *dt)
00752 {
00753     NS_ENSURE_ARG_POINTER(dt);
00754     return setDatetime_(toIcalComponent(mParent), mProperty, dt);
00755 }
00756 
00757 nsresult calIcalProperty::setDatetime_(calIcalComponent * parent,
00758                                        icalproperty * prop,
00759                                        calIDateTime * dt)
00760 {
00761     NS_ENSURE_ARG_POINTER(prop);
00762     NS_ENSURE_ARG_POINTER(dt);
00763 
00764     icaltimetype itt;
00765     dt->ToIcalTime(&itt);
00766 
00767     if (parent) {
00768         if (!itt.is_utc && itt.zone) {
00769             nsCOMPtr<calITimezone> tz;
00770             nsresult rv = dt->GetTimezone(getter_AddRefs(tz));
00771             NS_ENSURE_SUCCESS(rv, rv);
00772             rv = parent->getParentVCalendarOrThis()->AddTimezoneReference(tz);
00773             NS_ENSURE_SUCCESS(rv, rv);
00774             icalparameter * const param = icalparameter_new_from_value_string(
00775                 ICAL_TZID_PARAMETER, icaltimezone_get_tzid(const_cast<icaltimezone *>(itt.zone)));
00776             icalproperty_set_parameter(prop, param);
00777         }
00778     } else if (!itt.is_date && !itt.is_utc && itt.zone) {
00779         // no parent to add the CTIMEZONE to: coerce DATETIMEs to UTC, DATEs to floating
00780         icaltimezone_convert_time(&itt,
00781                                   const_cast<icaltimezone *>(itt.zone),
00782                                   icaltimezone_get_utc_timezone());
00783         itt.zone = icaltimezone_get_utc_timezone();
00784         itt.is_utc = 1;
00785     }
00786 
00787     icalvalue * const val = icalvalue_new_datetime(itt);
00788     CAL_ENSURE_MEMORY(val);
00789     icalproperty_set_value(prop, val);
00790     return NS_OK;
00791 }
00792 
00793 #define RO_COMP_DATE_ATTRIBUTE(Attrname, ICALNAME)                      \
00794 NS_IMETHODIMP                                                           \
00795 calIcalComponent::Get##Attrname(calIDateTime **dtp)                     \
00796 {                                                                       \
00797     return GetDateTimeAttribute(ICAL_##ICALNAME##_PROPERTY, dtp);       \
00798 }
00799 
00800 #define COMP_DATE_ATTRIBUTE(Attrname, ICALNAME)                         \
00801 RO_COMP_DATE_ATTRIBUTE(Attrname, ICALNAME)                              \
00802                                                                         \
00803 NS_IMETHODIMP                                                           \
00804 calIcalComponent::Set##Attrname(calIDateTime *dt)                       \
00805 {                                                                       \
00806     return SetDateTimeAttribute(ICAL_##ICALNAME##_PROPERTY, dt);        \
00807 }
00808 
00809 #define RO_COMP_DURATION_ATTRIBUTE(Attrname, ICALNAME)                  \
00810 NS_IMETHODIMP                                                           \
00811 calIcalComponent::Get##Attrname(calIDuration **dtp)                     \
00812 {                                                                       \
00813     icalproperty *prop =                                                \
00814         icalcomponent_get_first_property(mComponent,                    \
00815                                          ICAL_##ICALNAME##_PROPERTY);   \
00816     if (!prop) {                                                        \
00817         *dtp = nsnull;  /* invalid duration */                          \
00818         return NS_OK;                                                   \
00819     }                                                                   \
00820     struct icaldurationtype idt =                                       \
00821         icalvalue_get_duration(icalproperty_get_value(prop));           \
00822     *dtp = new calDuration(&idt);                                       \
00823     CAL_ENSURE_MEMORY(*dtp);                                            \
00824     NS_ADDREF(*dtp);                                                    \
00825     return NS_OK;                                                       \
00826 }
00827 
00828 NS_IMPL_ISUPPORTS1(calIcalComponent, calIIcalComponent)
00829 
00830 NS_IMETHODIMP_(icalcomponent *)
00831 calIcalComponent::GetIcalComponent()
00832 {
00833     return mComponent;
00834 }
00835 
00836 NS_IMETHODIMP_(icaltimezone *)
00837 calIcalComponent::GetIcalTimezone()
00838 {
00839     NS_ASSERTION(icalcomponent_isa(mComponent) == ICAL_VTIMEZONE_COMPONENT, "no VTIMEZONE -- unexpected!");
00840     if (!mTimezone && (icalcomponent_isa(mComponent) == ICAL_VTIMEZONE_COMPONENT)) {
00841         // xxx todo: libical needs a parent VCALENDAR to retrieve a icaltimezone
00842         NS_ASSERTION(mParent, "VTIMEZONE has no parent!");
00843         if (mParent) {
00844             icalproperty * const tzidProp = icalcomponent_get_first_property(mComponent, ICAL_TZID_PROPERTY);
00845             NS_ASSERTION(tzidProp, "no TZID property in VTIMEZONE!?");
00846             if (tzidProp) {
00847                 mTimezone = icalcomponent_get_timezone(mParent->GetIcalComponent(),
00848                                                        icalvalue_get_string(icalproperty_get_value(tzidProp)));
00849             }
00850         }
00851     }
00852     return mTimezone;
00853 }
00854 
00855 NS_IMETHODIMP
00856 calIcalComponent::GetFirstSubcomponent(const nsACString& kind,
00857                                        calIIcalComponent **subcomp)
00858 {
00859     NS_ENSURE_ARG_POINTER(subcomp);
00860 
00861     icalcomponent_kind compkind =
00862         icalcomponent_string_to_kind(PromiseFlatCString(kind).get());
00863 
00864     // Maybe someday I'll support X-COMPONENTs
00865     if (compkind == ICAL_NO_COMPONENT || compkind == ICAL_X_COMPONENT)
00866         return NS_ERROR_INVALID_ARG;
00867 
00868     icalcomponent *ical =
00869         icalcomponent_get_first_component(mComponent, compkind);
00870     if (!ical) {
00871         *subcomp = nsnull;
00872         return NS_OK;
00873     }
00874 
00875     *subcomp = new calIcalComponent(ical, this);
00876     CAL_ENSURE_MEMORY(*subcomp);
00877     NS_ADDREF(*subcomp);
00878     return NS_OK;
00879 }
00880 
00881 NS_IMETHODIMP
00882 calIcalComponent::GetNextSubcomponent(const nsACString& kind,
00883                                       calIIcalComponent **subcomp)
00884 {
00885     NS_ENSURE_ARG_POINTER(subcomp);
00886 
00887     icalcomponent_kind compkind =
00888         icalcomponent_string_to_kind(PromiseFlatCString(kind).get());
00889 
00890     // Maybe someday I'll support X-COMPONENTs
00891     if (compkind == ICAL_NO_COMPONENT || compkind == ICAL_X_COMPONENT)
00892         return NS_ERROR_INVALID_ARG;
00893 
00894     icalcomponent *ical =
00895         icalcomponent_get_next_component(mComponent, compkind);
00896     if (!ical) {
00897         *subcomp = nsnull;
00898         return NS_OK;
00899     }
00900 
00901     *subcomp = new calIcalComponent(ical, this);
00902     CAL_ENSURE_MEMORY(*subcomp);
00903     NS_ADDREF(*subcomp);
00904     return NS_OK;
00905 }
00906 
00907 NS_IMETHODIMP
00908 calIcalComponent::GetComponentType(nsACString &componentType)
00909 {
00910     componentType.Assign(icalcomponent_kind_to_string(icalcomponent_isa(mComponent)));
00911     return NS_OK;
00912 }
00913 
00914 
00915 COMP_STRING_ATTRIBUTE(Uid, UID, uid)
00916 COMP_STRING_ATTRIBUTE(Prodid, PRODID, prodid)
00917 COMP_STRING_ATTRIBUTE(Version, VERSION, version)
00918 COMP_STRING_TO_ENUM_ATTRIBUTE(Method, METHOD, method)
00919 COMP_STRING_TO_ENUM_ATTRIBUTE(Status, STATUS, status)
00920 COMP_STRING_TO_GENERAL_ENUM_ATTRIBUTE(Transp, TRANSP, transp)
00921 COMP_STRING_ATTRIBUTE(Summary, SUMMARY, summary)
00922 COMP_STRING_ATTRIBUTE(Description, DESCRIPTION, description)
00923 COMP_STRING_ATTRIBUTE(Location, LOCATION, location)
00924 COMP_STRING_ATTRIBUTE(Categories, CATEGORIES, categories)
00925 COMP_STRING_ATTRIBUTE(URL, URL, url)
00926 COMP_INT_ATTRIBUTE(Priority, PRIORITY, priority)
00927 COMP_STRING_TO_GENERAL_ENUM_ATTRIBUTE(IcalClass, CLASS, class)
00928 RO_COMP_DURATION_ATTRIBUTE(Duration, DURATION)
00929 COMP_DATE_ATTRIBUTE(StartTime, DTSTART)
00930 COMP_DATE_ATTRIBUTE(EndTime, DTEND)
00931 COMP_DATE_ATTRIBUTE(DueTime, DUE)
00932 COMP_DATE_ATTRIBUTE(StampTime, DTSTAMP)
00933 COMP_DATE_ATTRIBUTE(LastModified, LASTMODIFIED)
00934 COMP_DATE_ATTRIBUTE(CreatedTime, CREATED)
00935 COMP_DATE_ATTRIBUTE(CompletedTime, COMPLETED)
00936 COMP_DATE_ATTRIBUTE(RecurrenceId, RECURRENCEID)
00937 
00938 void calIcalComponent::ClearAllProperties(icalproperty_kind kind)
00939 {
00940     for (icalproperty *prop = icalcomponent_get_first_property(mComponent, kind), *next;
00941          prop; prop = next)
00942     {
00943         next = icalcomponent_get_next_property(mComponent, kind);
00944         icalcomponent_remove_property(mComponent, prop);
00945         icalproperty_free(prop);
00946     }
00947 }
00948 
00949 PR_STATIC_CALLBACK(PLDHashOperator)
00950 AddTimezoneComponentToIcal(nsACString const& /*tzid*/, calITimezone * tz, void * arg)
00951 {
00952     icalcomponent * const comp = static_cast<icalcomponent *>(arg);
00953     icaltimezone * icaltz = cal::getIcalTimezone(tz);
00954     if (icaltz) {
00955         icalcomponent * const tzcomp = icalcomponent_new_clone(icaltimezone_get_component(icaltz));
00956         icalcomponent_add_component(comp, tzcomp);
00957     }
00958     return PL_DHASH_NEXT;
00959 }
00960 
00961 NS_IMETHODIMP
00962 calIcalComponent::SerializeToICS(nsACString &serialized)
00963 {
00964     char *icalstr;
00965 
00966     nsresult rv = Serialize(&icalstr);
00967     if (NS_FAILED(rv)) {
00968         return rv;
00969     }
00970 
00971     serialized.Assign(icalstr);
00972     return NS_OK;
00973 }
00974 
00975 NS_IMETHODIMP
00976 calIcalComponent::ToString(nsACString& aResult)
00977 {
00978     return SerializeToICS(aResult);
00979 }
00980 
00981 NS_IMETHODIMP
00982 calIcalComponent::SerializeToICSStream(nsIInputStream **aStreamResult)
00983 {
00984     NS_ENSURE_ARG_POINTER(aStreamResult);
00985 
00986     char *icalstr;
00987     nsresult rv = Serialize(&icalstr);
00988     NS_ENSURE_SUCCESS(rv, rv);
00989 
00990     nsCOMPtr<nsIStringInputStream> const aStringStream(
00991         do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv));
00992     NS_ENSURE_SUCCESS(rv, rv);
00993     // copies the string into the input stream that's handed back.
00994     // This copy is necessary because we don't really own icalstr;
00995     // it's one of libical's ring buffers
00996     rv = aStringStream->SetData(icalstr, -1);
00997     NS_ENSURE_SUCCESS(rv, rv);
00998     NS_ADDREF(*aStreamResult = aStringStream);
00999     return rv;
01000 }
01001 
01002 nsresult
01003 calIcalComponent::Serialize(char **icalstr)
01004 {
01005     NS_ENSURE_ARG_POINTER(icalstr);
01006 
01007     // add the timezone bits
01008     if (icalcomponent_isa(mComponent) == ICAL_VCALENDAR_COMPONENT && mReferencedTimezones.Count() > 0) {
01009         mReferencedTimezones.EnumerateRead(AddTimezoneComponentToIcal, mComponent);
01010     }
01011 
01012     *icalstr = icalcomponent_as_ical_string(mComponent);
01013     if (!*icalstr) {
01014         // xxx todo: what about NS_ERROR_OUT_OF_MEMORY?
01015 #ifdef DEBUG
01016         fprintf(stderr, "Error serializing: %d (%s)\n",
01017                 icalerrno, icalerror_strerror(icalerrno));
01018 #endif
01019         // The return values in calIError match with libical errnos,
01020         // so no need for a conversion table or anything.
01021         return calIErrors::ICS_ERROR_BASE + icalerrno;
01022     }
01023 
01024     return NS_OK;
01025 }
01026 
01027 NS_IMETHODIMP
01028 calIcalComponent::Clone(calIIcalComponent **_retval)
01029 {
01030     NS_ENSURE_ARG_POINTER(_retval);
01031     icalcomponent * cloned = icalcomponent_new_clone(mComponent);
01032     if (cloned == nsnull)
01033         return NS_ERROR_OUT_OF_MEMORY;
01034     calIcalComponent * const comp = new calIcalComponent(cloned, nsnull, getTzProvider());
01035     if (comp == nsnull) {
01036         icalcomponent_free(cloned);
01037         return NS_ERROR_OUT_OF_MEMORY;
01038     }
01039     NS_ADDREF(*_retval = comp);
01040     return NS_OK;
01041 }
01042 
01043 NS_IMETHODIMP
01044 calIcalComponent::AddSubcomponent(calIIcalComponent *comp)
01045 {
01046     NS_ENSURE_ARG_POINTER(comp);
01047 
01048     /* XXX mildly unsafe assumption here.
01049      * To fix it, I will:
01050      * - check the object's classinfo to find out if I have one of my
01051      *   own objects, and if not
01052      * - use comp->serializeToICS and reparse to create a copy.
01053      *
01054      * I should probably also return the new/reused component so that the
01055      * caller has something it can poke at all live-like.
01056      */
01057     calIcalComponent * const ical = toIcalComponent(comp);
01058 
01059     PRUint32 tzCount = 0;
01060     calITimezone ** timezones = nsnull;
01061     nsresult rv = ical->GetReferencedTimezones(&tzCount, &timezones);
01062     NS_ENSURE_SUCCESS(rv, rv);
01063 
01064     calIcalComponent * const vcal = getParentVCalendarOrThis();
01065     PRBool failed = PR_FALSE;
01066     for (PRUint32 i = 0; i < tzCount; i++) {
01067         if (!failed) {
01068             rv = vcal->AddTimezoneReference(timezones[i]);
01069             if (NS_FAILED(rv))
01070                 failed = PR_TRUE;
01071         }
01072 
01073         NS_RELEASE(timezones[i]);
01074     }
01075 
01076     nsMemory::Free(timezones);
01077 
01078     if (failed)
01079         return rv;
01080 
01081     if (ical->mParent) {
01082         ical->mComponent = icalcomponent_new_clone(ical->mComponent);
01083     }
01084     ical->mParent = this;
01085     icalcomponent_add_component(mComponent, ical->mComponent);
01086     return NS_OK;
01087 }
01088 
01089 // NS_IMETHODIMP
01090 // IcalComponent::RemoveSubcomponent(calIIcalComponent *comp)
01091 // {
01092 //     NS_ENSURE_ARG_POINTER(comp);
01093 //     calIcalComponent *ical = static_cast<calIcalComponent *>(comp);
01094 //     icalcomponent_remove_component(mComponent, ical->mComponent);
01095 //     ical->mParent = nsnull;
01096 //     return NS_OK;
01097 // }
01098 
01099 NS_IMETHODIMP
01100 calIcalComponent::GetFirstProperty(const nsACString &kind,
01101                                    calIIcalProperty **prop)
01102 {
01103     NS_ENSURE_ARG_POINTER(prop);
01104 
01105     icalproperty_kind propkind =
01106         icalproperty_string_to_kind(PromiseFlatCString(kind).get());
01107 
01108     if (propkind == ICAL_NO_PROPERTY)
01109         return NS_ERROR_INVALID_ARG;
01110 
01111     icalproperty *icalprop = nsnull;
01112     if (propkind == ICAL_X_PROPERTY) {
01113         for (icalprop = 
01114                  icalcomponent_get_first_property(mComponent, ICAL_X_PROPERTY);
01115              icalprop;
01116              icalprop = icalcomponent_get_next_property(mComponent,
01117                                                         ICAL_X_PROPERTY)) {
01118             
01119             if (kind.Equals(icalproperty_get_x_name(icalprop)))
01120                 break;
01121         }
01122     } else {
01123         icalprop = icalcomponent_get_first_property(mComponent, propkind);
01124     }
01125 
01126     if (!icalprop) {
01127         *prop = nsnull;
01128         return NS_OK;
01129     }
01130 
01131     *prop = new calIcalProperty(icalprop, this);
01132     CAL_ENSURE_MEMORY(*prop);
01133     NS_ADDREF(*prop);
01134     return NS_OK;
01135 }
01136 
01137 NS_IMETHODIMP
01138 calIcalComponent::GetNextProperty(const nsACString &kind, calIIcalProperty **prop)
01139 {
01140     NS_ENSURE_ARG_POINTER(prop);
01141 
01142     icalproperty_kind propkind =
01143         icalproperty_string_to_kind(PromiseFlatCString(kind).get());
01144 
01145     if (propkind == ICAL_NO_PROPERTY)
01146         return NS_ERROR_INVALID_ARG;
01147     icalproperty *icalprop = nsnull;
01148     if (propkind == ICAL_X_PROPERTY) {
01149         for (icalprop = 
01150                  icalcomponent_get_next_property(mComponent, ICAL_X_PROPERTY);
01151              icalprop;
01152              icalprop = icalcomponent_get_next_property(mComponent,
01153                                                         ICAL_X_PROPERTY)) {
01154             
01155             if (kind.Equals(icalproperty_get_x_name(icalprop)))
01156                 break;
01157         }
01158     } else {
01159         icalprop = icalcomponent_get_next_property(mComponent, propkind);
01160     }
01161 
01162     if (!icalprop) {
01163         *prop = nsnull;
01164         return NS_OK;
01165     }
01166 
01167     *prop = new calIcalProperty(icalprop, this);
01168     CAL_ENSURE_MEMORY(*prop);
01169     NS_ADDREF(*prop);
01170     return NS_OK;
01171 }
01172 
01173 NS_IMETHODIMP
01174 calIcalComponent::AddProperty(calIIcalProperty * prop)
01175 {
01176     NS_ENSURE_ARG_POINTER(prop);
01177     // We assume a calIcalProperty is passed in (else the cast wouldn't run and
01178     // we are about to crash), so we assume that this ICS service code has created
01179     // the property.
01180     calIcalProperty * const ical = toIcalProperty(prop);
01181     if (ical->mParent) {
01182         ical->mProperty = icalproperty_new_clone(ical->mProperty);
01183     }
01184     ical->mParent = this;
01185     icalcomponent_add_property(mComponent, ical->mProperty);
01186 
01187     nsCOMPtr<calIDateTime> dt;
01188     if (NS_SUCCEEDED(prop->GetValueAsDatetime(getter_AddRefs(dt))) && dt) {
01189         // make sure timezone definition will be included:
01190         nsCOMPtr<calITimezone> tz;
01191         if (NS_SUCCEEDED(dt->GetTimezone(getter_AddRefs(tz))) && tz) {
01192             getParentVCalendarOrThis()->AddTimezoneReference(tz);
01193         }
01194     }
01195     return NS_OK;
01196 }
01197 
01198 // If you add then remove a property/component, the referenced
01199 // timezones won't get purged out. There's currently no client code.
01200 
01201 // NS_IMETHODIMP
01202 // calIcalComponent::RemoveProperty(calIIcalProperty *prop)
01203 // {
01204 //     NS_ENSURE_ARG_POINTER(prop);
01205 //     // XXX like AddSubcomponent, this is questionable
01206 //     calIcalProperty *ical = static_cast<calIcalProperty *>(prop);
01207 //     icalcomponent_remove_property(mComponent, ical->mProperty);
01208 //     ical->mParent = nsnull;
01209 //     return NS_OK;
01210 // }
01211 
01212 NS_IMPL_ISUPPORTS1_CI(calICSService, calIICSService)
01213 
01214 calICSService::calICSService()
01215 {
01216 }
01217 
01218 NS_IMETHODIMP
01219 calICSService::ParseICS(const nsACString& serialized,
01220                         calITimezoneProvider *tzProvider,
01221                         calIIcalComponent **component)
01222 {
01223     NS_ENSURE_ARG_POINTER(component);
01224     icalcomponent *ical =
01225         icalparser_parse_string(PromiseFlatCString(serialized).get());
01226     if (!ical) {
01227 #ifdef DEBUG
01228         fprintf(stderr, "Error parsing: '%20s': %d (%s)\n",
01229                 PromiseFlatCString(serialized).get(), icalerrno,
01230                 icalerror_strerror(icalerrno));
01231 #endif
01232         // The return values is calIError match with ical errors,
01233         // so no need for a conversion table or anything.
01234         return calIErrors::ICS_ERROR_BASE + icalerrno;
01235     }
01236     calIcalComponent *comp = new calIcalComponent(ical, nsnull, tzProvider);
01237     if (!comp) {
01238         icalcomponent_free(ical);
01239         return NS_ERROR_OUT_OF_MEMORY;
01240     }
01241     NS_ADDREF(*component = comp);
01242     return NS_OK;
01243 }
01244 
01245 NS_IMETHODIMP
01246 calICSService::CreateIcalComponent(const nsACString &kind, calIIcalComponent **comp)
01247 {
01248     NS_ENSURE_ARG_POINTER(comp);
01249     icalcomponent_kind compkind = 
01250         icalcomponent_string_to_kind(PromiseFlatCString(kind).get());
01251 
01252     // Maybe someday I'll support X-COMPONENTs
01253     if (compkind == ICAL_NO_COMPONENT || compkind == ICAL_X_COMPONENT)
01254         return NS_ERROR_INVALID_ARG;
01255 
01256     icalcomponent *ical = icalcomponent_new(compkind);
01257     if (!ical)
01258         return NS_ERROR_OUT_OF_MEMORY; // XXX translate
01259 
01260     *comp = new calIcalComponent(ical, nsnull);
01261     if (!*comp) {
01262         icalcomponent_free(ical);
01263         return NS_ERROR_OUT_OF_MEMORY;
01264     }
01265 
01266     NS_ADDREF(*comp);
01267     return NS_OK;
01268 }
01269 
01270 NS_IMETHODIMP
01271 calICSService::CreateIcalProperty(const nsACString &kind, calIIcalProperty **prop)
01272 {
01273     NS_ENSURE_ARG_POINTER(prop);
01274     icalproperty_kind propkind = 
01275         icalproperty_string_to_kind(PromiseFlatCString(kind).get());
01276 
01277     if (propkind == ICAL_NO_PROPERTY)
01278         return NS_ERROR_INVALID_ARG;
01279 
01280     icalproperty *icalprop = icalproperty_new(propkind);
01281     if (!icalprop)
01282         return NS_ERROR_OUT_OF_MEMORY; // XXX translate
01283 
01284     if (propkind == ICAL_X_PROPERTY)
01285         icalproperty_set_x_name(icalprop, PromiseFlatCString(kind).get());
01286 
01287     *prop = new calIcalProperty(icalprop, nsnull);
01288     CAL_ENSURE_MEMORY(*prop);
01289     NS_ADDREF(*prop);
01290     return NS_OK;
01291 }