Back to index

webcit  8.12-dfsg
calendar.c
Go to the documentation of this file.
00001 /*
00002  * Functions which handle calendar objects and their processing/display.
00003  *
00004  * Copyright (c) 1996-2012 by the citadel.org team
00005  *
00006  * This program is open source software.  You can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License, version 3.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "webcit.h"
00016 #include "webserver.h"
00017 #include "calendar.h"
00018 
00019 /*
00020  * Process a calendar object.  At this point it's already been deserialized by cal_process_attachment()
00021  *
00022  * cal:                     the calendar object
00023  * recursion_level:  Number of times we've recursed into this function
00024  * msgnum:           Message number on the Citadel server
00025  * cal_partnum:             MIME part number within that message containing the calendar object
00026  */
00027 void cal_process_object(StrBuf *Target,
00028                      icalcomponent *cal,
00029                      int recursion_level,
00030                      long msgnum,
00031                      const char *cal_partnum) 
00032 {
00033        icalcomponent *c;
00034        icalproperty *method = NULL;
00035        icalproperty_method the_method = ICAL_METHOD_NONE;
00036        icalproperty *p;
00037        struct icaltimetype t;
00038        time_t tt;
00039        char buf[256];
00040        char conflict_name[256];
00041        char conflict_message[256];
00042        int is_update = 0;
00043        char divname[32];
00044        static int divcount = 0;
00045        const char *ch;
00046 
00047        sprintf(divname, "rsvp%04x", ++divcount);
00048 
00049        /* Convert timezones to something easy to display.
00050         * It's safe to do this in memory because we're only changing it on the
00051         * display side -- when we tell the server to do something with the object,
00052         * the server will be working with its original copy in the database.
00053         */
00054        if ((cal) && (recursion_level == 0)) {
00055               ical_dezonify(cal);
00056        }
00057 
00058        /* Leading HTML for the display of this object */
00059        if (recursion_level == 0) {
00060               StrBufAppendPrintf(Target, "<div class=\"mimepart\">\n");
00061        }
00062 
00063        /* Look for a method */
00064        method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
00065 
00066        /* See what we need to do with this */
00067        if (method != NULL) {
00068               char *title;
00069               the_method = icalproperty_get_method(method);
00070 
00071               StrBufAppendPrintf(Target, "<div id=\"%s_title\">", divname);
00072               StrBufAppendPrintf(Target, "<img src=\"static/webcit_icons/essen/32x32/calendar.png\">");
00073               StrBufAppendPrintf(Target, "<span>");
00074               switch(the_method) {
00075               case ICAL_METHOD_REQUEST:
00076                      title = _("Meeting invitation");
00077                      break;
00078               case ICAL_METHOD_REPLY:
00079                      title = _("Attendee's reply to your invitation");
00080                      break;
00081               case ICAL_METHOD_PUBLISH:
00082                      title = _("Published event");
00083                      break;
00084               default:
00085                      title = _("This is an unknown type of calendar item.");
00086                      break;
00087               }
00088               StrBufAppendPrintf(Target, "</span>");
00089 
00090               StrBufAppendPrintf(Target, "&nbsp;&nbsp;%s",title);
00091               StrBufAppendPrintf(Target, "</div>");
00092        }
00093 
00094        StrBufAppendPrintf(Target, "<dl>");
00095        p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
00096        if (p != NULL) {
00097               StrBufAppendPrintf(Target, "<dt>");
00098               StrBufAppendPrintf(Target, _("Summary:"));
00099               StrBufAppendPrintf(Target, "</dt><dd>");
00100               StrEscAppend(Target, NULL, (char *)icalproperty_get_comment(p), 0, 0);
00101               StrBufAppendPrintf(Target, "</dd>\n");
00102        }
00103 
00104        p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
00105        if (p != NULL) {
00106               StrBufAppendPrintf(Target, "<dt>");
00107               StrBufAppendPrintf(Target, _("Location:"));
00108               StrBufAppendPrintf(Target, "</dt><dd>");
00109               StrEscAppend(Target, NULL, (char *)icalproperty_get_comment(p), 0, 0);
00110               StrBufAppendPrintf(Target, "</dd>\n");
00111        }
00112 
00113        /*
00114         * Only show start/end times if we're actually looking at the VEVENT
00115         * component.  Otherwise it shows bogus dates for things like timezone.
00116         */
00117        if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
00118 
00119               p = icalcomponent_get_first_property(cal, ICAL_DTSTART_PROPERTY);
00120               if (p != NULL) {
00121                      t = icalproperty_get_dtstart(p);
00122 
00123                      if (t.is_date) {
00124                             struct tm d_tm;
00125                             char d_str[32];
00126                             memset(&d_tm, 0, sizeof d_tm);
00127                             d_tm.tm_year = t.year - 1900;
00128                             d_tm.tm_mon = t.month - 1;
00129                             d_tm.tm_mday = t.day;
00130                             wc_strftime(d_str, sizeof d_str, "%x", &d_tm);
00131                             StrBufAppendPrintf(Target, "<dt>");
00132                             StrBufAppendPrintf(Target, _("Date:"));
00133                             StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", d_str);
00134                      }
00135                      else {
00136                             tt = icaltime_as_timet(t);
00137                             webcit_fmt_date(buf, 256, tt, DATEFMT_FULL);
00138                             StrBufAppendPrintf(Target, "<dt>");
00139                             StrBufAppendPrintf(Target, _("Starting date/time:"));
00140                             StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", buf);
00141                      }
00142               }
00143        
00144               p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
00145               if (p != NULL) {
00146                      t = icalproperty_get_dtend(p);
00147                      tt = icaltime_as_timet(t);
00148                      webcit_fmt_date(buf, 256, tt, DATEFMT_FULL);
00149                      StrBufAppendPrintf(Target, "<dt>");
00150                      StrBufAppendPrintf(Target, _("Ending date/time:"));
00151                      StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", buf);
00152               }
00153 
00154        }
00155 
00156        p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
00157        if (p != NULL) {
00158               StrBufAppendPrintf(Target, "<dt>");
00159               StrBufAppendPrintf(Target, _("Description:"));
00160               StrBufAppendPrintf(Target, "</dt><dd>");
00161               StrEscAppend(Target, NULL, (char *)icalproperty_get_comment(p), 0, 0);
00162               StrBufAppendPrintf(Target, "</dd>\n");
00163        }
00164 
00165        if (icalcomponent_get_first_property(cal, ICAL_RRULE_PROPERTY)) {
00166               /* Unusual string syntax used here in order to re-use existing translations */
00167               StrBufAppendPrintf(Target, "<dt>%s:</dt><dd>%s.</dd>\n",
00168                      _("Recurrence"),
00169                      _("This is a recurring event")
00170               );
00171        }
00172 
00173        /* If the component has attendees, iterate through them. */
00174        for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); 
00175             (p != NULL); 
00176             p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) {
00177               StrBufAppendPrintf(Target, "<dt>");
00178               StrBufAppendPrintf(Target, _("Attendee:"));
00179               StrBufAppendPrintf(Target, "</dt><dd>");
00180               ch = icalproperty_get_attendee(p);
00181               if ((ch != NULL) && !strncasecmp(buf, "MAILTO:", 7)) {
00182 
00184                      safestrncpy(buf, ch + 7, sizeof(buf));
00185                      striplt(buf);
00186                      StrEscAppend(Target, NULL, buf, 0, 0);
00187                      StrBufAppendPrintf(Target, " ");
00188 
00190                      partstat_as_string(buf, p);
00191                      StrEscAppend(Target, NULL, buf, 0, 0);
00192               }
00193               StrBufAppendPrintf(Target, "</dd>\n");
00194        }
00195 
00196        /* If the component has subcomponents, recurse through them. */
00197        for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
00198             (c != 0);
00199             c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
00200               /* Recursively process subcomponent */
00201               cal_process_object(Target, c, recursion_level+1, msgnum, cal_partnum);
00202        }
00203 
00204        /* If this is a REQUEST, display conflicts and buttons */
00205        if (the_method == ICAL_METHOD_REQUEST) {
00206 
00207               /* Check for conflicts */
00208               syslog(9, "Checking server calendar for conflicts...\n");
00209               serv_printf("ICAL conflicts|%ld|%s|", msgnum, cal_partnum);
00210               serv_getln(buf, sizeof buf);
00211               if (buf[0] == '1') {
00212                      while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00213                             extract_token(conflict_name, buf, 3, '|', sizeof conflict_name);
00214                             is_update = extract_int(buf, 4);
00215 
00216                             if (is_update) {
00217                                    snprintf(conflict_message, sizeof conflict_message,
00218                                            _("This is an update of '%s' which is already in your calendar."), conflict_name);
00219                             }
00220                             else {
00221                                    snprintf(conflict_message, sizeof conflict_message,
00222                                            _("This event would conflict with '%s' which is already in your calendar."), conflict_name);
00223                             }
00224 
00225                             StrBufAppendPrintf(Target, "<dt>%s",
00226                                    (is_update ?
00227                                     _("Update:") :
00228                                     _("CONFLICT:")
00229                                           )
00230                                    );
00231                             StrBufAppendPrintf(Target, "</dt><dd>");
00232                             StrEscAppend(Target, NULL, conflict_message, 0, 0);
00233                             StrBufAppendPrintf(Target, "</dd>\n");
00234                      }
00235               }
00236               syslog(9, "...done.\n");
00237 
00238               StrBufAppendPrintf(Target, "</dl>");
00239 
00240               /* Display the Accept/Decline buttons */
00241               StrBufAppendPrintf(Target, "<p id=\"%s_question\">"
00242                      "%s "
00243                      "&nbsp;&nbsp;&nbsp;<span class=\"button_link\"> "
00244                      "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Accept');\">%s</a>"
00245                      "</span>&nbsp;&nbsp;&nbsp;<span class=\"button_link\">"
00246                      "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Tentative');\">%s</a>"
00247                      "</span>&nbsp;&nbsp;&nbsp;<span class=\"button_link\">"
00248                      "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Decline');\">%s</a>"
00249                      "</span></p>\n",
00250                      divname,
00251                      _("How would you like to respond to this invitation?"),
00252                      divname, divname, msgnum, cal_partnum, _("Accept"),
00253                      divname, divname, msgnum, cal_partnum, _("Tentative"),
00254                      divname, divname, msgnum, cal_partnum, _("Decline")
00255                      );
00256 
00257        }
00258 
00259        /* If this is a REPLY, display update button */
00260        if (the_method == ICAL_METHOD_REPLY) {
00261 
00262               /* Display the update buttons */
00263               StrBufAppendPrintf(Target, "<p id=\"%s_question\" >"
00264                      "%s "
00265                      "&nbsp;&nbsp;&nbsp;<span class=\"button_link\"> "
00266                      "<a href=\"javascript:HandleRSVP('%s_question','%s_title','%ld','%s','Update');\">%s</a>"
00267                      "</span>&nbsp;&nbsp;&nbsp;<span class=\"button_link\">"
00268                      "<a href=\"javascript:HandleRSVP('%s_question','%s_title','%ld','%s','Ignore');\">%s</a>"
00269                      "</span></p>\n",
00270                      divname,
00271                      _("Click <i>Update</i> to accept this reply and update your calendar."),
00272                      divname, divname, msgnum, cal_partnum, _("Update"),
00273                      divname, divname, msgnum, cal_partnum, _("Ignore")
00274                      );
00275        
00276        }
00277        
00278        /* Trailing HTML for the display of this object */
00279        if (recursion_level == 0) {
00280               StrBufAppendPrintf(Target, "<p>&nbsp;</p></div>\n");
00281        }
00282 }
00283 
00284 
00285 /*
00286  * Deserialize a calendar object in a message so it can be displayed.
00287  */
00288 void cal_process_attachment(wc_mime_attachment *Mime) 
00289 {
00290        icalcomponent *cal;
00291 
00292        cal = icalcomponent_new_from_string(ChrPtr(Mime->Data));
00293        FlushStrBuf(Mime->Data);
00294        if (cal == NULL) {
00295               StrBufAppendPrintf(Mime->Data, _("There was an error parsing this calendar item."));
00296               StrBufAppendPrintf(Mime->Data, "<br>\n");
00297               return;
00298        }
00299 
00300        cal_process_object(Mime->Data, cal, 0, Mime->msgnum, ChrPtr(Mime->PartNum));
00301 
00302        /* Free the memory we obtained from libical's constructor */
00303        icalcomponent_free(cal);
00304 }
00305 
00306 
00307 
00308 
00309 /*
00310  * Respond to a meeting request - accept/decline meeting
00311  */
00312 void respond_to_request(void) 
00313 {
00314        char buf[1024];
00315 
00316        begin_ajax_response();
00317 
00318        serv_printf("ICAL respond|%s|%s|%s|",
00319               bstr("msgnum"),
00320               bstr("cal_partnum"),
00321               bstr("sc")
00322        );
00323        serv_getln(buf, sizeof buf);
00324 
00325        if (buf[0] == '2') {
00326               wc_printf("<img src=\"static/webcit_icons/essen/32x32/calendar.png\"><span>");
00327               if (!strcasecmp(bstr("sc"), "accept")) {
00328                      wc_printf(_("You have accepted this meeting invitation.  "
00329                             "It has been entered into your calendar.")
00330                      );
00331               } else if (!strcasecmp(bstr("sc"), "tentative")) {
00332                      wc_printf(_("You have tentatively accepted this meeting invitation.  "
00333                             "It has been 'pencilled in' to your calendar.")
00334                      );
00335               } else if (!strcasecmp(bstr("sc"), "decline")) {
00336                      wc_printf(_("You have declined this meeting invitation.  "
00337                               "It has <b>not</b> been entered into your calendar.")
00338                             );
00339               }
00340               wc_printf(" ");
00341               wc_printf(_("A reply has been sent to the meeting organizer."));
00342               wc_printf("</span>");
00343        } else {
00344               wc_printf("<img align=\"center\" src=\"static/webcit_icons/error.gif\"><span>");
00345               wc_printf("%s\n", &buf[4]);
00346               wc_printf("</span>");
00347        }
00348 
00349        end_ajax_response();
00350 }
00351 
00352 
00353 
00354 /*
00355  * Handle an incoming RSVP
00356  */
00357 void handle_rsvp(void) 
00358 {
00359        char buf[1024];
00360 
00361        begin_ajax_response();
00362 
00363        serv_printf("ICAL handle_rsvp|%s|%s|%s|",
00364               bstr("msgnum"),
00365               bstr("cal_partnum"),
00366               bstr("sc")
00367        );
00368        serv_getln(buf, sizeof buf);
00369 
00370        if (buf[0] == '2') {
00371               wc_printf("<img src=\"static/webcit_icons/calendar.png\"><span>");
00372               if (!strcasecmp(bstr("sc"), "update")) {
00376                      wc_printf(_("Your calendar has been updated to reflect this RSVP."));
00377               } else if (!strcasecmp(bstr("sc"), "ignore")) {
00378                      wc_printf(_("You have chosen to ignore this RSVP. "
00379                               "Your calendar has <b>not</b> been updated.")
00380                             );
00381               }
00382               wc_printf("</span>");
00383        } else {
00384               wc_printf("<img src=\"static/webcit_icons/error.gif\"><span> %s\n", &buf[4]);
00385               wc_printf("</span>");
00386        }
00387 
00388        end_ajax_response();
00389 }
00390 
00391 
00392 
00393 
00394 /*
00395  * free memory allocated using libical
00396  */
00397 void delete_cal(void *vCal)
00398 {
00399        disp_cal *Cal = (disp_cal*) vCal;
00400        icalcomponent_free(Cal->cal);
00401        free(Cal->from);
00402        free(Cal);
00403 }
00404 
00405 /*
00406  * This is the meat-and-bones of the first part of our two-phase calendar display.
00407  * As we encounter calendar items in messages being read from the server, we break out
00408  * any iCalendar objects and store them in a hash table.  Later on, the second phase will
00409  * use this hash table to render the calendar for display.
00410  */
00411 void display_individual_cal(icalcomponent *event, long msgnum, char *from, int unread, calview *calv)
00412 {
00413        icalproperty *ps = NULL;
00414        struct icaltimetype dtstart, dtend;
00415        struct icaldurationtype dur;
00416        wcsession *WCC = WC;
00417        disp_cal *Cal;
00418        size_t len;
00419        time_t final_recurrence = 0;
00420        icalcomponent *cptr = NULL;
00421 
00422        /* recur variables */
00423        icalproperty *rrule = NULL;
00424        struct icalrecurrencetype recur;
00425        icalrecur_iterator *ritr = NULL;
00426        struct icaltimetype next;
00427        int num_recur = 0;
00428        int stop_rr = 0;
00429 
00430        /* first and foremost, check for bogosity.  bail if we see no DTSTART property */
00431 
00432        if (icalcomponent_get_first_property(icalcomponent_get_first_component(
00433               event, ICAL_VEVENT_COMPONENT), ICAL_DTSTART_PROPERTY) == NULL)
00434        {
00435               return;
00436        }
00437 
00438        /* ok, chances are we've got a live one here.  let's try to figure out where it goes. */
00439 
00440        dtstart = icaltime_null_time();
00441        dtend = icaltime_null_time();
00442        
00443        if (WCC->disp_cal_items == NULL) {
00444               WCC->disp_cal_items = NewHash(0, Flathash);
00445        }
00446 
00447        /* Note: anything we do here, we also have to do below for the recurrences. */
00448        Cal = (disp_cal*) malloc(sizeof(disp_cal));
00449        memset(Cal, 0, sizeof(disp_cal));
00450        Cal->cal = icalcomponent_new_clone(event);
00451 
00452        /* Dezonify and decapsulate at the very last moment */
00453        ical_dezonify(Cal->cal);
00454        if (icalcomponent_isa(Cal->cal) != ICAL_VEVENT_COMPONENT) {
00455               cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT);
00456               if (cptr) {
00457                      cptr = icalcomponent_new_clone(cptr);
00458                      icalcomponent_free(Cal->cal);
00459                      Cal->cal = cptr;
00460               }
00461        }
00462 
00463        Cal->unread = unread;
00464        len = strlen(from);
00465        Cal->from = (char*)malloc(len+ 1);
00466        memcpy(Cal->from, from, len + 1);
00467        Cal->cal_msgnum = msgnum;
00468 
00469        /* Precalculate the starting date and time of this event, and store it in our top-level
00470         * structure.  Later, when we are rendering the calendar, we can just peek at these values
00471         * without having to break apart every calendar item.
00472         */
00473        ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
00474        if (ps != NULL) {
00475               dtstart = icalproperty_get_dtstart(ps);
00476               Cal->event_start = icaltime_as_timet(dtstart);
00477        }
00478 
00479        /* Do the same for the ending date and time.  It makes the day view much easier to render. */
00480        ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
00481        if (ps != NULL) {
00482               dtend = icalproperty_get_dtend(ps);
00483               Cal->event_end = icaltime_as_timet(dtend);
00484        }
00485 
00486        /* Store it in the hash list. */
00487        /* syslog(LOG_DEBUG, "INITIAL: %s", ctime(&Cal->event_start)); */
00488        Put(WCC->disp_cal_items, 
00489            (char*) &Cal->event_start,
00490            sizeof(Cal->event_start), 
00491            Cal, 
00492            delete_cal);
00493 
00494        /****************************** handle recurring events ******************************/
00495 
00496        if (icaltime_is_null_time(dtstart)) return;      /* Can't recur without a start time */
00497 
00498        if (!icaltime_is_null_time(dtend)) {             /* Need duration for recurrences */
00499               dur = icaltime_subtract(dtend, dtstart);
00500        }
00501        else {
00502               dur = icaltime_subtract(dtstart, dtstart);
00503        }
00504 
00505        /*
00506         * Just let libical iterate the recurrence, and keep looping back to the top of this function,
00507         * adding new hash entries that all point back to the same msgnum, until either the iteration
00508         * stops or some outer bound is reached.  The display code will automatically do the Right Thing.
00509         */
00510        cptr = event;
00511        if (icalcomponent_isa(cptr) != ICAL_VEVENT_COMPONENT) {
00512               cptr = icalcomponent_get_first_component(cptr, ICAL_VEVENT_COMPONENT);
00513        }
00514        if (!cptr) return;
00515        ps = icalcomponent_get_first_property(cptr, ICAL_DTSTART_PROPERTY);
00516        if (ps == NULL) return;
00517        dtstart = icalproperty_get_dtstart(ps);
00518        rrule = icalcomponent_get_first_property(cptr, ICAL_RRULE_PROPERTY);
00519        if (!rrule) return;
00520        recur = icalproperty_get_rrule(rrule);
00521        ritr = icalrecur_iterator_new(recur, dtstart);
00522        if (!ritr) return;
00523 
00524        while (next = icalrecur_iterator_next(ritr), ((!icaltime_is_null_time(next))&&(!stop_rr)) ) {
00525               ++num_recur;
00526               if (num_recur > 1) {        /* Skip the first one.  We already did it at the root. */
00527                      icalcomponent *cptr;
00528 
00529                      /* Note: anything we do here, we also have to do above for the root event. */
00530                      Cal = (disp_cal*) malloc(sizeof(disp_cal));
00531                      memset(Cal, 0, sizeof(disp_cal));
00532                      Cal->cal = icalcomponent_new_clone(event);
00533                      Cal->unread = unread;
00534                      len = strlen(from);
00535                      Cal->from = (char*)malloc(len+ 1);
00536                      memcpy(Cal->from, from, len + 1);
00537                      Cal->cal_msgnum = msgnum;
00538 
00539                      if (icalcomponent_isa(Cal->cal) == ICAL_VEVENT_COMPONENT) {
00540                             cptr = Cal->cal;
00541                      }
00542                      else {
00543                             cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT);
00544                      }
00545                      if (cptr) {
00546 
00547                             /* Remove any existing DTSTART properties */
00548                             while (       ps = icalcomponent_get_first_property(cptr, ICAL_DTSTART_PROPERTY),
00549                                    ps != NULL
00550                             ) {
00551                                    icalcomponent_remove_property(cptr, ps);
00552                             }
00553 
00554                             /* Add our shiny new DTSTART property from the iteration */
00555                             ps = icalproperty_new_dtstart(next);
00556                             icalcomponent_add_property(cptr, ps);
00557                             Cal->event_start = icaltime_as_timet(next);
00558                             final_recurrence = Cal->event_start;
00559 
00560                             /* Remove any existing DTEND properties */
00561                             while (       ps = icalcomponent_get_first_property(cptr, ICAL_DTEND_PROPERTY),
00562                                    (ps != NULL)
00563                             ) {
00564                                    icalcomponent_remove_property(cptr, ps);
00565                             }
00566 
00567                             /* Add our shiny new DTEND property from the iteration */
00568                             ps = icalproperty_new_dtend(icaltime_add(next, dur));
00569                             icalcomponent_add_property(cptr, ps);
00570 
00571                      }
00572 
00573                      /* Dezonify and decapsulate at the very last moment */
00574                      ical_dezonify(Cal->cal);
00575                      if (icalcomponent_isa(Cal->cal) != ICAL_VEVENT_COMPONENT) {
00576                             cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT);
00577                             if (cptr) {
00578                                    cptr = icalcomponent_new_clone(cptr);
00579                                    icalcomponent_free(Cal->cal);
00580                                    Cal->cal = cptr;
00581                             }
00582                      }
00583 
00584                      if (   (Cal->event_start > calv->lower_bound)
00585                             && (Cal->event_start < calv->upper_bound)
00586                      ) {
00587                             /* syslog(LOG_DEBUG, "REPEATS: %s", ctime(&Cal->event_start)); */
00588                             Put(WCC->disp_cal_items, 
00589                                    (char*) &Cal->event_start,
00590                                    sizeof(Cal->event_start), 
00591                                    Cal, 
00592                                    delete_cal
00593                             );
00594                      }
00595                      else {
00596                             delete_cal(Cal);
00597                      }
00598 
00599                      /* If an upper bound is set, stop when we go out of scope */
00600                      if (final_recurrence > calv->upper_bound) stop_rr = 1;
00601               }
00602        }
00603        icalrecur_iterator_free(ritr);
00604        /* syslog(9, "Performed %d recurrences; final one is %s", num_recur, ctime(&final_recurrence)); */
00605 }
00606 
00607 
00608 
00609 
00610 
00611 
00612 void process_ical_object(long msgnum, int unread,
00613                       char *from, 
00614                       char *FlatIcal, 
00615                       icalcomponent_kind which_kind,
00616                       IcalCallbackFunc CallBack,
00617                       calview *calv
00618        ) 
00619 {
00620        icalcomponent *cal, *c;
00621 
00622        cal = icalcomponent_new_from_string(FlatIcal);
00623        if (cal != NULL) {
00624 
00625               /* A which_kind of (-1) means just load the whole thing */
00626               if (which_kind == (-1)) {
00627                      CallBack(cal, msgnum, from, unread, calv);
00628               }
00629               
00630               /* Otherwise recurse and hunt */
00631               else {
00632                      
00633                      /* Simple components of desired type */
00634                      if (icalcomponent_isa(cal) == which_kind) {
00635                             CallBack(cal, msgnum, from, unread, calv);
00636                      }
00637                      
00638                      /* Subcomponents of desired type */
00639                      for (c = icalcomponent_get_first_component(cal, which_kind);
00640                           (c != 0);
00641                           c = icalcomponent_get_next_component(cal, which_kind)) {
00642                             CallBack(c, msgnum, from, unread, calv);
00643                      }
00644                      
00645               }
00646               
00647               icalcomponent_free(cal);
00648        }
00649 }
00650 
00651 /*
00652  * Code common to all icalendar display handlers.  Given a message number and a MIME
00653  * type, we load the message and hunt for that MIME type.  If found, we load
00654  * the relevant part, deserialize it into a libical component, filter it for
00655  * the requested object type, and feed it to the specified handler.
00656  */
00657 void load_ical_object(long msgnum, int unread,
00658                     icalcomponent_kind which_kind,
00659                     IcalCallbackFunc CallBack,
00660                     calview *calv,
00661                     int RenderAsync
00662        ) 
00663 {
00664        StrBuf *Buf;
00665        StrBuf *Data = NULL;
00666        const char *bptr;
00667        int Done = 0;
00668        char from[128] = "";
00669        char mime_partnum[256];
00670        char mime_filename[256];
00671        char mime_content_type[256];
00672        char mime_disposition[256];
00673        char relevant_partnum[256];
00674        char *relevant_source = NULL;
00675        int phase = 0;                            /* 0 = citadel headers, 1 = mime headers, 2 = body */
00676        char msg4_content_type[256] = "";
00677        char msg4_content_encoding[256] = "";
00678        int msg4_content_length = 0;
00679 
00680        relevant_partnum[0] = '\0';
00681        serv_printf("MSG4 %ld", msgnum);   /* we need the mime headers */
00682        Buf = NewStrBuf();
00683        StrBuf_ServGetln(Buf);
00684        if (GetServerStatus(Buf, NULL) != 1) {
00685               FreeStrBuf (&Buf);
00686               return;
00687        }
00688        while (!Done && (StrBuf_ServGetln(Buf)>=0)) {
00689               if ( (StrLength(Buf)==3) && 
00690                    !strcmp(ChrPtr(Buf), "000")) {
00691                      Done = 1;
00692                      break;
00693               }
00694               bptr = ChrPtr(Buf);
00695               switch (phase) {
00696               case 0:
00697                      if (!strncasecmp(bptr, "part=", 5)) {
00698                             extract_token(mime_filename, &bptr[5], 1, '|', sizeof mime_filename);
00699                             extract_token(mime_partnum, &bptr[5], 2, '|', sizeof mime_partnum);
00700                             extract_token(mime_disposition, &bptr[5], 3, '|', sizeof mime_disposition);
00701                             extract_token(mime_content_type, &bptr[5], 4, '|', sizeof mime_content_type);
00702                             /* do we care? mime_length = */extract_int(&bptr[5], 5);
00703 
00704                             if (  (!strcasecmp(mime_content_type, "text/calendar"))
00705                                   || (!strcasecmp(mime_content_type, "application/ics"))
00706                                   || (!strcasecmp(mime_content_type, "text/vtodo"))
00707                                   || (!strcasecmp(mime_content_type, "text/todo"))
00708                                    ) {
00709                                    strcpy(relevant_partnum, mime_partnum);
00710                             }
00711                      }
00712                      else if (!strncasecmp(bptr, "from=", 4)) {
00713                             extract_token(from, bptr, 1, '=', sizeof(from));
00714                      }
00715                      else if ((phase == 0) && (!strncasecmp(bptr, "text", 4))) {
00716                             phase = 1;
00717                      }
00718               break;
00719               case 1:
00720                      if (!IsEmptyStr(bptr)) {
00721                             if (!strncasecmp(bptr, "Content-type: ", 14)) {
00722                                    safestrncpy(msg4_content_type, &bptr[14], sizeof msg4_content_type);
00723                                    striplt(msg4_content_type);
00724                             }
00725                             else if (!strncasecmp(bptr, "Content-transfer-encoding: ", 27)) {
00726                                    safestrncpy(msg4_content_encoding, &bptr[27], sizeof msg4_content_encoding);
00727                                    striplt(msg4_content_type);
00728                             }
00729                             else if ((!strncasecmp(bptr, "Content-length: ", 16))) {
00730                                    msg4_content_length = atoi(&bptr[16]);
00731                             }
00732                             break;
00733                      }
00734                      else {
00735                             phase++;
00736                             
00737                             if ((msg4_content_length > 0)
00738                                 && ( !strcasecmp(msg4_content_encoding, "7bit"))
00739                                 && ((!strcasecmp(mime_content_type, "text/calendar"))
00740                                    || (!strcasecmp(mime_content_type, "application/ics"))
00741                                    || (!strcasecmp(mime_content_type, "text/vtodo"))
00742                                    || (!strcasecmp(mime_content_type, "text/todo"))
00743                                        )
00744                                    ) 
00745                             {
00746                             }
00747                      }
00748               case 2:
00749                      if (Data == NULL)
00750                             Data = NewStrBufPlain(NULL, msg4_content_length * 2);
00751                      if (msg4_content_length > 0) {
00752                             StrBuf_ServGetBLOBBuffered(Data, msg4_content_length);
00753                             phase ++;
00754                      }
00755                      else {
00756                             StrBufAppendBuf(Data, Buf, 0);
00757                             StrBufAppendBufPlain(Data, "\r\n", 1, 0);
00758                      }
00759               case 3:
00760                      StrBufAppendBuf(Data, Buf, 0);
00761               }
00762        }
00763        FreeStrBuf(&Buf);
00764 
00765        /* If MSG4 didn't give us the part we wanted, but we know that we can find it
00766         * as one of the other MIME parts, attempt to load it now.
00767         */
00768        if ((Data == NULL) && (!IsEmptyStr(relevant_partnum))) {
00769               Data = load_mimepart(msgnum, relevant_partnum);
00770        }
00771 
00772        if (Data != NULL) {
00773               relevant_source = (char*) ChrPtr(Data);
00774               process_ical_object(msgnum, unread,
00775                                 from, 
00776                                 relevant_source, 
00777                                 which_kind,
00778                                 CallBack,
00779                                 calv);
00780        }
00781        FreeStrBuf (&Data);
00782 
00783        icalmemory_free_ring();
00784 }
00785 
00786 /*
00787  * Display a calendar item
00788  */
00789 int calendar_LoadMsgFromServer(SharedMessageStatus *Stat, 
00790                             void **ViewSpecific, 
00791                             message_summary* Msg, 
00792                             int is_new, 
00793                             int i)
00794 {
00795        calview *c = (calview*) *ViewSpecific;
00796        load_ical_object(Msg->msgnum, is_new, (-1), display_individual_cal, c, 1);
00797        return 0;
00798 }
00799 
00800 /*
00801  * display the editor component for an event
00802  */
00803 void display_edit_event(void) {
00804        long msgnum = 0L;
00805 
00806        msgnum = lbstr("msgnum");
00807        if (msgnum > 0L) {
00808               /* existing event */
00809               load_ical_object(msgnum, 0, ICAL_VEVENT_COMPONENT, display_edit_individual_event, NULL, 0);
00810        }
00811        else {
00812               /* new event */
00813               display_edit_individual_event(NULL, 0L, "", 0, NULL);
00814        }
00815 }
00816 
00817 /*
00818  * save an edited event
00819  */
00820 void save_event(void) {
00821        long msgnum = 0L;
00822 
00823        msgnum = lbstr("msgnum");
00824 
00825        if (msgnum > 0L) {
00826               load_ical_object(msgnum, 0, (-1), save_individual_event, NULL, 0);
00827        }
00828        else {
00829               save_individual_event(NULL, 0L, "", 0, NULL);
00830        }
00831 }
00832 
00833 
00834 
00835 
00836 
00837 /*
00838  * Anonymous request of freebusy data for a user
00839  */
00840 void do_freebusy(void)
00841 {
00842        const char *req = ChrPtr(WC->Hdr->HR.ReqLine);
00843        char who[SIZ];
00844        char buf[SIZ];
00845        int len;
00846        long lines;
00847 
00848        extract_token(who, req, 0, ' ', sizeof who);
00849        if (!strncasecmp(who, "/freebusy/", 10)) {
00850               strcpy(who, &who[10]);
00851        }
00852        unescape_input(who);
00853 
00854        len = strlen(who);
00855        if ( (!strcasecmp(&who[len-4], ".vcf"))
00856             || (!strcasecmp(&who[len-4], ".ifb"))
00857             || (!strcasecmp(&who[len-4], ".vfb")) ) {
00858               who[len-4] = 0;
00859        }
00860 
00861        syslog(9, "freebusy requested for <%s>\n", who);
00862        serv_printf("ICAL freebusy|%s", who);
00863        serv_getln(buf, sizeof buf);
00864 
00865        if (buf[0] != '1') {
00866               hprintf("HTTP/1.1 404 %s\n", &buf[4]);
00867               output_headers(0, 0, 0, 0, 0, 0);
00868               hprintf("Content-Type: text/plain\r\n");
00869               wc_printf("%s\n", &buf[4]);
00870               end_burst();
00871               return;
00872        }
00873 
00874        read_server_text(WC->WBuf, &lines);
00875        http_transmit_thing("text/calendar", 0);
00876 }
00877 
00878 
00879 
00880 int calendar_Cleanup(void **ViewSpecific)
00881 {
00882        calview *c;
00883        
00884        c = (calview *) *ViewSpecific;
00885 
00886        wDumpContent(1);
00887        free (c);
00888        *ViewSpecific = NULL;
00889 
00890        return 0;
00891 }
00892 
00893 int __calendar_Cleanup(void **ViewSpecific)
00894 {
00895        calview *c;
00896        
00897        c = (calview *) *ViewSpecific;
00898 
00899        free (c);
00900        *ViewSpecific = NULL;
00901 
00902        return 0;
00903 }
00904 
00905 
00906 void 
00907 InitModule_CALENDAR
00908 (void)
00909 {
00910        RegisterReadLoopHandlerset(
00911               VIEW_CALENDAR,
00912               calendar_GetParamsGetServerCall,
00913               NULL,
00914               NULL,
00915               NULL,
00916               calendar_LoadMsgFromServer,
00917               calendar_RenderView_or_Tail,
00918               calendar_Cleanup);
00919 
00920        RegisterReadLoopHandlerset(
00921               VIEW_CALBRIEF,
00922               calendar_GetParamsGetServerCall,
00923               NULL,
00924               NULL,
00925               NULL,
00926               calendar_LoadMsgFromServer,
00927               calendar_RenderView_or_Tail,
00928               calendar_Cleanup);
00929 
00930 
00931 
00932        RegisterPreference("daystart", _("Calendar day view begins at:"), PRF_INT, NULL);
00933        RegisterPreference("dayend", _("Calendar day view ends at:"), PRF_INT, NULL);
00934        RegisterPreference("weekstart", _("Week starts on:"), PRF_INT, NULL);
00935 
00936        WebcitAddUrlHandler(HKEY("freebusy"), "", 0, do_freebusy, COOKIEUNNEEDED|ANONYMOUS|FORCE_SESSIONCLOSE);
00937        WebcitAddUrlHandler(HKEY("display_edit_task"), "", 0, display_edit_task, 0);
00938        WebcitAddUrlHandler(HKEY("display_edit_event"), "", 0, display_edit_event, 0);
00939        WebcitAddUrlHandler(HKEY("save_event"), "", 0, save_event, 0);
00940        WebcitAddUrlHandler(HKEY("respond_to_request"), "", 0, respond_to_request, 0);
00941        WebcitAddUrlHandler(HKEY("handle_rsvp"), "", 0, handle_rsvp, 0);
00942 }