bb1166d8babbed331965383e3fbc42dfe8a63526
[citadel.git] / webcit / calendar.c
1 /*
2  * $Id$
3  *
4  * Functions which handle calendar objects and their processing/display.
5  */
6
7 #include "webcit.h"
8 #include "webserver.h"
9
10
11 /*
12  * \brief Process a calendar object
13  * ...at this point it's already been deserialized by cal_process_attachment()
14  * \param cal the calendar object
15  * \param recursion_level call stack depth ??????
16  * \param msgnum number of the mesage in our db
17  * \param cal_partnum of the calendar object ???? 
18  */
19 void cal_process_object(icalcomponent *cal,
20                         int recursion_level,
21                         long msgnum,
22                         char *cal_partnum
23 ) {
24         icalcomponent *c;
25         icalproperty *method = NULL;
26         icalproperty_method the_method = ICAL_METHOD_NONE;
27         icalproperty *p;
28         struct icaltimetype t;
29         time_t tt;
30         char buf[256];
31         char conflict_name[256];
32         char conflict_message[256];
33         int is_update = 0;
34         char divname[32];
35         static int divcount = 0;
36
37         sprintf(divname, "rsvp%04x", ++divcount);
38
39         /** Leading HTML for the display of this object */
40         if (recursion_level == 0) {
41                 wprintf("<div class=\"mimepart\">\n");
42         }
43
44         /** Look for a method */
45         method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
46
47         /** See what we need to do with this */
48         if (method != NULL) {
49                 the_method = icalproperty_get_method(method);
50                 char *title;
51
52                 wprintf("<div id=\"%s_title\">", divname);
53                 wprintf("<img src=\"static/calarea_48x.gif\">");
54                 wprintf("<span>");
55                 switch(the_method) {
56                     case ICAL_METHOD_REQUEST:
57                         title = _("Meeting invitation");
58                         break;
59                     case ICAL_METHOD_REPLY:
60                         title = _("Attendee's reply to your invitation");
61                         break;
62                     case ICAL_METHOD_PUBLISH:
63                         title = _("Published event");
64                         break;
65                     default:
66                         title = _("This is an unknown type of calendar item.");
67                         break;
68                 }
69                 wprintf("</span>");
70
71                 wprintf("&nbsp;&nbsp;%s",title);
72                 wprintf("</div>");
73         }
74
75         wprintf("<dl>");
76         p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
77         if (p != NULL) {
78                 wprintf("<dt>");
79                 wprintf(_("Summary:"));
80                 wprintf("</dt><dd>");
81                 escputs((char *)icalproperty_get_comment(p));
82                 wprintf("</dd>\n");
83         }
84
85         p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
86         if (p != NULL) {
87                 wprintf("<dt>");
88                 wprintf(_("Location:"));
89                 wprintf("</dt><dd>");
90                 escputs((char *)icalproperty_get_comment(p));
91                 wprintf("</dd>\n");
92         }
93
94         /**
95          * Only show start/end times if we're actually looking at the VEVENT
96          * component.  Otherwise it shows bogus dates for things like timezone.
97          */
98         if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
99
100                 p = icalcomponent_get_first_property(cal,
101                                                 ICAL_DTSTART_PROPERTY);
102                 if (p != NULL) {
103                         t = icalproperty_get_dtstart(p);
104
105                         if (t.is_date) {
106                                 struct tm d_tm;
107                                 char d_str[32];
108                                 memset(&d_tm, 0, sizeof d_tm);
109                                 d_tm.tm_year = t.year - 1900;
110                                 d_tm.tm_mon = t.month - 1;
111                                 d_tm.tm_mday = t.day;
112                                 wc_strftime(d_str, sizeof d_str, "%x", &d_tm);
113                                 wprintf("<dt>");
114                                 wprintf(_("Date:"));
115                                 wprintf("</dt><dd>%s</dd>", d_str);
116                         }
117                         else {
118                                 tt = icaltime_as_timet(t);
119                                 webcit_fmt_date(buf, tt, 0);
120                                 wprintf("<dt>");
121                                 wprintf(_("Starting date/time:"));
122                                 wprintf("</dt><dd>%s</dd>", buf);
123                         }
124                 }
125         
126                 p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
127                 if (p != NULL) {
128                         t = icalproperty_get_dtend(p);
129                         tt = icaltime_as_timet(t);
130                         webcit_fmt_date(buf, tt, 0);
131                         wprintf("<dt>");
132                         wprintf(_("Ending date/time:"));
133                         wprintf("</dt><dd>%s</dd>", buf);
134                 }
135
136         }
137
138         p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
139         if (p != NULL) {
140                 wprintf("<dt>");
141                 wprintf(_("Description:"));
142                 wprintf("</dt><dd>");
143                 escputs((char *)icalproperty_get_comment(p));
144                 wprintf("</dd>\n");
145         }
146
147         /** If the component has attendees, iterate through them. */
148         for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); (p != NULL); p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) {
149                 wprintf("<dt>");
150                 wprintf(_("Attendee:"));
151                 wprintf("</dt><dd>");
152                 safestrncpy(buf, icalproperty_get_attendee(p), sizeof buf);
153                 if (!strncasecmp(buf, "MAILTO:", 7)) {
154
155                         /** screen name or email address */
156                         strcpy(buf, &buf[7]);
157                         striplt(buf);
158                         escputs(buf);
159                         wprintf(" ");
160
161                         /** participant status */
162                         partstat_as_string(buf, p);
163                         escputs(buf);
164                 }
165                 wprintf("</dd>\n");
166         }
167
168         /** If the component has subcomponents, recurse through them. */
169         for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
170             (c != 0);
171             c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
172                 /* Recursively process subcomponent */
173                 cal_process_object(c, recursion_level+1, msgnum, cal_partnum);
174         }
175
176         /** If this is a REQUEST, display conflicts and buttons */
177         if (the_method == ICAL_METHOD_REQUEST) {
178
179                 /* Check for conflicts */
180                 lprintf(9, "Checking server calendar for conflicts...\n");
181                 serv_printf("ICAL conflicts|%ld|%s|", msgnum, cal_partnum);
182                 serv_getln(buf, sizeof buf);
183                 if (buf[0] == '1') {
184                         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
185                                 extract_token(conflict_name, buf, 3, '|', sizeof conflict_name);
186                                 is_update = extract_int(buf, 4);
187
188                                 if (is_update) {
189                                         snprintf(conflict_message, sizeof conflict_message,
190                                                 _("This is an update of '%s' which is already in your calendar."), conflict_name);
191                                 }
192                                 else {
193                                         snprintf(conflict_message, sizeof conflict_message,
194                                                 _("This event would conflict with '%s' which is already in your calendar."), conflict_name);
195                                 }
196
197                                 wprintf("<dt>%s",
198                                         (is_update ?
199                                                 _("Update:") :
200                                                 _("CONFLICT:")
201                                         )
202                                 );
203                                 wprintf("</dt><dd>");
204                                 escputs(conflict_message);
205                                 wprintf("</dd>\n");
206                         }
207                 }
208                 lprintf(9, "...done.\n");
209
210                 wprintf("</dl>");
211
212                 /** Display the Accept/Decline buttons */
213                 wprintf("<p id=\"%s_question\">"
214                         "%s "
215                         "&nbsp;&nbsp;&nbsp;<span class=\"button_link\"> "
216                         "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Accept');\">%s</a>"
217                         "</span>&nbsp;&nbsp;&nbsp;<span class=\"button_link\">"
218                         "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Tentative');\">%s</a>"
219                         "</span>&nbsp;&nbsp;&nbsp;<span class=\"button_link\">"
220                         "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Decline');\">%s</a>"
221                         "</span></p>\n",
222                         divname,
223                         _("How would you like to respond to this invitation?"),
224                         divname, divname, msgnum, cal_partnum, _("Accept"),
225                         divname, divname, msgnum, cal_partnum, _("Tentative"),
226                         divname, divname, msgnum, cal_partnum, _("Decline")
227                 );
228
229         }
230
231         /** If this is a REPLY, display update button */
232         if (the_method == ICAL_METHOD_REPLY) {
233
234                 /** \todo  In the future, if we want to validate this object before \
235                  * continuing, we can do it this way:
236                 serv_printf("ICAL whatever|%ld|%s|", msgnum, cal_partnum);
237                 serv_getln(buf, sizeof buf);
238                 }
239                  ***********/
240
241                 /** Display the update buttons */
242                 wprintf("<p id=\"%s_question\" >"
243                         "%s "
244                         "&nbsp;&nbsp;&nbsp;<span class=\"button_link\"> "
245                         "<a href=\"javascript:HandleRSVP('%s_question','%s_title','%ld','%s','Update');\">%s</a>"
246                         "</span>&nbsp;&nbsp;&nbsp;<span class=\"button_link\">"
247                         "<a href=\"javascript:HandleRSVP('%s_question','%s_title','%ld','%s','Ignore');\">%s</a>"
248                         "</span></p>\n",
249                         divname,
250                         _("Click <i>Update</i> to accept this reply and update your calendar."),
251                         divname, divname, msgnum, cal_partnum, _("Update"),
252                         divname, divname, msgnum, cal_partnum, _("Ignore")
253                 );
254
255         }
256
257         /** Trailing HTML for the display of this object */
258         if (recursion_level == 0) {
259                 wprintf("<p>&nbsp;</p></div>\n");
260         }
261 }
262
263
264 /**
265  * \brief process calendar mail atachment
266  * Deserialize a calendar object in a message so it can be processed.
267  * (This is the main entry point for these things)
268  * \param part_source the part of the message we want to parse
269  * \param msgnum number of the mesage in our db
270  * \param cal_partnum the number of the calendar item
271  */
272 void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
273         icalcomponent *cal;
274
275         cal = icalcomponent_new_from_string(part_source);
276
277         if (cal == NULL) {
278                 wprintf(_("There was an error parsing this calendar item."));
279                 wprintf("<br />\n");
280                 return;
281         }
282
283         ical_dezonify(cal);
284         cal_process_object(cal, 0, msgnum, cal_partnum);
285
286         /* Free the memory we obtained from libical's constructor */
287         icalcomponent_free(cal);
288 }
289
290
291
292
293 /**
294  * \brief accept/decline meeting
295  * Respond to a meeting request
296  */
297 void respond_to_request(void) {
298         char buf[1024];
299
300         begin_ajax_response();
301
302         serv_printf("ICAL respond|%s|%s|%s|",
303                 bstr("msgnum"),
304                 bstr("cal_partnum"),
305                 bstr("sc")
306         );
307         serv_getln(buf, sizeof buf);
308
309         if (buf[0] == '2') {
310                 wprintf("<img src=\"static/calarea_48x.gif\"><span>");
311                 if (!strcasecmp(bstr("sc"), "accept")) {
312                         wprintf(_("You have accepted this meeting invitation.  "
313                                 "It has been entered into your calendar.")
314                         );
315                 } else if (!strcasecmp(bstr("sc"), "tentative")) {
316                         wprintf(_("You have tentatively accepted this meeting invitation.  "
317                                 "It has been 'pencilled in' to your calendar.")
318                         );
319                 } else if (!strcasecmp(bstr("sc"), "decline")) {
320                         wprintf(_("You have declined this meeting invitation.  "
321                                 "It has <b>not</b> been entered into your calendar.")
322                         );
323                 }
324                 wprintf(" ");
325                 wprintf(_("A reply has been sent to the meeting organizer."));
326                 wprintf("</span>");
327         } else {
328                 wprintf("<img align=\"center\" src=\"static/error.gif\"><span>");
329                 wprintf("%s\n", &buf[4]);
330                 wprintf("</span>");
331         }
332
333         end_ajax_response();
334 }
335
336
337
338 /**
339  * \brief Handle an incoming RSVP
340  */
341 void handle_rsvp(void) {
342         char buf[1024];
343
344         begin_ajax_response();
345
346         serv_printf("ICAL handle_rsvp|%s|%s|%s|",
347                 bstr("msgnum"),
348                 bstr("cal_partnum"),
349                 bstr("sc")
350         );
351         serv_getln(buf, sizeof buf);
352
353         if (buf[0] == '2') {
354                 wprintf("<img src=\"static/calarea_48x.gif\"><span>");
355                 if (!strcasecmp(bstr("sc"), "update")) {
356                         wprintf(_("Your calendar has been updated to reflect this RSVP."));
357                 } else if (!strcasecmp(bstr("sc"), "ignore")) {
358                         wprintf(_("You have chosen to ignore this RSVP. "
359                                 "Your calendar has <b>not</b> been updated.")
360                         );
361                 }
362                 wprintf("</span>");
363         } else {
364                 wprintf("<img src=\"static/error.gif\"><span> %s\n", &buf[4]);
365                 wprintf("</span>");
366         }
367
368         end_ajax_response();
369
370 }
371
372
373
374 /*@}*/
375 /*-----------------------------------------------------------------------**/
376
377
378
379 /**
380  * \defgroup MsgDisplayHandlers Display handlers for message reading 
381  * \ingroup Calendaring
382  */
383
384 /*@{*/
385
386
387
388 /**
389  * \brief get items, keep them.
390  * If we're reading calendar items, just store them for now.  We have to
391  * sort and re-output them later when we draw the calendar.
392  * \param cal Our calendar to process
393  * \param msgnum number of the mesage in our db
394  */
395 void display_individual_cal(icalcomponent *cal, long msgnum, char *from, int unread)
396 {
397         struct wcsession *WCC = WC;     /* stack this for faster access (WC is a function) */
398
399         WCC->num_cal += 1;
400         WCC->disp_cal = realloc(WC->disp_cal, (sizeof(struct disp_cal) * WCC->num_cal) );
401         WCC->disp_cal[WCC->num_cal - 1].cal = icalcomponent_new_clone(cal);
402         WCC->disp_cal[WCC->num_cal - 1].unread = unread;
403         WCC->disp_cal[WCC->num_cal - 1].from = malloc (strlen(from) + 1);
404         strcpy (WCC->disp_cal[WCC->num_cal - 1].from, from);
405         ical_dezonify(WCC->disp_cal[WCC->num_cal - 1].cal);
406         WCC->disp_cal[WCC->num_cal - 1].cal_msgnum = msgnum;
407 }
408
409
410
411 /*
412  * \brief edit a task
413  * Display a task by itself (for editing)
414  * \param supplied_vtodo the todo item we want to edit
415  * \param msgnum number of the mesage in our db
416  */
417 void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum, char *from, int unread) {
418         icalcomponent *vtodo;
419         icalproperty *p;
420         struct icaltimetype t;
421         time_t now;
422         int created_new_vtodo = 0;
423
424         now = time(NULL);
425
426         if (supplied_vtodo != NULL) {
427                 vtodo = supplied_vtodo;
428
429                 /**
430                  * If we're looking at a fully encapsulated VCALENDAR
431                  * rather than a VTODO component, attempt to use the first
432                  * relevant VTODO subcomponent.  If there is none, the
433                  * NULL returned by icalcomponent_get_first_component() will
434                  * tell the next iteration of this function to create a
435                  * new one.
436                  */
437                 if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
438                         display_edit_individual_task(
439                                 icalcomponent_get_first_component(
440                                         vtodo, ICAL_VTODO_COMPONENT
441                                         ), 
442                                 msgnum,
443                                 from, unread
444                         );
445                         return;
446                 }
447         }
448         else {
449                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
450                 created_new_vtodo = 1;
451         }
452
453         output_headers(1, 1, 2, 0, 0, 0);
454         wprintf("<div id=\"banner\">\n");
455         wprintf("<img src=\"static/taskmanag_48x.gif\">");
456         wprintf("<h1>");
457         wprintf(_("Edit task"));
458         wprintf("</h1>");
459         wprintf("</div>\n");
460
461         wprintf("<div id=\"content\" class=\"service\">\n");
462
463         wprintf("<div class=\"fix_scrollbar_bug\">"
464                 "<table class=\"calendar_background\"><tr><td>");
465         
466         wprintf("<FORM METHOD=\"POST\" action=\"save_task\">\n");
467         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%ld\">\n", WC->nonce);
468         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
469                 msgnum);
470
471         wprintf("<TABLE border=0>\n");
472
473         wprintf("<TR><TD>");
474         wprintf(_("Summary:"));
475         wprintf("</TD><TD>"
476                 "<INPUT TYPE=\"text\" NAME=\"summary\" "
477                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
478         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
479         if (p != NULL) {
480                 escputs((char *)icalproperty_get_comment(p));
481         }
482         wprintf("\"></TD></TR>\n");
483
484         wprintf("<TR><TD>");
485         wprintf(_("Start date:"));
486         wprintf("</TD><TD>");
487         p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY);
488         if (p != NULL) {
489                 t = icalproperty_get_dtstart(p);
490         }
491         else {
492                 t = icaltime_from_timet(now, 0);
493         }
494         display_icaltimetype_as_webform(&t, "dtstart");
495         wprintf("</TD></TR>\n");
496
497         wprintf("<TR><TD>");
498         wprintf(_("Due date:"));
499         wprintf("</TD><TD>");
500         p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
501         if (p != NULL) {
502                 t = icalproperty_get_due(p);
503         }
504         else {
505                 t = icaltime_from_timet(now, 0);
506         }
507         display_icaltimetype_as_webform(&t, "due");
508         wprintf("</TD></TR>\n");
509         wprintf("<TR><TD>");
510         wprintf(_("Description:"));
511         wprintf("</TD><TD>");
512         wprintf("<TEXTAREA NAME=\"description\" wrap=soft "
513                 "ROWS=10 COLS=80 WIDTH=80>\n"
514         );
515         p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
516         if (p != NULL) {
517                 escputs((char *)icalproperty_get_comment(p));
518         }
519         wprintf("</TEXTAREA></TD></TR></TABLE>\n");
520
521         wprintf("<CENTER>"
522                 "<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">"
523                 "&nbsp;&nbsp;"
524                 "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
525                 "&nbsp;&nbsp;"
526                 "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n"
527                 "</CENTER>\n",
528                 _("Save"),
529                 _("Delete"),
530                 _("Cancel")
531         );
532
533         wprintf("</FORM>\n");
534
535         wprintf("</td></tr></table></div>\n");
536         wDumpContent(1);
537
538         if (created_new_vtodo) {
539                 icalcomponent_free(vtodo);
540         }
541 }
542
543 /*
544  * \brief Save an edited task
545  * \param supplied_vtodo the task to save
546  * \param msgnum number of the mesage in our db
547  */
548 void save_individual_task(icalcomponent *supplied_vtodo, long msgnum, char* from, int unread) {
549         char buf[SIZ];
550         int delete_existing = 0;
551         icalproperty *prop;
552         icalcomponent *vtodo, *encaps;
553         int created_new_vtodo = 0;
554         int i;
555         int sequence = 0;
556         struct icaltimetype t;
557
558         if (supplied_vtodo != NULL) {
559                 vtodo = supplied_vtodo;
560                 /**
561                  * If we're looking at a fully encapsulated VCALENDAR
562                  * rather than a VTODO component, attempt to use the first
563                  * relevant VTODO subcomponent.  If there is none, the
564                  * NULL returned by icalcomponent_get_first_component() will
565                  * tell the next iteration of this function to create a
566                  * new one.
567                  */
568                 if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
569                         save_individual_task(
570                                 icalcomponent_get_first_component(
571                                         vtodo, ICAL_VTODO_COMPONENT), 
572                                 msgnum, from, unread
573                         );
574                         return;
575                 }
576         }
577         else {
578                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
579                 created_new_vtodo = 1;
580         }
581
582         if (!IsEmptyStr(bstr("save_button"))) {
583
584                 /** Replace values in the component with ones from the form */
585
586                 while (prop = icalcomponent_get_first_property(vtodo,
587                       ICAL_SUMMARY_PROPERTY), prop != NULL) {
588                         icalcomponent_remove_property(vtodo, prop);
589                         icalproperty_free(prop);
590                 }
591                 if (!IsEmptyStr(bstr("summary"))) {
592         
593                         icalcomponent_add_property(vtodo,
594                                         icalproperty_new_summary(bstr("summary")));
595                 } else {
596                         icalcomponent_add_property(vtodo,
597                                         icalproperty_new_summary("Untitled Task"));
598                 }
599         
600                 while (prop = icalcomponent_get_first_property(vtodo,
601                       ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
602                         icalcomponent_remove_property(vtodo, prop);
603                         icalproperty_free(prop);
604                 }
605                 icalcomponent_add_property(vtodo,
606                         icalproperty_new_description(bstr("description")));
607         
608                 while (prop = icalcomponent_get_first_property(vtodo,
609                       ICAL_DTSTART_PROPERTY), prop != NULL) {
610                         icalcomponent_remove_property(vtodo, prop);
611                         icalproperty_free(prop);
612                 }
613                 icaltime_from_webform(&t, "dtstart");
614                 icalcomponent_add_property(vtodo,
615                         icalproperty_new_dtstart(t)
616                 );
617         
618                 while (prop = icalcomponent_get_first_property(vtodo,
619                       ICAL_DUE_PROPERTY), prop != NULL) {
620                         icalcomponent_remove_property(vtodo, prop);
621                         icalproperty_free(prop);
622                 }
623                 icaltime_from_webform(&t, "due");
624                 icalcomponent_add_property(vtodo,
625                         icalproperty_new_due(t)
626                 );
627
628                 /** Give this task a UID if it doesn't have one. */
629                 lprintf(9, "Give this task a UID if it doesn't have one.\n");
630                 if (icalcomponent_get_first_property(vtodo,
631                    ICAL_UID_PROPERTY) == NULL) {
632                         generate_uuid(buf);
633                         icalcomponent_add_property(vtodo,
634                                 icalproperty_new_uid(buf)
635                         );
636                 }
637
638                 /** Increment the sequence ID */
639                 lprintf(9, "Increment the sequence ID\n");
640                 while (prop = icalcomponent_get_first_property(vtodo,
641                       ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
642                         i = icalproperty_get_sequence(prop);
643                         lprintf(9, "Sequence was %d\n", i);
644                         if (i > sequence) sequence = i;
645                         icalcomponent_remove_property(vtodo, prop);
646                         icalproperty_free(prop);
647                 }
648                 ++sequence;
649                 lprintf(9, "New sequence is %d.  Adding...\n", sequence);
650                 icalcomponent_add_property(vtodo,
651                         icalproperty_new_sequence(sequence)
652                 );
653
654                 /**
655                  * Encapsulate event into full VCALENDAR component.  Clone it first,
656                  * for two reasons: one, it's easier to just free the whole thing
657                  * when we're done instead of unbundling, but more importantly, we
658                  * can't encapsulate something that may already be encapsulated
659                  * somewhere else.
660                  */
661                 lprintf(9, "Encapsulating into a full VCALENDAR component\n");
662                 encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo));
663
664                 /* Serialize it and save it to the message base */
665                 serv_puts("ENT0 1|||4");
666                 serv_getln(buf, sizeof buf);
667                 if (buf[0] == '4') {
668                         serv_puts("Content-type: text/calendar");
669                         serv_puts("");
670                         serv_puts(icalcomponent_as_ical_string(encaps));
671                         serv_puts("000");
672
673                         /**
674                          * Probably not necessary; the server will see the UID
675                          * of the object and delete the old one anyway, but
676                          * just in case...
677                          */
678                         delete_existing = 1;
679                 }
680                 icalcomponent_free(encaps);
681         }
682
683         /**
684          * If the user clicked 'Delete' then explicitly delete the message.
685          */
686         if (!IsEmptyStr(bstr("delete_button"))) {
687                 delete_existing = 1;
688         }
689
690         if ( (delete_existing) && (msgnum > 0L) ) {
691                 serv_printf("DELE %ld", atol(bstr("msgnum")));
692                 serv_getln(buf, sizeof buf);
693         }
694
695         if (created_new_vtodo) {
696                 icalcomponent_free(vtodo);
697         }
698
699         /** Go back to the task list */
700         readloop("readfwd");
701 }
702
703
704
705 /**
706  * \brief generic item handler
707  * Code common to all display handlers.  Given a message number and a MIME
708  * type, we load the message and hunt for that MIME type.  If found, we load
709  * the relevant part, deserialize it into a libical component, filter it for
710  * the requested object type, and feed it to the specified handler.
711  * \param mimetype mimetyp of our object
712  * \param which_kind sort of ical type
713  * \param msgnum number of the mesage in our db
714  * \param callback a funcion \todo
715  *
716  */
717 void display_using_handler(long msgnum, int unread,
718                            icalcomponent_kind which_kind,
719                            void (*callback)(icalcomponent *, long, char*, int)
720         ) {
721         char buf[1024];
722         char from[128] = "";
723         char mime_partnum[256];
724         char mime_filename[256];
725         char mime_content_type[256];
726         char mime_disposition[256];
727         int mime_length;
728         char relevant_partnum[256];
729         char *relevant_source = NULL;
730         icalcomponent *cal, *c;
731
732         relevant_partnum[0] = '\0';
733         sprintf(buf, "MSG4 %ld", msgnum);       /* we need the mime headers */
734         serv_puts(buf);
735         serv_getln(buf, sizeof buf);
736         if (buf[0] != '1') return;
737
738         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
739                 if (!strncasecmp(buf, "part=", 5)) {
740                         extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
741                         extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
742                         extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
743                         extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
744                         mime_length = extract_int(&buf[5], 5);
745
746                         if (  (!strcasecmp(mime_content_type, "text/calendar"))
747                            || (!strcasecmp(mime_content_type, "application/ics"))
748                            || (!strcasecmp(mime_content_type, "text/vtodo"))
749                         ) {
750                                 strcpy(relevant_partnum, mime_partnum);
751                         }
752                 }
753                 else if (!strncasecmp(buf, "from=", 4)) {
754                         extract_token(from, buf, 1, '=', sizeof(from));
755                 }
756         }
757
758         if (!IsEmptyStr(relevant_partnum)) {
759                 relevant_source = load_mimepart(msgnum, relevant_partnum);
760                 if (relevant_source != NULL) {
761
762                         cal = icalcomponent_new_from_string(relevant_source);
763                         if (cal != NULL) {
764
765                                 ical_dezonify(cal);
766
767                                 /** Simple components of desired type */
768                                 if (icalcomponent_isa(cal) == which_kind) {
769                                         callback(cal, msgnum, from, unread);
770                                 }
771
772                                 /** Subcomponents of desired type */
773                                 for (c = icalcomponent_get_first_component(cal,
774                                     which_kind);
775                                     (c != 0);
776                                     c = icalcomponent_get_next_component(cal,
777                                     which_kind)) {
778                                         callback(c, msgnum, from, unread);
779                                 }
780                                 icalcomponent_free(cal);
781                         }
782                         free(relevant_source);
783                 }
784         }
785         icalmemory_free_ring();
786 }
787
788 /**
789  * \brief display whole calendar
790  * \param msgnum number of the mesage in our db
791  */
792 void display_calendar(long msgnum, int unread) {
793         display_using_handler(msgnum, unread,
794                                 ICAL_VEVENT_COMPONENT,
795                                 display_individual_cal);
796 }
797
798 /**
799  * \brief display whole taksview
800  * \param msgnum number of the mesage in our db
801  */
802 void display_task(long msgnum, int unread) {
803         display_using_handler(msgnum, unread,
804                                 ICAL_VTODO_COMPONENT,
805                                 display_individual_cal);
806 }
807
808 /**
809  * \brief display the editor component for a task
810  */
811 void display_edit_task(void) {
812         long msgnum = 0L;
813
814         /** Force change the room if we have to */
815         if (!IsEmptyStr(bstr("taskrm"))) {
816                 gotoroom(bstr("taskrm"));
817         }
818
819         msgnum = atol(bstr("msgnum"));
820         if (msgnum > 0L) {
821                 /** existing task */
822                 display_using_handler(msgnum, 0,
823                                 ICAL_VTODO_COMPONENT,
824                                 display_edit_individual_task);
825         }
826         else {
827                 /** new task */
828                 display_edit_individual_task(NULL, 0L, "", 0);
829         }
830 }
831
832 /**
833  *\brief save an edited task
834  */
835 void save_task(void) {
836         long msgnum = 0L;
837
838         msgnum = atol(bstr("msgnum"));
839         if (msgnum > 0L) {
840                 display_using_handler(msgnum, 0,
841                                 ICAL_VTODO_COMPONENT,
842                                 save_individual_task);
843         }
844         else {
845                 save_individual_task(NULL, 0L, "", 0);
846         }
847 }
848
849 /**
850  * \brief display the editor component for an event
851  */
852 void display_edit_event(void) {
853         long msgnum = 0L;
854
855         msgnum = atol(bstr("msgnum"));
856         if (msgnum > 0L) {
857                 /* existing event */
858                 display_using_handler(msgnum, 0,
859                                 ICAL_VEVENT_COMPONENT,
860                                 display_edit_individual_event);
861         }
862         else {
863                 /* new event */
864                 display_edit_individual_event(NULL, 0L, "", 0);
865         }
866 }
867
868 /**
869  * \brief save an edited event
870  */
871 void save_event(void) {
872         long msgnum = 0L;
873
874         msgnum = atol(bstr("msgnum"));
875
876         if (msgnum > 0L) {
877                 display_using_handler(msgnum, 0,
878                                 ICAL_VEVENT_COMPONENT,
879                                 save_individual_event);
880         }
881         else {
882                 save_individual_event(NULL, 0L, "", 0);
883         }
884 }
885
886
887
888
889
890 /**
891  * \brief freebusy display (for client software)
892  * \param req dunno. ?????
893  */
894 void do_freebusy(char *req) {
895         char who[SIZ];
896         char buf[SIZ];
897         char *fb;
898         int len;
899
900         extract_token(who, req, 1, ' ', sizeof who);
901         if (!strncasecmp(who, "/freebusy/", 10)) {
902                 strcpy(who, &who[10]);
903         }
904         unescape_input(who);
905
906         len = strlen(who);
907         if ( (!strcasecmp(&who[len-4], ".vcf"))
908            || (!strcasecmp(&who[len-4], ".ifb"))
909            || (!strcasecmp(&who[len-4], ".vfb")) ) {
910                 who[len-4] = 0;
911         }
912
913         lprintf(9, "freebusy requested for <%s>\n", who);
914         serv_printf("ICAL freebusy|%s", who);
915         serv_getln(buf, sizeof buf);
916
917         if (buf[0] != '1') {
918                 wprintf("HTTP/1.1 404 %s\n", &buf[4]);
919                 output_headers(0, 0, 0, 0, 0, 0);
920                 wprintf("Content-Type: text/plain\r\n");
921                 wprintf("\r\n");
922                 wprintf("%s\n", &buf[4]);
923                 return;
924         }
925
926         fb = read_server_text();
927         http_transmit_thing(fb, strlen(fb), "text/calendar", 0);
928         free(fb);
929 }
930
931