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