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