diff options
-rw-r--r-- | calendar/icalendar.c | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/calendar/icalendar.c b/calendar/icalendar.c new file mode 100644 index 0000000000..43c1b46016 --- /dev/null +++ b/calendar/icalendar.c @@ -0,0 +1,722 @@ +/* + * icalendar server for gnomecal + * + * This module interfaces between libical and the gnomecal internal + * representation + * + * Copyright (C) 1999 The Free Software Foundation + * Authors: + * Russell Steinthal (rms39@columbia.edu) + * + */ + +#include <gnome.h> +#include <unistd.h> +#include <sys/stat.h> +#include "ical.h" +#include "calobj.h" +#include "calendar.h" + +void icalendar_calendar_load (Calendar* cal, char* fname); +static icalcomponent* icalendar_parse_file (char* fname); +static time_t icaltime_to_timet (struct icaltimetype* i); +static CalendarAlarm* parse_alarm (icalproperty *prop); +static iCalPerson* parse_person (icalproperty *prop, gchar *value); +static iCalRelation* parse_related (icalproperty *prop); + +/* Duplicate a string without memory leaks */ +static gchar* copy_str (gchar** store, gchar* src) +{ + if (*store) + g_free (*store); + return (*store = g_strdup (src)); +} + +static GList* +copy_to_list (GList** store, gchar* src) +{ + *store = g_list_prepend (*store, g_strdup (src)); + return *store; +} + +/* eh? + static icalcomponent* + ical_object_to_icalcomponent (iCalObject* ical) + { + icalcomponent *comp; +*/ + +static iCalObject * +ical_object_create_from_icalcomponent (icalcomponent* comp) +{ + iCalObject *ical = NULL; + iCalPerson *person; + icalcomponent *subcomp; + icalproperty *prop; + icalparameter *param; + struct icaltimetype ictime; + time_t *pt; + CalendarAlarm *alarm = NULL; + icalcomponent_kind compType; + struct icalgeotype geo; + struct icalperiodtype period; + + gboolean root = FALSE; + gboolean attachment = FALSE; + + char *tmpStr; /* this is a library-owned string */ + + ical = g_new0 (iCalObject, 1); + + compType = icalcomponent_isa (comp); + + switch (compType) { + case ICAL_XROOT_COMPONENT: + root = TRUE; + break; + case ICAL_XATTACH_COMPONENT: + attachment = TRUE; + break; + case ICAL_VEVENT_COMPONENT: + ical->type = ICAL_EVENT; + break; + case ICAL_VTODO_COMPONENT: + ical->type = ICAL_TODO; + break; + case ICAL_VJOURNAL_COMPONENT: + ical->type = ICAL_JOURNAL; + break; + case ICAL_VCALENDAR_COMPONENT: + /* FIXME: what does this mean? */ + break; + case ICAL_VFREEBUSY_COMPONENT: + ical->type = ICAL_FBREQUEST; + /* NOTE: This is not conclusive- you need to analyze + properties to determine whether this is an + FBREQUEST or an FBREPLY */ + break; + case ICAL_VTIMEZONE_COMPONENT: + ical->type = ICAL_TIMEZONE; + break; + case ICAL_VALARM_COMPONENT: + case ICAL_XAUDIOALARM_COMPONENT: + case ICAL_XDISPLAYALARM_COMPONENT: + case ICAL_XEMAILALARM_COMPONENT: + case ICAL_XPROCEDUREALARM_COMPONENT: + /* this should not be reached, since this loop should + only be processing first level components */ + break; + case ICAL_XSTANDARD_COMPONENT: + /* FIXME: what does this mean? */ + break; + case ICAL_XDAYLIGHT_COMPONENT: + /* FIXME: what does this mean? */ + break; + case ICAL_X_COMPONENT: + /* FIXME: what does this mean? */ + break; + case ICAL_VSCHEDULE_COMPONENT: + /* FIXME: what does this mean? */ + break; + case ICAL_XLICINVALID_COMPONENT: + /* FIXME: what does this mean? */ + break; + case ICAL_NO_COMPONENT: + case ICAL_ANY_COMPONENT: + /* should not occur */ + break; + case ICAL_VQUERY_COMPONENT: + case ICAL_VCAR_COMPONENT: + case ICAL_VCOMMAND_COMPONENT: + /* FIXME: what does this mean? */ + break; + } + + prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); + while (prop) { + switch (icalproperty_isa (prop)) { + case ICAL_CALSCALE_PROPERTY: + if (g_strcasecmp (icalproperty_get_calscale (prop), + "GREGORIAN")) + g_warning ("Unknown calendar format."); + break; + case ICAL_METHOD_PROPERTY: + /* FIXME: implement something here */ + break; + case ICAL_ATTACH_PROPERTY: + /* FIXME: not yet implemented */ + break; + case ICAL_CATEGORIES_PROPERTY: + copy_to_list (&ical->categories, + icalproperty_get_categories (prop)); + break; + case ICAL_CLASS_PROPERTY: + copy_str (&ical->class, icalproperty_get_class (prop)); + break; + case ICAL_COMMENT_PROPERTY: + tmpStr = icalproperty_get_comment (prop); + tmpStr = g_strconcat (icalproperty_get_comment (prop), + ical->comment, + NULL); + if (ical->comment) + g_free (ical->comment); + ical->comment = tmpStr; + break; + case ICAL_DESCRIPTION_PROPERTY: + copy_str (&ical->desc, + icalproperty_get_description (prop)); + break; + case ICAL_GEO_PROPERTY: + geo = icalproperty_get_geo (prop); + ical->geo.latitude = geo.lat; + ical->geo.longitude = geo.lon; + ical->geo.valid = TRUE; + break; + case ICAL_LOCATION_PROPERTY: + copy_str (&ical->location, + icalproperty_get_location (prop)); + break; + case ICAL_PERCENTCOMPLETE_PROPERTY: + ical->percent = icalproperty_get_percentcomplete (prop); + break; + case ICAL_PRIORITY_PROPERTY: + ical->priority = icalproperty_get_priority (prop); + if (ical->priority < 0 || ical->priority > 9) + g_warning ("Priority out-of-range (see RFC2445)"); + break; + case ICAL_RESOURCES_PROPERTY: + copy_to_list (&ical->resources, + icalproperty_get_resources (prop)); + break; + case ICAL_STATUS_PROPERTY: + copy_str (&ical->status, + icalproperty_get_status (prop)); + break; + case ICAL_SUMMARY_PROPERTY: + copy_str (&ical->summary, + icalproperty_get_summary (prop)); + break; + case ICAL_COMPLETED_PROPERTY: + ictime = icalproperty_get_completed (prop); + ical->completed = icaltime_to_timet (&ictime); + break; + case ICAL_DTEND_PROPERTY: + ictime = icalproperty_get_dtend (prop); + ical->dtend = icaltime_to_timet (&ictime); + param = icalproperty_get_first_parameter (prop, + ICAL_VALUE_PARAMETER); + ical->date_only = (icalparameter_get_value (param) == + ICAL_VALUE_DATE); + /* FIXME: We should handle timezone specifiers */ + break; + case ICAL_DUE_PROPERTY: + ictime = icalproperty_get_due (prop); + ical->dtend = icaltime_to_timet (&ictime); + param = icalproperty_get_first_parameter (prop, + ICAL_VALUE_PARAMETER); + ical->date_only = (icalparameter_get_value (param) == + ICAL_VALUE_DATE); + /* FIXME: We should handle timezone specifiers */ + break; + case ICAL_DTSTART_PROPERTY: + ictime = icalproperty_get_dtstart (prop); + ical->dtstart = icaltime_to_timet (&ictime); + param = icalproperty_get_first_parameter (prop, + ICAL_VALUE_PARAMETER); + ical->date_only = (icalparameter_get_value (param) == + ICAL_VALUE_DATE); + /* FIXME: We should handle timezone specifiers */ + break; + case ICAL_DURATION_PROPERTY: + /* FIXME: I don't see the necessary libical function */ + break; + case ICAL_FREEBUSY_PROPERTY: + period = icalproperty_get_freebusy (prop); + ical->dtstart = icaltime_to_timet (&(period.start)); + /* FIXME: period.end is specified as being relative to start, so +this may not be correct */ + ical->dtend = icaltime_to_timet (&(period.end)); + break; + case ICAL_TRANSP_PROPERTY: + tmpStr = icalproperty_get_transp (prop); + /* do not i18n the following string constant! */ + if (!g_strcasecmp (tmpStr, "TRANSPARENT")) + ical->transp = ICAL_TRANSPARENT; + else + ical->transp = ICAL_OPAQUE; + break; + case ICAL_TZID_PROPERTY: + case ICAL_TZNAME_PROPERTY: + case ICAL_TZOFFSETFROM_PROPERTY: + case ICAL_TZOFFSETTO_PROPERTY: + case ICAL_TZURL_PROPERTY: + /* no implementation for now */ + break; + case ICAL_ATTENDEE_PROPERTY: + tmpStr = icalproperty_get_attendee (prop); + person = parse_person (prop, tmpStr); + ical->attendee = g_list_prepend (ical->attendee, + person); + break; + case ICAL_CONTACT_PROPERTY: + tmpStr = icalproperty_get_contact (prop); + person = parse_person (prop, tmpStr); + ical->contact = g_list_prepend (ical->contact, person); + break; + case ICAL_ORGANIZER_PROPERTY: + tmpStr = icalproperty_get_organizer (prop); + person = parse_person (prop, tmpStr); + if (ical->organizer) + g_free (ical->organizer); + ical->organizer = person; + break; + case ICAL_RECURRENCEID_PROPERTY: + ictime = icalproperty_get_recurrenceid (prop); + ical->recurid = icaltime_to_timet (&ictime); + /* FIXME: Range parameter not implemented */ + break; + case ICAL_RELATEDTO_PROPERTY: + ical->related = g_list_prepend (ical->related, + parse_related (prop)); + break; + case ICAL_URL_PROPERTY: + copy_str (&ical->url, + icalproperty_get_url (prop)); + break; + case ICAL_UID_PROPERTY: + copy_str (&ical->uid, + icalproperty_get_uid (prop)); + break; + case ICAL_EXDATE_PROPERTY: + /* FIXME: This does not appear to parse + multiple exdate values in one property, as + allowed by the RFC; needs a libical fix */ + ictime = icalproperty_get_exdate (prop); + pt = g_new0 (time_t, 1); + *pt = icaltime_to_timet (&ictime); + ical->exdate = g_list_prepend (ical->exdate, pt); + break; + case ICAL_EXRULE_PROPERTY: + case ICAL_RDATE_PROPERTY: + case ICAL_RRULE_PROPERTY: + /* FIXME: need recursion processing */ + break; + case ICAL_ACTION_PROPERTY: + case ICAL_REPEAT_PROPERTY: + case ICAL_TRIGGER_PROPERTY: + /* should only occur in VALARM's, handled below */ + g_assert_not_reached(); + break; + case ICAL_CREATED_PROPERTY: + ictime = icalproperty_get_created (prop); + ical->created = icaltime_to_timet (&ictime); + break; + case ICAL_DTSTAMP_PROPERTY: + ictime = icalproperty_get_dtstamp (prop); + ical->dtstamp = icaltime_to_timet (&ictime); + break; + case ICAL_LASTMODIFIED_PROPERTY: + ictime = icalproperty_get_lastmodified (prop); + ical->last_mod = icaltime_to_timet (&ictime); + break; + case ICAL_SEQUENCE_PROPERTY: + ical->seq = icalproperty_get_sequence (prop); + break; + case ICAL_REQUESTSTATUS_PROPERTY: + copy_str (&ical->rstatus, + icalproperty_get_requeststatus (prop)); + break; + case ICAL_X_PROPERTY: + g_warning ("Unsupported X-property: %s", + icalproperty_as_ical_string (prop)); + break; + case ICAL_XLICERROR_PROPERTY: + g_warning ("Unsupported property: %s", + icalproperty_get_xlicerror (prop)); + break; + case ICAL_PRODID_PROPERTY: + case ICAL_VERSION_PROPERTY: + /* nothing to do for this property */ + break; + default: + g_warning ("Unsupported property: %s", icalproperty_as_ical_string +(prop)); + break; + + } + + prop = icalcomponent_get_next_property (comp, + ICAL_ANY_PROPERTY); + } + + /* now parse subcomponents --- should only be VALARM's */ + subcomp = icalcomponent_get_first_component (comp, + ICAL_ANY_COMPONENT); + while (subcomp) { + compType = icalcomponent_isa (subcomp); + switch (compType) { + case ICAL_VALARM_COMPONENT: + alarm = parse_alarm (subcomp); + if (alarm) + ical->alarms = g_list_prepend (ical->alarms, + alarm); + break; + default: + g_warning ("Only nested VALARM components are supported."); + } + + subcomp = icalcomponent_get_next_component (comp, + ICAL_ANY_COMPONENT); + } + + return ical; +} + +void +icalendar_calendar_load (Calendar* cal, char* fname) +{ + icalcomponent *comp; + icalcomponent *subcomp; + iCalObject *ical; + + comp = icalendar_parse_file (fname); + subcomp = icalcomponent_get_first_component (comp, + ICAL_ANY_COMPONENT); + while (subcomp) { + ical = ical_object_create_from_icalcomponent (subcomp); + if (ical->type != ICAL_EVENT && + ical->type != ICAL_TODO && + ical->type != ICAL_JOURNAL) { + g_warning ("Skipping unsupported iCalendar component."); + } else + calendar_add_object (cal, ical); + subcomp = icalcomponent_get_next_component (comp, + ICAL_ANY_COMPONENT); + } +} + +static icalcomponent* +icalendar_parse_file (char* fname) +{ + FILE* fp; + icalcomponent* comp = NULL; + gchar* str; + struct stat st; + int n; + + fp = fopen (fname, "r"); + if (!fp) { + g_warning ("Cannot open open calendar file."); + return NULL; + } + + stat (fname, &st); + + str = g_malloc (st.st_size + 2); + + n = fread ((gchar*) str, 1, st.st_size, fp); + if (n != st.st_size) { + g_warning ("Read error."); + } + str[n] = '\0'; + + fclose (fp); + + comp = icalparser_parse_string (str); + g_free (str); + + return comp; +} + +static time_t icaltime_to_timet (struct icaltimetype* i) +{ + extern long timezone; + struct tm t; + time_t ret; + + t.tm_year = i->year - 1900; + t.tm_mon = i->month - 1; + t.tm_mday = i->day; + if (!i->is_date) { + t.tm_hour = i->hour; + t.tm_min = i->minute; + t.tm_sec = i->second; + } else { + t.tm_hour = 0; + t.tm_min = 0; + t.tm_sec = 0; + } + + ret = mktime(&t); + + return ret - (i->is_utc ? timezone : 0); +} + +static iCalPerson* +parse_person (icalproperty* prop, gchar* value) +{ + icalparameter* param; + icalparameter_role role; + icalparameter_partstat partstat; + icalparameter_cutype cutype; + + iCalPerson* ret; + + ret = g_new0 (iCalPerson, 1); + + ret->addr = g_strdup (value); + + param = icalproperty_get_first_parameter (prop, + ICAL_CN_PARAMETER); + ret->name = g_strdup (icalparameter_get_cn (param)); + + param = icalproperty_get_first_parameter (prop, + ICAL_ROLE_PARAMETER); + if (param) { + role = icalparameter_get_role (param); + switch (role) { + case ICAL_ROLE_CHAIR: + ret->role = g_strdup ("CHAIR"); + break; + case ICAL_ROLE_REQPARTICIPANT: + ret->role = g_strdup ("REQPARTICIPANT"); + break; + case ICAL_ROLE_OPTPARTICIPANT: + ret->role = g_strdup ("OPTPARTICIPANT"); + break; + case ICAL_ROLE_NONPARTICIPANT: + ret->role = g_strdup ("NONPARTICIPANT"); + break; + case ICAL_ROLE_XNAME: + default: + ret->role = g_strdup ("UNKNOWN"); + break; + } + } else + ret->role = g_strdup ("REQPARTICIPANT"); + + param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER); + if (param) { + partstat = icalparameter_get_partstat (param); + switch (partstat) { + case ICAL_PARTSTAT_NEEDSACTION: + ret->partstat = g_strdup ("NEEDSACTION"); + break; + case ICAL_PARTSTAT_ACCEPTED: + ret->partstat = g_strdup ("ACCEPTED"); + break; + case ICAL_PARTSTAT_DECLINED: + ret->partstat = g_strdup ("DECLINED"); + break; + case ICAL_PARTSTAT_TENTATIVE: + ret->partstat = g_strdup ("TENTATIVE"); + break; + case ICAL_PARTSTAT_DELEGATED: + ret->partstat = g_strdup ("DELEGATED"); + break; + case ICAL_PARTSTAT_COMPLETED: + ret->partstat = g_strdup ("COMPLETED"); + break; + case ICAL_PARTSTAT_INPROCESS: + ret->partstat = g_strdup ("INPROCESS"); + break; + case ICAL_PARTSTAT_XNAME: + ret->partstat = g_strdup (icalparameter_get_xvalue (param)); + break; + default: + ret->partstat = g_strdup ("UNKNOWN"); + break; + } + } else + ret->partstat = g_strdup ("NEEDSACTION"); + + param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER); + if (param) + ret->rsvp = icalparameter_get_rsvp (param); + else + ret->rsvp = FALSE; + + param = icalproperty_get_first_parameter (prop, ICAL_CUTYPE_PARAMETER +); + if (param) { + cutype = icalparameter_get_cutype (param); + switch (cutype) { + case ICAL_CUTYPE_INDIVIDUAL: + ret->cutype = g_strdup ("INDIVIDUAL"); + break; + case ICAL_CUTYPE_GROUP: + ret->cutype = g_strdup ("GROUP"); + break; + case ICAL_CUTYPE_RESOURCE: + ret->cutype = g_strdup ("RESOURCE"); + break; + case ICAL_CUTYPE_ROOM: + ret->cutype = g_strdup ("ROOM"); + break; + case ICAL_CUTYPE_UNKNOWN: + case ICAL_CUTYPE_XNAME: + default: + ret->cutype = g_strdup ("UNKNOWN"); + break; + } + } else + ret->cutype = g_strdup ("INDIVIDUAL"); + + param = icalproperty_get_first_parameter (prop, ICAL_MEMBER_PARAMETER +); + while (param) { + copy_to_list (&ret->member, icalparameter_get_member (param)); + param = icalproperty_get_next_parameter (prop, + ICAL_MEMBER_PARAMETER); + } + + param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDTO_PARAMETER); + while (param) { + copy_to_list (&ret->deleg_to, + icalparameter_get_delegatedto (param)); + param = icalproperty_get_next_parameter (prop, + ICAL_DELEGATEDTO_PARAMETER); + } + + param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER); + while (param) { + copy_to_list (&ret->deleg_from, + icalparameter_get_delegatedfrom (param)); + param = icalproperty_get_next_parameter (prop, + ICAL_DELEGATEDFROM_PARAMETER); + } + + param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER +); + copy_str (&ret->sent_by, + icalparameter_get_sentby (param)); + + param = icalproperty_get_first_parameter (prop, ICAL_DIR_PARAMETER); + while (param) { + copy_to_list (&ret->deleg_to, + icalparameter_get_delegatedto (param)); + param = icalproperty_get_next_parameter (prop, + ICAL_DIR_PARAMETER); + } + + return ret; +} + +static iCalRelation* +parse_related (icalproperty* prop) +{ + iCalRelation* rel; + icalparameter* param; + icalparameter_reltype type; + + rel = g_new0 (iCalRelation, 1); + rel->uid = g_strdup (icalproperty_get_relatedto (prop)); + + param = icalproperty_get_first_parameter (prop, + ICAL_RELTYPE_PARAMETER); + if (param) { + type = icalparameter_get_reltype (param); + switch (type) { + case ICAL_RELTYPE_PARENT: + rel->reltype = g_strdup ("PARENT"); + break; + case ICAL_RELTYPE_CHILD: + rel->reltype = g_strdup ("CHILD"); + break; + case ICAL_RELTYPE_SIBLING: + rel->reltype = g_strdup ("SIBLING"); + break; + case ICAL_RELTYPE_XNAME: + rel->reltype = g_strdup (icalparameter_get_xvalue (param)); + break; + default: + rel->reltype = g_strdup ("UNKNOWN"); + break; + } + } else + rel->reltype = g_strdup ("PARENT"); + + return rel; +} + +#ifdef TEST + +int main(int argc, char* argv[]) +{ + icalcomponent* comp; + comp = icalendar_parse_file (argv[1]); + printf ("%s\n", icalcomponent_as_ical_string (comp)); + return 0; +} + +#endif + + +static CalendarAlarm* +parse_alarm (icalcomponent* comp) +{ + CalendarAlarm *alarm; + icalproperty *prop; + char *tmpStr; + struct icaldurationtype dur; + struct icalattachtype attach; + + g_return_val_if_fail (comp != NULL, NULL); + + alarm = g_new0 (CalendarAlarm, 1); + + prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); + while (prop) { + switch (icalproperty_isa (prop)) { + case ICAL_ACTION_PROPERTY: + tmpStr = icalproperty_get_action (prop); + if (!g_strcasecmp (tmpStr, "AUDIO")) + alarm->type = ALARM_AUDIO; + else if (!g_strcasecmp (tmpStr, "DISPLAY")) + alarm->type = ALARM_DISPLAY; + else if (!g_strcasecmp (tmpStr, "EMAIL")) + alarm->type = ALARM_MAIL; + else if (!g_strcasecmp (tmpStr, "PROCEDURE")) + alarm->type = ALARM_PROGRAM; + else + g_warning ("Unsupported alarm type!"); + break; + case ICAL_TRIGGER_PROPERTY: + /* FIXME: waiting on proper libical support */ + break; + case ICAL_REPEAT_PROPERTY: + alarm->snooze_repeat = icalproperty_get_repeat (prop); + break; + case ICAL_DURATION_PROPERTY: + dur = icalproperty_get_duration (prop); + alarm->snooze_secs = icaldurationtype_as_timet (dur); + break; + case ICAL_ATTACH_PROPERTY: + attach = icalproperty_get_attach (prop); + copy_str (&alarm->attach, + icalattachtype_get_url (&attach)); + break; + case ICAL_DESCRIPTION_PROPERTY: + copy_str (&alarm->desc, + icalproperty_get_description (prop)); + break; + case ICAL_SUMMARY_PROPERTY: + copy_str (&alarm->summary, + icalproperty_get_summary (prop)); + break; + case ICAL_ATTENDEE_PROPERTY: + copy_str (&alarm->attendee, + icalproperty_get_attendee (prop)); + break; + default: + g_warning ("Unsupported alarm property: %s", + icalproperty_as_ical_string (prop)); + break; + } + + prop = icalcomponent_get_next_property (comp, + ICAL_ANY_PROPERTY); + } + + return alarm; +} |