* Update ical_dezonify() to use system tzdata
[citadel.git] / webcit / ical_dezonify.c
1 /* 
2  * $Id$ 
3  * 
4  * Function to go through an ical component set and convert all non-UTC
5  * date/time properties to UTC.  It also strips out any VTIMEZONE
6  * subcomponents afterwards, because they're irrelevant.
7  *
8  * Everything here will work on both a fully encapsulated VCALENDAR component
9  * or any type of subcomponent.
10  *
11  */
12
13 #include "webcit.h"
14 #include "webserver.h"
15
16 #define DBG_ICAL        1
17
18
19 /*
20  * Figure out which time zone needs to be used for timestamps that are
21  * not UTC and do not have a time zone specified.
22  *
23  */
24 icaltimezone *get_default_icaltimezone(void) {
25
26         icaltimezone *zone = NULL;
27         char *default_zone_name = serv_info.serv_default_cal_zone;
28
29         if (!zone) {
30                 zone = icaltimezone_get_builtin_timezone(default_zone_name);
31         }
32         if (!zone) {
33                 lprintf(1, "Unable to load '%s' time zone.  Defaulting to UTC.\n", default_zone_name);
34                 zone = icaltimezone_get_utc_timezone();
35         }
36         if (!zone) {
37                 lprintf(1, "Unable to load UTC time zone!\n");
38         }
39         return zone;
40 }
41
42
43 /*
44  * Back end function for ical_dezonify()
45  *
46  * We supply this with the master component, the relevant component,
47  * and the property (which will be a DTSTART, DTEND, etc.)
48  * which we want to convert to UTC.
49  */
50 void ical_dezonify_backend(icalcomponent *cal,
51                         icalcomponent *rcal,
52                         icalproperty *prop) {
53
54         icaltimezone *t = NULL;
55         icalparameter *param;
56         const char *tzid = NULL;
57         struct icaltimetype TheTime;
58         int utc_declared_as_tzid = 0;   /* Component declared 'TZID=GMT' instead of using Z syntax */
59
60         /* Give me nothing and I will give you nothing in return. */
61         if (cal == NULL) return;
62
63         /* Hunt for a TZID parameter in this property. */
64         param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER);
65
66         /* Get the stringish name of this TZID. */
67         if (param != NULL) {
68                 tzid = icalparameter_get_tzid(param);
69
70                 /* Convert it to an icaltimezone type. */
71                 if (tzid != NULL) {
72 #ifdef DBG_ICAL
73                         lprintf(9, "                * Stringy supplied timezone is: '%s'\n", tzid);
74 #endif
75                         if ( (!strcasecmp(tzid, "UTC")) || (!strcasecmp(tzid, "GMT")) ) {
76                                 utc_declared_as_tzid = 1;
77 #ifdef DBG_ICAL
78                                 lprintf(9, "                * ...and we handle that internally.\n");
79 #endif
80                         }
81                         else {
82                                 /* try attached first */
83                                 t = icalcomponent_get_timezone(cal, tzid);
84 #ifdef DBG_ICAL
85                                 lprintf(9, "                * ...and I %s have tzdata for that zone.\n",
86                                         (t ? "DO" : "DO NOT")
87                                 );
88 #endif
89                                 /* then try built-in timezones */
90                                 if (!t) {
91                                         t = icaltimezone_get_builtin_timezone(tzid);
92 #ifdef DBG_ICAL
93                                         if (t) {
94                                                 lprintf(9, "                * Using system tzdata!\n");
95                                         }
96 #endif
97                                 }
98                         }
99                 }
100
101         }
102
103         /* Now we know the timezone.  Convert to UTC. */
104
105         if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) {
106                 TheTime = icalproperty_get_dtstart(prop);
107         }
108         else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) {
109                 TheTime = icalproperty_get_dtend(prop);
110         }
111         else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) {
112                 TheTime = icalproperty_get_due(prop);
113         }
114         else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) {
115                 TheTime = icalproperty_get_exdate(prop);
116         }
117         else {
118                 return;
119         }
120
121 #ifdef DBG_ICAL
122         lprintf(9, "                * Was: %s\n", icaltime_as_ical_string(TheTime));
123 #endif
124
125         if (TheTime.is_utc) {
126 #ifdef DBG_ICAL
127                 lprintf(9, "                * This property is ALREADY UTC.\n");
128 #endif
129         }
130
131         else if (utc_declared_as_tzid) {
132 #ifdef DBG_ICAL
133                 lprintf(9, "                * Replacing '%s' TZID with 'Z' suffix.\n", tzid);
134 #endif
135                 TheTime.is_utc = 1;
136         }
137
138         else {
139                 /* Do the conversion. */
140                 if (t != NULL) {
141 #ifdef DBG_ICAL
142                         lprintf(9, "                * Timezone prop found.  Converting to UTC.\n");
143 #endif
144                 }
145                 else {
146 #ifdef DBG_ICAL
147                         lprintf(9, "                * Converting default timezone to UTC.\n");
148 #endif
149                 }
150
151                 if (t == NULL) {
152                         t = get_default_icaltimezone();
153                 }
154                 icaltimezone_convert_time(&TheTime, t, icaltimezone_get_utc_timezone());
155                 TheTime.is_utc = 1;
156         }
157
158         icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER);
159 #ifdef DBG_ICAL
160         lprintf(9, "                * Now: %s\n", icaltime_as_ical_string(TheTime));
161 #endif
162
163         /* Now add the converted property back in. */
164         if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) {
165                 icalproperty_set_dtstart(prop, TheTime);
166         }
167         else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) {
168                 icalproperty_set_dtend(prop, TheTime);
169         }
170         else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) {
171                 icalproperty_set_due(prop, TheTime);
172         }
173         else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) {
174                 icalproperty_set_exdate(prop, TheTime);
175         }
176 }
177
178
179 /*
180  * Recursive portion of ical_dezonify()
181  */
182 void ical_dezonify_recurse(icalcomponent *cal, icalcomponent *rcal) {
183         icalcomponent *c;
184         icalproperty *p;
185
186         /*
187          * Recurse through all subcomponents *except* VTIMEZONE ones.
188          */
189         for (c=icalcomponent_get_first_component(
190                                         rcal, ICAL_ANY_COMPONENT);
191                 c != NULL;
192                 c = icalcomponent_get_next_component(
193                                         rcal, ICAL_ANY_COMPONENT)
194         ) {
195                 if (icalcomponent_isa(c) != ICAL_VTIMEZONE_COMPONENT) {
196                         ical_dezonify_recurse(cal, c);
197                 }
198         }
199
200         /*
201          * Now look for DTSTART and DTEND properties
202          */
203         for (p=icalcomponent_get_first_property(rcal, ICAL_ANY_PROPERTY);
204                 p != NULL;
205                 p = icalcomponent_get_next_property(rcal, ICAL_ANY_PROPERTY)
206         ) {
207                 if (
208                         (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY)
209                         || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY)
210                         || (icalproperty_isa(p) == ICAL_DUE_PROPERTY)
211                         || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY)
212                    ) {
213                         ical_dezonify_backend(cal, rcal, p);
214                 }
215         }
216 }
217
218
219 /*
220  * Convert all DTSTART and DTEND properties in all subcomponents to UTC.
221  * This function will search any VTIMEZONE subcomponents to learn the
222  * relevant timezone information.
223  */
224 void ical_dezonify(icalcomponent *cal) {
225         icalcomponent *vt = NULL;
226
227 #ifdef DBG_ICAL
228         lprintf(9, "ical_dezonify() started\n");
229 #endif
230
231         /* Convert all times to UTC */
232         ical_dezonify_recurse(cal, cal);
233
234         /* Strip out VTIMEZONE subcomponents -- we don't need them anymore */
235         while (vt = icalcomponent_get_first_component(
236                         cal, ICAL_VTIMEZONE_COMPONENT), vt != NULL) {
237                 icalcomponent_remove_component(cal, vt);
238                 icalcomponent_free(vt);
239         }
240
241 #ifdef DBG_ICAL
242         lprintf(9, "ical_dezonify() completed\n");
243 #endif
244 }
245