Mailing list header changes (fuck you Google)
[citadel.git] / webcit / ical_subst.c
1 /*
2  * Copyright (c) 1996-2012 by the citadel.org team
3  *
4  * This program is open source software.  You can redistribute it and/or
5  * modify it under the terms of the GNU General Public License, version 3.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  */
12
13 #include "webcit.h"
14
15 extern IcalKindEnumMap icalproperty_kind_map[];
16 extern IcalMethodEnumMap icalproperty_method_map[];
17
18 HashList *IcalComponentMap = NULL;
19 CtxType CTX_ICAL = CTX_NONE;
20 CtxType CTX_ICALPROPERTY = CTX_NONE;
21 CtxType CTX_ICALMETHOD = CTX_NONE;
22 CtxType CTX_ICALTIME = CTX_NONE;
23 CtxType CTX_ICALATTENDEE = CTX_NONE;
24 CtxType CTX_ICALCONFLICT = CTX_NONE;
25
26 void tmplput_ICalItem(StrBuf *Target, WCTemplputParams *TP)
27 {
28         icalcomponent *cal = (icalcomponent *) CTX(CTX_ICAL);
29         icalproperty *p;
30         icalproperty_kind Kind;
31         const char *str;
32
33         Kind = (icalproperty_kind) GetTemplateTokenNumber(Target, TP, 0, ICAL_ANY_PROPERTY);
34         p = icalcomponent_get_first_property(cal, Kind);
35         if (p != NULL) {
36                 str = icalproperty_get_comment (p);
37                 StrBufAppendTemplateStr(Target, TP, str, 1);
38         }
39 }
40
41 void tmplput_CtxICalProperty(StrBuf *Target, WCTemplputParams *TP)
42 {
43         icalproperty *p = (icalproperty *) CTX(CTX_ICALPROPERTY);
44         const char *str;
45
46         str = icalproperty_get_comment (p);
47         StrBufAppendTemplateStr(Target, TP, str, 0);
48 }
49
50 int ReleaseIcalSubCtx(StrBuf *Target, WCTemplputParams *TP)
51 {
52         WCTemplputParams *TPP = TP;
53         UnStackContext(TP);
54         free(TPP);
55         return 0;
56 }
57 int cond_ICalIsA(StrBuf *Target, WCTemplputParams *TP)
58 {
59         icalcomponent *cal = (icalcomponent *) CTX(CTX_ICAL);
60         icalcomponent_kind c = GetTemplateTokenNumber(Target, TP, 2, ICAL_NO_COMPONENT);
61         return icalcomponent_isa(cal) == c;
62 }
63
64 int cond_ICalHaveItem(StrBuf *Target, WCTemplputParams *TP)
65 {
66         icalcomponent *cal = (icalcomponent *) CTX(CTX_ICAL);
67         icalproperty *p;
68         icalproperty_kind Kind;
69
70         Kind = (icalproperty_kind) GetTemplateTokenNumber(Target, TP, 2, ICAL_ANY_PROPERTY);
71         p = icalcomponent_get_first_property(cal, Kind);
72         if (p != NULL) {
73                 WCTemplputParams *DynamicTP;
74         
75                 DynamicTP = (WCTemplputParams*) malloc(sizeof(WCTemplputParams));
76                 StackDynamicContext (TP, 
77                                      DynamicTP, 
78                                      p,
79                                      CTX_ICALPROPERTY,
80                                      0,
81                                      TP->Tokens,
82                                      ReleaseIcalSubCtx,
83                                      TP->Tokens->Params[1]->lvalue);
84
85                 return 1;
86         }
87         return 0;
88 }
89
90 int ReleaseIcalTimeCtx(StrBuf *Target, WCTemplputParams *TP)
91 {
92         WCTemplputParams *TPP = TP;
93
94         UnStackContext(TP);
95         free(TPP);
96         return 0;
97 }
98
99 int cond_ICalHaveTimeItem(StrBuf *Target, WCTemplputParams *TP)
100 {
101         icalcomponent *cal = (icalcomponent *) CTX(CTX_ICAL);
102         icalproperty *p;
103         icalproperty_kind Kind;
104
105         Kind = (icalproperty_kind) GetTemplateTokenNumber(Target, TP, 2, ICAL_ANY_PROPERTY);
106         p = icalcomponent_get_first_property(cal, Kind);
107         if (p != NULL) {
108                 struct icaltimetype *t;
109                 struct icaltimetype tt;
110                 WCTemplputParams *DynamicTP;
111
112                 DynamicTP = (WCTemplputParams*) malloc(sizeof(WCTemplputParams) + 
113                                                        sizeof(struct icaltimetype));
114                 t = (struct icaltimetype *) &DynamicTP[1];
115                 memset(&tt, 0, sizeof(struct icaltimetype));
116                 switch (Kind)
117                 {
118                 case ICAL_DTSTART_PROPERTY:
119                         tt = icalproperty_get_dtstart(p);
120                         break;
121                 case ICAL_DTEND_PROPERTY:
122                         tt = icalproperty_get_dtend(p);
123                         break;
124                 default:
125                         break;
126                 }
127                 memcpy(t, &tt, sizeof(struct icaltimetype));
128
129                 StackDynamicContext (TP, 
130                                      DynamicTP, 
131                                      t,
132                                      CTX_ICALTIME,
133                                      0,
134                                      TP->Tokens,
135                                      ReleaseIcalTimeCtx,
136                                      TP->Tokens->Params[1]->lvalue);
137
138                 return 1;
139         }
140         return 0;
141 }
142
143
144 int cond_ICalTimeIsDate(StrBuf *Target, WCTemplputParams *TP)
145 {
146         struct icaltimetype *t = (struct icaltimetype *) CTX(CTX_ICALTIME);
147         return t->is_date;
148 }
149
150 void tmplput_ICalTime_Date(StrBuf *Target, WCTemplputParams *TP)
151 {
152         struct tm d_tm;
153         long len;
154         char buf[256];
155         struct icaltimetype *t = (struct icaltimetype *) CTX(CTX_ICALTIME);
156
157         memset(&d_tm, 0, sizeof d_tm);
158         d_tm.tm_year = t->year - 1900;
159         d_tm.tm_mon = t->month - 1;
160         d_tm.tm_mday = t->day;
161         len = wc_strftime(buf, sizeof(buf), "%x", &d_tm);
162         StrBufAppendBufPlain(Target, buf, len, 0);
163 }
164 void tmplput_ICalTime_Time(StrBuf *Target, WCTemplputParams *TP)
165 {
166         long len;
167         char buf[256];
168         struct icaltimetype *t = (struct icaltimetype *) CTX(CTX_ICALTIME);
169         time_t tt;
170
171         tt = icaltime_as_timet(*t);
172         len = webcit_fmt_date(buf, sizeof(buf), tt, DATEFMT_FULL);
173         StrBufAppendBufPlain(Target, buf, len, 0);
174 }
175
176 void tmplput_ICalDate(StrBuf *Target, WCTemplputParams *TP)
177 {
178         icalcomponent *cal = (icalcomponent *) CTX(CTX_ICAL);
179         icalproperty *p;
180         icalproperty_kind Kind;
181         struct icaltimetype t;
182         time_t tt;
183         char buf[256];
184
185         Kind = (icalproperty_kind) GetTemplateTokenNumber(Target, TP, 0, ICAL_ANY_PROPERTY);
186         p = icalcomponent_get_first_property(cal, Kind);
187         if (p != NULL) {
188                 long len;
189                 t = icalproperty_get_dtend(p);
190                 tt = icaltime_as_timet(t);
191                 len = webcit_fmt_date(buf, 256, tt, DATEFMT_FULL);
192                 StrBufAppendBufPlain(Target, buf, len, 0);
193         }
194 }
195
196 void tmplput_CtxICalPropertyDate(StrBuf *Target, WCTemplputParams *TP)
197 {
198         icalproperty *p = (icalproperty *) CTX(CTX_ICALPROPERTY);
199         struct icaltimetype t;
200         time_t tt;
201         char buf[256];
202
203         long len;
204         t = icalproperty_get_dtend(p);
205         tt = icaltime_as_timet(t);
206         len = webcit_fmt_date(buf, sizeof(buf), tt, DATEFMT_FULL);
207         StrBufAppendBufPlain(Target, buf, len, 0);
208 }
209
210
211
212 void render_MIME_ICS_TPL(StrBuf *Target, WCTemplputParams *TP, StrBuf *FoundCharset)
213 {
214         wc_mime_attachment *Mime = CTX(CTX_MIME_ATACH);
215         icalproperty_method the_method = ICAL_METHOD_NONE;
216         icalproperty *method = NULL;
217         icalcomponent *cal = NULL;
218         icalcomponent *c = NULL;
219         WCTemplputParams SubTP;
220         WCTemplputParams SuperTP;
221
222         static int divcount = 0;
223
224         if (StrLength(Mime->Data) == 0) {
225                 MimeLoadData(Mime);
226         }
227         if (StrLength(Mime->Data) > 0) {
228                 cal = icalcomponent_new_from_string(ChrPtr(Mime->Data));
229         }
230         if (cal == NULL) {
231                 StrBufAppendPrintf(Mime->Data, _("There was an error parsing this calendar item."));
232                 StrBufAppendPrintf(Mime->Data, "<br>\n");
233                 return;
234         }
235
236         putlbstr("divname",  ++divcount);
237
238
239         putbstr("cal_partnum", NewStrBufDup(Mime->PartNum));
240         putlbstr("msgnum", Mime->msgnum);
241
242         memset(&SubTP, 0, sizeof(WCTemplputParams));
243         memset(&SuperTP, 0, sizeof(WCTemplputParams));
244
245         /*//ical_dezonify(cal); */
246
247         /* If the component has subcomponents, recurse through them. */
248         c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
249         c = (c != NULL) ? c : cal;
250
251         method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
252         if (method != NULL) {
253                 the_method = icalproperty_get_method(method);
254         }
255
256         StackContext (TP,
257                       &SuperTP,
258                       &the_method,
259                       CTX_ICALMETHOD,
260                       0,
261                       TP->Tokens);
262
263         StackContext (&SuperTP, 
264                       &SubTP, 
265                       c,
266                       CTX_ICAL,
267                       0,
268                       SuperTP.Tokens);
269         FlushStrBuf(Mime->Data);
270 ///     DoTemplate(HKEY("ical_attachment_display"), Mime->Data, &SubTP);
271         DoTemplate(HKEY("ical_edit"), Mime->Data, &SubTP);
272
273         /*/ cal_process_object(Mime->Data, cal, 0, Mime->msgnum, ChrPtr(Mime->PartNum)); */
274
275         /* Free the memory we obtained from libical's constructor */
276         StrBufPlain(Mime->ContentType, HKEY("text/html"));
277         StrBufAppendPrintf(WC->trailing_javascript,
278                 "eventEditAllDay();             \n"
279                 "RecurrenceShowHide();          \n"
280                 "EnableOrDisableCheckButton();  \n"
281         );
282
283         UnStackContext(&SuperTP);
284         UnStackContext(&SubTP);
285         icalcomponent_free(cal);
286 }
287 void CreateIcalComponendKindLookup(void)
288 {
289         int i = 0;
290
291         IcalComponentMap = NewHash (1, NULL);
292         while (icalproperty_kind_map[i].NameLen != 0) {
293                 RegisterNS(icalproperty_kind_map[i].Name, 
294                            icalproperty_kind_map[i].NameLen, 
295                            0, 
296                            10, 
297                            tmplput_ICalItem,
298                            NULL, 
299                            CTX_ICAL);
300                 Put(IcalComponentMap, 
301                     icalproperty_kind_map[i].Name, 
302                     icalproperty_kind_map[i].NameLen, 
303                     &icalproperty_kind_map[i],
304                     reference_free_handler);
305                            
306                            
307                 i++;
308         }
309 }
310
311
312
313
314 int cond_ICalIsMethod(StrBuf *Target, WCTemplputParams *TP)
315 {
316         icalproperty_method *the_method = (icalproperty_method *) CTX(CTX_ICALMETHOD);
317         icalproperty_method which_method;
318
319         which_method = GetTemplateTokenNumber(Target, TP, 2, ICAL_METHOD_X);
320         return *the_method == which_method;
321 }
322
323
324 typedef struct CalendarConflict
325 {
326         long is_update;
327         long existing_msgnum;
328         StrBuf *conflict_event_uid;
329         StrBuf *conflict_event_summary;
330 }CalendarConflict;
331 void DeleteConflict(void *vConflict)
332 {
333         CalendarConflict *c = (CalendarConflict *) vConflict;
334
335         FreeStrBuf(&c->conflict_event_uid);
336         FreeStrBuf(&c->conflict_event_summary);
337         free(c);
338 }
339 HashList *iterate_FindConflict(StrBuf *Target, WCTemplputParams *TP)
340 {
341         StrBuf *Line;
342         HashList *Conflicts = NULL;
343         CalendarConflict *Conflict;
344         wc_mime_attachment *Mime = (wc_mime_attachment *) CTX(CTX_MIME_ATACH);
345
346         serv_printf("ICAL conflicts|%ld|%s|", Mime->msgnum, ChrPtr(Mime->PartNum));
347
348         Line = NewStrBuf();
349         StrBuf_ServGetln(Line);
350         if (GetServerStatus(Line, NULL) == 1)
351         {
352                 const char *Pos = NULL;
353                 int Done = 0;
354                 int n = 0;
355                 Conflicts = NewHash(1, Flathash);
356                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
357                         if ( (StrLength(Line)==3) && 
358                              !strcmp(ChrPtr(Line), "000")) 
359                         {
360                                 Done = 1;
361                         }
362                         else {
363                                 Conflict = (CalendarConflict *) malloc(sizeof(CalendarConflict));
364                                 Conflict->conflict_event_uid = NewStrBufPlain(NULL, StrLength(Line));
365                                 Conflict->conflict_event_summary = NewStrBufPlain(NULL, StrLength(Line));
366
367                                 Conflict->existing_msgnum = StrBufExtractNext_long(Line, &Pos, '|');
368                                 StrBufSkip_NTokenS(Line, &Pos, '|', 1);
369                                 StrBufExtract_NextToken(Conflict->conflict_event_uid, Line, &Pos, '|');
370                                 StrBufExtract_NextToken(Conflict->conflict_event_summary, Line, &Pos, '|');
371                                 Conflict->is_update = StrBufExtractNext_long(Line, &Pos, '|');
372
373                                 Put(Conflicts, IKEY(n), Conflict, DeleteConflict);
374                                 n++;
375                                 Pos = NULL;
376                         }
377         }
378         FreeStrBuf(&Line);
379         syslog(LOG_DEBUG, "...done.\n");
380         return Conflicts;
381 }
382
383
384
385 void tmplput_ConflictEventMsgID(StrBuf *Target, WCTemplputParams *TP)
386 {
387         CalendarConflict *C = (CalendarConflict *) CTX(CTX_ICALCONFLICT);
388         char buf[sizeof(long) * 16];
389
390         snprintf(buf, sizeof(buf), "%ld", C->existing_msgnum);
391         StrBufAppendTemplateStr(Target, TP, buf, 0);
392 }
393 void tmplput_ConflictEUID(StrBuf *Target, WCTemplputParams *TP)
394 {
395         CalendarConflict *C = (CalendarConflict *) CTX(CTX_ICALCONFLICT);
396         
397         StrBufAppendTemplate(Target, TP, C->conflict_event_uid, 0);
398 }
399 void tmplput_ConflictSummary(StrBuf *Target, WCTemplputParams *TP)
400 {
401         CalendarConflict *C = (CalendarConflict *) CTX(CTX_ICALCONFLICT);
402
403         StrBufAppendTemplate(Target, TP, C->conflict_event_summary, 0);
404 }
405 int cond_ConflictIsUpdate(StrBuf *Target, WCTemplputParams *TP)
406 {
407         CalendarConflict *C = (CalendarConflict *) CTX(CTX_ICALCONFLICT);
408         return C->is_update;
409 }
410
411 typedef struct CalAttendee
412 {
413         StrBuf *AttendeeStr;
414         icalparameter_partstat partstat;
415 } CalAttendee;
416
417 void DeleteAtt(void *vAtt)
418 {
419         CalAttendee *att = (CalAttendee*) vAtt;
420         FreeStrBuf(&att->AttendeeStr);
421         free(vAtt);
422 }
423
424 HashList *iterate_get_ical_attendees(StrBuf *Target, WCTemplputParams *TP)
425 {
426         icalcomponent *cal = (icalcomponent *) CTX(CTX_ICAL);
427         icalparameter *partstat_param;
428         icalproperty *p;
429         CalAttendee *Att;
430         HashList *Attendees = NULL;
431         const char *ch;
432         int n = 0;
433
434         /* If the component has attendees, iterate through them. */
435         for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); 
436              (p != NULL); 
437              p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) {
438                 ch = icalproperty_get_attendee(p);
439                 if ((ch != NULL) && !strncasecmp(ch, "MAILTO:", 7)) {
440                         Att = (CalAttendee*) malloc(sizeof(CalAttendee));
441
442                         /** screen name or email address */
443                         Att->AttendeeStr = NewStrBufPlain(ch + 7, -1);
444                         StrBufTrim(Att->AttendeeStr);
445
446                         /** participant status */
447                         partstat_param = icalproperty_get_first_parameter(
448                                 p,
449                                 ICAL_PARTSTAT_PARAMETER
450                                 );
451                         if (partstat_param == NULL) {
452                                 Att->partstat = ICAL_PARTSTAT_X;
453                         }
454                         else {
455                                 Att->partstat = icalparameter_get_partstat(partstat_param);
456                         }
457                         if (Attendees == NULL)
458                                 Attendees = NewHash(1, Flathash);
459                         Put(Attendees, IKEY(n), Att, DeleteAtt);
460                         n++;
461                 }
462         }
463         return Attendees;
464 }
465
466 void tmplput_ICalAttendee(StrBuf *Target, WCTemplputParams *TP)
467 {
468         CalAttendee *Att = (CalAttendee*) CTX(CTX_ICALATTENDEE);
469         StrBufAppendTemplate(Target, TP, Att->AttendeeStr, 0);
470 }
471 int cond_ICalAttendeeState(StrBuf *Target, WCTemplputParams *TP)
472 {
473         CalAttendee *Att = (CalAttendee*) CTX(CTX_ICALATTENDEE);
474         icalparameter_partstat which_partstat;
475
476         which_partstat = GetTemplateTokenNumber(Target, TP, 2, ICAL_PARTSTAT_X);
477         return Att->partstat == which_partstat;
478 }
479         /* If the component has subcomponents, recurse through them. * /
480         for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
481              (c != 0);
482              c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
483                 // Recursively process subcomponent
484                 cal_process_object(Target, c, recursion_level+1, msgnum, cal_partnum);
485         }
486         */
487
488
489 void 
490 InitModule_ICAL_SUBST
491 (void)
492 {
493         RegisterCTX(CTX_ICAL);
494 /*
495         RegisterMimeRenderer(HKEY("text/calendar"), render_MIME_ICS_TPL, 1, 501);
496         RegisterMimeRenderer(HKEY("application/ics"), render_MIME_ICS_TPL, 1, 500);
497 */
498
499         CreateIcalComponendKindLookup ();
500         RegisterConditional("COND:ICAL:PROPERTY", 1, cond_ICalHaveItem, CTX_ICAL);
501         RegisterConditional("COND:ICAL:IS:A", 1, cond_ICalIsA, CTX_ICAL);
502
503
504         RegisterIterator("ICAL:CONFLICT", 0, NULL, iterate_FindConflict, 
505                          NULL, DeleteHash, CTX_MIME_ATACH, CTX_ICALCONFLICT, IT_NOFLAG);
506         RegisterNamespace("ICAL:CONFLICT:MSGID", 0, 1, tmplput_ConflictEventMsgID, NULL, CTX_ICALCONFLICT);
507         RegisterNamespace("ICAL:CONFLICT:EUID", 0, 1, tmplput_ConflictEUID, NULL, CTX_ICALCONFLICT);
508         RegisterNamespace("ICAL:CONFLICT:SUMMARY", 0, 1, tmplput_ConflictSummary, NULL, CTX_ICALCONFLICT);
509         RegisterConditional("ICAL:CONFLICT:IS:UPDATE", 0, cond_ConflictIsUpdate, CTX_ICALCONFLICT);
510
511
512         RegisterCTX(CTX_ICALATTENDEE);
513         RegisterIterator("ICAL:ATTENDEES", 0, NULL, iterate_get_ical_attendees, 
514                          NULL, DeleteHash, CTX_ICALATTENDEE, CTX_ICAL, IT_NOFLAG);
515         RegisterNamespace("ICAL:ATTENDEE", 1, 2, tmplput_ICalAttendee, NULL, CTX_ICALATTENDEE);
516         RegisterConditional("COND:ICAL:ATTENDEE", 1, cond_ICalAttendeeState, CTX_ICALATTENDEE);
517
518         RegisterCTX(CTX_ICALPROPERTY);
519         RegisterNamespace("ICAL:ITEM", 1, 2, tmplput_ICalItem, NULL, CTX_ICAL);
520         RegisterNamespace("ICAL:PROPERTY:STR", 0, 1, tmplput_CtxICalProperty, NULL, CTX_ICALPROPERTY);
521         RegisterNamespace("ICAL:PROPERTY:DATE", 0, 1, tmplput_CtxICalPropertyDate, NULL, CTX_ICALPROPERTY);
522
523         RegisterCTX(CTX_ICALMETHOD);
524         RegisterConditional("COND:ICAL:METHOD", 1, cond_ICalIsMethod, CTX_ICALMETHOD);
525
526
527         RegisterCTX(CTX_ICALTIME);
528         RegisterConditional("COND:ICAL:DT:PROPERTY", 1, cond_ICalHaveTimeItem, CTX_ICAL);
529         RegisterConditional("COND:ICAL:DT:ISDATE", 0, cond_ICalTimeIsDate, CTX_ICALTIME);
530         RegisterNamespace("ICAL:DT:DATE", 0, 1, tmplput_ICalTime_Date, NULL, CTX_ICALTIME);
531         RegisterNamespace("ICAL:DT:DATETIME", 0, 1, tmplput_ICalTime_Time, NULL, CTX_ICALTIME);
532 }
533
534 void 
535 ServerShutdownModule_ICAL
536 (void)
537 {
538         DeleteHash(&IcalComponentMap);
539 }