/* * 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 #include #include #include "icalendar.h" 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; } 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; } 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; }