Mailing list header changes (fuck you Google)
[citadel.git] / webcit / tasks.c
1 #include "webcit.h"
2 #include "calendar.h"
3 #include "webserver.h"
4
5 /*
6  * qsort filter to move completed tasks to bottom of task list
7  */
8 int task_completed_cmp(const void *vtask1, const void *vtask2) {
9         disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1);
10 /*      disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2); */
11
12         icalproperty_status t1 = icalcomponent_get_status((Task1)->cal);
13         /* icalproperty_status t2 = icalcomponent_get_status(((struct disp_cal *)task2)->cal); */
14         
15         if (t1 == ICAL_STATUS_COMPLETED) 
16                 return 1;
17         return 0;
18 }
19
20
21 /*
22  * Helper function for do_tasks_view().  Returns the due date/time of a vtodo.
23  */
24 time_t get_task_due_date(icalcomponent *vtodo, int *is_date) {
25         icalproperty *p;
26
27         if (vtodo == NULL) {
28                 return(0L);
29         }
30
31         /*
32          * If we're looking at a fully encapsulated VCALENDAR
33          * rather than a VTODO component, recurse into the data
34          * structure until we get a VTODO.
35          */
36         if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
37                 return get_task_due_date(
38                         icalcomponent_get_first_component(
39                                 vtodo, ICAL_VTODO_COMPONENT
40                                 ), is_date
41                         );
42         }
43
44         p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
45         if (p != NULL) {
46                 struct icaltimetype t = icalproperty_get_due(p);
47
48                 if (is_date)
49                         *is_date = t.is_date;
50                 return(icaltime_as_timet(t));
51         }
52         else {
53                 return(0L);
54         }
55 }
56
57 /*
58  * Compare the due dates of two tasks (this is for sorting)
59  */
60 int task_due_cmp(const void *vtask1, const void *vtask2) {
61         disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1);
62         disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2);
63
64         time_t t1;
65         time_t t2;
66
67         t1 =  get_task_due_date(Task1->cal, NULL);
68         t2 =  get_task_due_date(Task2->cal, NULL);
69         if (t1 < t2) return(-1);
70         if (t1 > t2) return(1);
71         return(0);
72 }
73
74 /*
75  * do the whole task view stuff
76  */
77 int tasks_RenderView_or_Tail(SharedMessageStatus *Stat, 
78                               void **ViewSpecific, 
79                               long oper)
80 {
81         long hklen;
82         const char *HashKey;
83         void *vCal;
84         disp_cal *Cal;
85         HashPos *Pos;
86         int nItems;
87         time_t due;
88         char buf[SIZ];
89         icalproperty *p;
90         wcsession *WCC = WC;
91
92         wc_printf("<table class=\"calendar_view_background\"><tbody id=\"taskview\">\n<tr>\n<th>");
93         wc_printf(_("Completed?"));
94         wc_printf("</th><th>");
95         wc_printf(_("Name of task"));
96         wc_printf("</th><th>");
97         wc_printf(_("Date due"));
98         wc_printf("</th><th>");
99         wc_printf(_("Category"));
100         wc_printf(" (<select id=\"selectcategory\"><option value=\"showall\">%s</option></select>)</th></tr>\n",
101                 _("Show All"));
102
103         nItems = GetCount(WC->disp_cal_items);
104
105         /* Sort them if necessary
106         if (nItems > 1) {
107                 SortByPayload(WC->disp_cal_items, task_due_cmp);
108         }
109         * this shouldn't be neccessary, since we sort by the start time.
110         */
111
112         /* And then again, by completed */
113         if (nItems > 1) {
114                 SortByPayload(WC->disp_cal_items, 
115                               task_completed_cmp);
116         }
117
118         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
119         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
120                 icalproperty_status todoStatus;
121                 int is_date;
122
123                 Cal = (disp_cal*)vCal;
124                 wc_printf("<tr><td>");
125                 todoStatus = icalcomponent_get_status(Cal->cal);
126                 wc_printf("<input type=\"checkbox\" name=\"completed\" value=\"completed\" ");
127                 if (todoStatus == ICAL_STATUS_COMPLETED) {
128                         wc_printf("checked=\"checked\" ");
129                 }
130                 wc_printf("disabled=\"disabled\">\n</td><td>");
131                 p = icalcomponent_get_first_property(Cal->cal,
132                         ICAL_SUMMARY_PROPERTY);
133                 wc_printf("<a href=\"display_edit_task?msgnum=%ld?taskrm=", Cal->cal_msgnum);
134                 urlescputs(ChrPtr(WC->CurRoom.name));
135                 wc_printf("\">");
136                 /* wc_printf("<img align=middle "
137                 "src=\"static/taskmanag_16x.gif\" border=0>&nbsp;"); */
138                 if (p != NULL) {
139                         escputs((char *)icalproperty_get_comment(p));
140                 }
141                 wc_printf("</a>\n");
142                 wc_printf("</td>\n");
143
144                 due = get_task_due_date(Cal->cal, &is_date);
145                 wc_printf("<td><span");
146                 if (due > 0) {
147                         webcit_fmt_date(buf, SIZ, due, is_date ? DATEFMT_RAWDATE : DATEFMT_FULL);
148                         wc_printf(">%s",buf);
149                 }
150                 else {
151                         wc_printf(">");
152                 }
153                 wc_printf("</span></td>");
154                 wc_printf("<td>");
155                 p = icalcomponent_get_first_property(Cal->cal,
156                         ICAL_CATEGORIES_PROPERTY);
157                 if (p != NULL) {
158                         escputs((char *)icalproperty_get_categories(p));
159                 }
160                 wc_printf("</td>");
161                 wc_printf("</tr>");
162         }
163
164         wc_printf("</tbody></table>\n");
165
166         /* Free the list */
167         DeleteHash(&WC->disp_cal_items);
168         DeleteHashPos(&Pos);
169         return 0;
170 }
171
172
173 /*
174  * Display a task by itself (for editing)
175  */
176 void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum, char *from,
177                         int unread, calview *calv)
178 {
179         wcsession *WCC = WC;
180         icalcomponent *vtodo;
181         icalproperty *p;
182         struct icaltimetype IcalTime;
183         int created_new_vtodo = 0;
184         icalproperty_status todoStatus;
185
186         if (supplied_vtodo != NULL) {
187                 vtodo = supplied_vtodo;
188
189                 /*
190                  * It's safe to convert to UTC here because there are no recurrences to worry about.
191                  */
192                 ical_dezonify(vtodo);
193
194                 /*
195                  * If we're looking at a fully encapsulated VCALENDAR
196                  * rather than a VTODO component, attempt to use the first
197                  * relevant VTODO subcomponent.  If there is none, the
198                  * NULL returned by icalcomponent_get_first_component() will
199                  * tell the next iteration of this function to create a
200                  * new one.
201                  */
202                 if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
203                         display_edit_individual_task(
204                                 icalcomponent_get_first_component(
205                                         vtodo, ICAL_VTODO_COMPONENT
206                                         ), 
207                                 msgnum, from, unread, calv
208                                 );
209                         return;
210                 }
211         }
212         else {
213                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
214                 created_new_vtodo = 1;
215         }
216         
217         /* TODO: Can we take all this and move it into a template?       */
218         output_headers(1, 1, 1, 0, 0, 0);
219         wc_printf("<!-- start task edit form -->");
220         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
221         /* Get summary early for title */
222         wc_printf("<div class=\"box\">\n");
223         wc_printf("<div class=\"boxlabel\">");
224         wc_printf(_("Edit task"));
225         wc_printf("- ");
226         if (p != NULL) {
227                 escputs((char *)icalproperty_get_comment(p));
228         }
229         wc_printf("</div>");
230         
231         wc_printf("<div class=\"boxcontent\">\n");
232         wc_printf("<FORM METHOD=\"POST\" action=\"save_task\">\n");
233         wc_printf("<div style=\"display: none;\">\n     ");
234
235         wc_printf("<input type=\"hidden\" name=\"go\" value=\"");
236         StrEscAppend(WCC->WBuf, WCC->CurRoom.name, NULL, 0, 0);
237         wc_printf("\">\n");
238
239         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
240         wc_printf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n", msgnum);
241         wc_printf("<INPUT TYPE=\"hidden\" NAME=\"return_to_summary\" VALUE=\"%d\">\n",
242                 ibstr("return_to_summary"));
243         wc_printf("</div>");
244         wc_printf("<table class=\"calendar_background\"><tr><td>");
245         wc_printf("<TABLE STYLE=\"border: none;\">\n");
246
247         wc_printf("<TR><TD>");
248         wc_printf(_("Summary:"));
249         wc_printf("</TD><TD>"
250                 "<INPUT TYPE=\"text\" NAME=\"summary\" "
251                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
252         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
253         if (p != NULL) {
254                 escputs((char *)icalproperty_get_comment(p));
255         }
256         wc_printf("\"></TD></TR>\n");
257
258         wc_printf("<TR><TD>");
259         wc_printf(_("Start date:"));
260         wc_printf("</TD><TD>");
261         p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY);
262         wc_printf("<INPUT TYPE=\"CHECKBOX\" NAME=\"nodtstart\" ID=\"nodtstart\" VALUE=\"NODTSTART\" ");
263         if (p == NULL) {
264                 wc_printf("CHECKED=\"CHECKED\"");
265         }
266         wc_printf(">");
267         wc_printf(_("No date"));
268         
269         wc_printf(" ");
270         wc_printf("<span ID=\"dtstart_date\">");
271         wc_printf(_("or"));
272         wc_printf(" ");
273         if (p != NULL) {
274                 IcalTime = icalproperty_get_dtstart(p);
275         }
276         else
277                 IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone());
278         display_icaltimetype_as_webform(&IcalTime, "dtstart", 0);
279
280         wc_printf("<INPUT TYPE=\"CHECKBOX\" NAME=\"dtstart_time_assoc\" ID=\"dtstart_time_assoc\" VALUE=\"yes\"");
281         if (!IcalTime.is_date) {
282                 wc_printf("CHECKED=\"CHECKED\"");
283         }
284         wc_printf(">");
285         wc_printf(_("Time associated"));
286         wc_printf("</span></TD></TR>\n");
287
288         wc_printf("<TR><TD>");
289         wc_printf(_("Due date:"));
290         wc_printf("</TD><TD>");
291         p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
292         wc_printf("<INPUT TYPE=\"CHECKBOX\" NAME=\"nodue\" ID=\"nodue\" VALUE=\"NODUE\"");
293         if (p == NULL) {
294                 wc_printf("CHECKED=\"CHECKED\"");
295         }
296         wc_printf(">");
297         wc_printf(_("No date"));
298         wc_printf(" ");
299         wc_printf("<span ID=\"due_date\">\n");
300         wc_printf(_("or"));
301         wc_printf(" ");
302         if (p != NULL) {
303                 IcalTime = icalproperty_get_due(p);
304         }
305         else
306                 IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone());
307         display_icaltimetype_as_webform(&IcalTime, "due", 0);
308
309         wc_printf("<INPUT TYPE=\"CHECKBOX\" NAME=\"due_time_assoc\" ID=\"due_time_assoc\" VALUE=\"yes\"");
310         if (!IcalTime.is_date) {
311                 wc_printf("CHECKED=\"CHECKED\"");
312         }
313         wc_printf(">");
314         wc_printf(_("Time associated"));
315         wc_printf("</span></TD></TR>\n");
316         todoStatus = icalcomponent_get_status(vtodo);
317         wc_printf("<TR><TD>\n");
318         wc_printf(_("Completed:"));
319         wc_printf("</TD><TD>");
320         wc_printf("<INPUT TYPE=\"CHECKBOX\" NAME=\"status\" VALUE=\"COMPLETED\"");
321         if (todoStatus == ICAL_STATUS_COMPLETED) {
322                 wc_printf(" CHECKED=\"CHECKED\"");
323         } 
324         wc_printf(" >");
325         wc_printf("</TD></TR>");
326         /* start category field */
327         p = icalcomponent_get_first_property(vtodo, ICAL_CATEGORIES_PROPERTY);
328         wc_printf("<TR><TD>");
329         wc_printf(_("Category:"));
330         wc_printf("</TD><TD>");
331         wc_printf("<INPUT TYPE=\"text\" NAME=\"category\" MAXLENGTH=\"32\" SIZE=\"32\" VALUE=\"");
332         if (p != NULL) {
333                 escputs((char *)icalproperty_get_categories(p));
334         }
335         wc_printf("\">");
336         wc_printf("</TD></TR>\n ");
337         /* end category field */
338         wc_printf("<TR><TD>");
339         wc_printf(_("Description:"));
340         wc_printf("</TD><TD>");
341         wc_printf("<TEXTAREA NAME=\"description\" "
342                 "ROWS=\"10\" COLS=\"80\">\n"
343                 );
344         p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
345         if (p != NULL) {
346                 escputs((char *)icalproperty_get_comment(p));
347         }
348         wc_printf("</TEXTAREA></TD></TR></TABLE>\n");
349
350         wc_printf("<SPAN STYLE=\"text-align: center;\">"
351                 "<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">"
352                 "&nbsp;&nbsp;"
353                 "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
354                 "&nbsp;&nbsp;"
355                 "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n"
356                 "</SPAN>\n",
357                 _("Save"),
358                 _("Delete"),
359                 _("Cancel")
360                 );
361         wc_printf("</td></tr></table>");
362         wc_printf("</FORM>\n");
363         wc_printf("</div></div></div>\n");
364         wc_printf("<!-- end task edit form -->");
365         wDumpContent(1);
366
367         if (created_new_vtodo) {
368                 icalcomponent_free(vtodo);
369         }
370 }
371
372 /*
373  * Save an edited task
374  *
375  * supplied_vtodo       the task to save
376  * msgnum               number of the mesage in our db
377  */
378 void save_individual_task(icalcomponent *supplied_vtodo, long msgnum, char* from, int unread,
379                           calview *calv)
380 {
381         char buf[SIZ];
382         int delete_existing = 0;
383         icalproperty *prop;
384         icalcomponent *vtodo, *encaps;
385         int created_new_vtodo = 0;
386         int i;
387         int sequence = 0;
388         struct icaltimetype t;
389
390         if (supplied_vtodo != NULL) {
391                 vtodo = supplied_vtodo;
392                 /**
393                  * If we're looking at a fully encapsulated VCALENDAR
394                  * rather than a VTODO component, attempt to use the first
395                  * relevant VTODO subcomponent.  If there is none, the
396                  * NULL returned by icalcomponent_get_first_component() will
397                  * tell the next iteration of this function to create a
398                  * new one.
399                  */
400                 if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
401                         save_individual_task(
402                                 icalcomponent_get_first_component(
403                                         vtodo, ICAL_VTODO_COMPONENT), 
404                                 msgnum, from, unread, calv
405                                 );
406                         return;
407                 }
408         }
409         else {
410                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
411                 created_new_vtodo = 1;
412         }
413
414         if (havebstr("save_button")) {
415
416                 /** Replace values in the component with ones from the form */
417
418                 while (prop = icalcomponent_get_first_property(vtodo,
419                                                                ICAL_SUMMARY_PROPERTY), prop != NULL) {
420                         icalcomponent_remove_property(vtodo, prop);
421                         icalproperty_free(prop);
422                 }
423                 if (havebstr("summary")) {
424
425                         icalcomponent_add_property(vtodo,
426                                                    icalproperty_new_summary(bstr("summary")));
427                 } else {
428                         icalcomponent_add_property(vtodo,
429                                                    icalproperty_new_summary(_("Untitled Task")));
430                 }
431         
432                 while (prop = icalcomponent_get_first_property(vtodo,
433                                                                ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
434                         icalcomponent_remove_property(vtodo, prop);
435                         icalproperty_free(prop);
436                 }
437                 if (havebstr("description")) {
438                         icalcomponent_add_property(vtodo,
439                                                    icalproperty_new_description(bstr("description")));
440                 }
441         
442                 while (prop = icalcomponent_get_first_property(vtodo,
443                                                                ICAL_DTSTART_PROPERTY), prop != NULL) {
444                         icalcomponent_remove_property(vtodo, prop);
445                         icalproperty_free(prop);
446                 }
447                 if (IsEmptyStr(bstr("nodtstart"))) {
448                         if (yesbstr("dtstart_time")) {
449                                 icaltime_from_webform(&t, "dtstart");
450                         }
451                         else {
452                                 icaltime_from_webform_dateonly(&t, "dtstart");
453                         }
454                         icalcomponent_add_property(vtodo,
455                                                    icalproperty_new_dtstart(t)
456                                 );
457                 }
458                 while(prop = icalcomponent_get_first_property(vtodo,
459                                                               ICAL_STATUS_PROPERTY), prop != NULL) {
460                         icalcomponent_remove_property(vtodo,prop);
461                         icalproperty_free(prop);
462                 }
463                 while(prop = icalcomponent_get_first_property(vtodo,
464                                                               ICAL_PERCENTCOMPLETE_PROPERTY), prop != NULL) {
465                         icalcomponent_remove_property(vtodo,prop);
466                         icalproperty_free(prop);
467                 }
468
469                 if (havebstr("status")) {
470                         icalproperty_status taskStatus = icalproperty_string_to_status(bstr("status"));
471                         icalcomponent_set_status(vtodo, taskStatus);
472                         icalcomponent_add_property(vtodo,
473                                 icalproperty_new_percentcomplete(
474                                         (strcasecmp(bstr("status"), "completed") ? 0 : 100)
475                                 )
476                         );
477                 }
478                 else {
479                         icalcomponent_add_property(vtodo, icalproperty_new_percentcomplete(0));
480                 }
481                 while (prop = icalcomponent_get_first_property(vtodo,
482                                                                ICAL_CATEGORIES_PROPERTY), prop != NULL) {
483                         icalcomponent_remove_property(vtodo,prop);
484                         icalproperty_free(prop);
485                 }
486                 if (!IsEmptyStr(bstr("category"))) {
487                         prop = icalproperty_new_categories(bstr("category"));
488                         icalcomponent_add_property(vtodo,prop);
489                 }
490                 while (prop = icalcomponent_get_first_property(vtodo,
491                                                                ICAL_DUE_PROPERTY), prop != NULL) {
492                         icalcomponent_remove_property(vtodo, prop);
493                         icalproperty_free(prop);
494                 }
495                 if (IsEmptyStr(bstr("nodue"))) {
496                         if (yesbstr("due_time")) {
497                                 icaltime_from_webform(&t, "due");
498                         }
499                         else {
500                                 icaltime_from_webform_dateonly(&t, "due");
501                         }
502                         icalcomponent_add_property(vtodo,
503                                                    icalproperty_new_due(t)
504                                 );
505                 }
506                 /** Give this task a UID if it doesn't have one. */
507                 syslog(LOG_DEBUG, "Give this task a UID if it doesn't have one.\n");
508                 if (icalcomponent_get_first_property(vtodo,
509                                                      ICAL_UID_PROPERTY) == NULL) {
510                         generate_uuid(buf);
511                         icalcomponent_add_property(vtodo,
512                                                    icalproperty_new_uid(buf)
513                                 );
514                 }
515
516                 /* Increment the sequence ID */
517                 syslog(LOG_DEBUG, "Increment the sequence ID\n");
518                 while (prop = icalcomponent_get_first_property(vtodo,
519                                                                ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
520                         i = icalproperty_get_sequence(prop);
521                         syslog(LOG_DEBUG, "Sequence was %d\n", i);
522                         if (i > sequence) sequence = i;
523                         icalcomponent_remove_property(vtodo, prop);
524                         icalproperty_free(prop);
525                 }
526                 ++sequence;
527                 syslog(LOG_DEBUG, "New sequence is %d.  Adding...\n", sequence);
528                 icalcomponent_add_property(vtodo,
529                                            icalproperty_new_sequence(sequence)
530                         );
531
532                 /*
533                  * Encapsulate event into full VCALENDAR component.  Clone it first,
534                  * for two reasons: one, it's easier to just free the whole thing
535                  * when we're done instead of unbundling, but more importantly, we
536                  * can't encapsulate something that may already be encapsulated
537                  * somewhere else.
538                  */
539                 syslog(LOG_DEBUG, "Encapsulating into a full VCALENDAR component\n");
540                 encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo));
541
542                 /* Serialize it and save it to the message base */
543                 serv_puts("ENT0 1|||4");
544                 serv_getln(buf, sizeof buf);
545                 if (buf[0] == '4') {
546                         serv_puts("Content-type: text/calendar");
547                         serv_puts("");
548                         serv_puts(icalcomponent_as_ical_string(encaps));
549                         serv_puts("000");
550
551                         /*
552                          * Probably not necessary; the server will see the UID
553                          * of the object and delete the old one anyway, but
554                          * just in case...
555                          */
556                         delete_existing = 1;
557                 }
558                 icalcomponent_free(encaps);
559         }
560
561         /**
562          * If the user clicked 'Delete' then explicitly delete the message.
563          */
564         if (havebstr("delete_button")) {
565                 delete_existing = 1;
566         }
567
568         if ( (delete_existing) && (msgnum > 0L) ) {
569                 serv_printf("DELE %ld", lbstr("msgnum"));
570                 serv_getln(buf, sizeof buf);
571         }
572
573         if (created_new_vtodo) {
574                 icalcomponent_free(vtodo);
575         }
576
577         /* Go back to wherever we came from */
578         if (ibstr("return_to_summary") == 1) {
579                 display_summary_page();
580         }
581         else {
582                 readloop(readfwd, eUseDefault);
583         }
584 }
585
586
587 /*
588  * free memory allocated using libical
589  */
590 void delete_task(void *vCal)
591 {
592         disp_cal *Cal = (disp_cal*) vCal;
593         icalcomponent_free(Cal->cal);
594         free(Cal->from);
595         free(Cal);
596 }
597
598
599 /*
600  * Load a Task into a hash table for later display.
601  */
602 void load_task(icalcomponent *event, long msgnum, char *from, int unread, calview *calv)
603 {
604         icalproperty *ps = NULL;
605         struct icaltimetype dtstart, dtend;
606         wcsession *WCC = WC;
607         disp_cal *Cal;
608         size_t len;
609         icalcomponent *cptr = NULL;
610
611         dtstart = icaltime_null_time();
612         dtend = icaltime_null_time();
613         
614         if (WCC->disp_cal_items == NULL) {
615                 WCC->disp_cal_items = NewHash(0, Flathash);
616         }
617
618         Cal = (disp_cal*) malloc(sizeof(disp_cal));
619         memset(Cal, 0, sizeof(disp_cal));
620         Cal->cal = icalcomponent_new_clone(event);
621
622         /* Dezonify and decapsulate at the very last moment */
623         ical_dezonify(Cal->cal);
624         if (icalcomponent_isa(Cal->cal) != ICAL_VTODO_COMPONENT) {
625                 cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VTODO_COMPONENT);
626                 if (cptr) {
627                         cptr = icalcomponent_new_clone(cptr);
628                         icalcomponent_free(Cal->cal);
629                         Cal->cal = cptr;
630                 }
631         }
632
633         Cal->unread = unread;
634         len = strlen(from);
635         Cal->from = (char*)malloc(len+ 1);
636         memcpy(Cal->from, from, len + 1);
637         Cal->cal_msgnum = msgnum;
638
639         /* Precalculate the starting date and time of this event, and store it in our top-level
640          * structure.  Later, when we are rendering the calendar, we can just peek at these values
641          * without having to break apart every calendar item.
642          */
643         ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
644         if (ps != NULL) {
645                 dtstart = icalproperty_get_dtstart(ps);
646                 Cal->event_start = icaltime_as_timet(dtstart);
647         }
648
649         /* Do the same for the ending date and time.  It makes the day view much easier to render. */
650         ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
651         if (ps != NULL) {
652                 dtend = icalproperty_get_dtend(ps);
653                 Cal->event_end = icaltime_as_timet(dtend);
654         }
655
656         /* Store it in the hash list. */
657         /* syslog(LOG_DEBUG, "INITIAL: %s", ctime(&Cal->event_start)); */
658         Put(WCC->disp_cal_items, 
659             (char*) &Cal->event_start,
660             sizeof(Cal->event_start), 
661             Cal, 
662             delete_task
663         );
664 }
665
666
667
668 /*
669  * Display task view
670  */
671 int tasks_LoadMsgFromServer(SharedMessageStatus *Stat, 
672                             void **ViewSpecific, 
673                             message_summary* Msg, 
674                             int is_new, 
675                             int i)
676 {
677         /* Not (yet?) needed here? calview *c = (calview *) *ViewSpecific; */
678
679         load_ical_object(Msg->msgnum, is_new, ICAL_VTODO_COMPONENT, load_task, NULL, 0);
680         return 0;
681 }
682
683 /*
684  * Display the editor component for a task
685  */
686 void display_edit_task(void) {
687         long msgnum = 0L;
688                         
689         /* Force change the room if we have to */
690         if (havebstr("taskrm")) {
691                 gotoroom(sbstr("taskrm"));
692         }
693
694         msgnum = lbstr("msgnum");
695         if (msgnum > 0L) {
696                 /* existing task */
697                 load_ical_object(msgnum, 0,
698                                  ICAL_VTODO_COMPONENT,
699                                  display_edit_individual_task,
700                                  NULL, 0
701                 );
702         }
703         else {
704                 /* new task */
705                 display_edit_individual_task(NULL, 0L, "", 0, NULL);
706         }
707 }
708
709 /*
710  * save an edited task
711  */
712 void save_task(void) {
713         long msgnum = 0L;
714         msgnum = lbstr("msgnum");
715         if (msgnum > 0L) {
716                 load_ical_object(msgnum, 0, ICAL_VTODO_COMPONENT, save_individual_task, NULL, 0);
717         }
718         else {
719                 save_individual_task(NULL, 0L, "", 0, NULL);
720         }
721 }
722
723
724
725 int tasks_GetParamsGetServerCall(SharedMessageStatus *Stat, 
726                                  void **ViewSpecific, 
727                                  long oper, 
728                                  char *cmd, 
729                                  long len,
730                                  char *filter,
731                                  long flen)
732 {
733         strcpy(cmd, "MSGS ALL");
734         Stat->maxmsgs = 32767;
735         return 200;
736 }
737
738
739 int tasks_Cleanup(void **ViewSpecific)
740 {
741         wDumpContent(1);
742 /* Tasks doesn't need the calview struct... 
743         free (*ViewSpecific);
744         *ViewSpecific = NULL;
745         */
746         return 0;
747 }
748
749 void 
750 InitModule_TASKS
751 (void)
752 {
753         RegisterReadLoopHandlerset(
754                 VIEW_TASKS,
755                 tasks_GetParamsGetServerCall,
756                 NULL,
757                 NULL,
758                 NULL,
759                 tasks_LoadMsgFromServer,
760                 tasks_RenderView_or_Tail,
761                 tasks_Cleanup,
762                 NULL);
763         WebcitAddUrlHandler(HKEY("save_task"), "", 0, save_task, 0);
764 }