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