/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * JP Rosevear * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "itip-utils.h" #include #include "dialogs/comp-editor-util.h" #include #define d(x) static const gchar *itip_methods[] = { "PUBLISH", "REQUEST", "REPLY", "ADD", "CANCEL", "RERESH", "COUNTER", "DECLINECOUNTER" }; static icalproperty_method itip_methods_enum[] = { ICAL_METHOD_PUBLISH, ICAL_METHOD_REQUEST, ICAL_METHOD_REPLY, ICAL_METHOD_ADD, ICAL_METHOD_CANCEL, ICAL_METHOD_REFRESH, ICAL_METHOD_COUNTER, ICAL_METHOD_DECLINECOUNTER, }; /** * itip_get_user_identities: * * Returns a %NULL-terminated array of name + address strings based on * registered mail identities. Free the returned array with g_strfreev(). * * Returns: an %NULL-terminated array of mail identity strings **/ gchar ** itip_get_user_identities (void) { EAccountList *account_list; EIterator *iterator; gchar **identities; gint length, ii = 0; account_list = e_get_account_list (); length = e_list_length (E_LIST (account_list)); iterator = e_list_get_iterator (E_LIST (account_list)); identities = g_new0 (gchar *, length + 1); while (e_iterator_is_valid (iterator)) { EAccount *account; /* XXX EIterator misuses const. */ account = (EAccount *) e_iterator_get (iterator); if (account->enabled) identities[ii++] = g_strdup_printf ( "%s <%s>", account->id->name, account->id->address); e_iterator_next (iterator); } g_object_unref (iterator); return identities; } /** * itip_get_fallback_identity: * * Returns a name + address string taken from the default mail identity, * but only if the corresponding account is enabled. If the account is * disabled, the function returns %NULL. This is meant to be used as a * fallback identity for organizers. Free the returned string with * g_free(). * * Returns: a fallback mail identity, or %NULL **/ gchar * itip_get_fallback_identity (void) { EAccount *account; gchar *identity = NULL; account = e_get_default_account (); if (account != NULL && account->enabled) identity = g_strdup_printf ( "%s <%s>", account->id->name, account->id->address); return identity; } /** * itip_address_is_user: * @address: an email address * * Looks for a registered mail identity with a matching email address. * * Returns: %TRUE if a match was found, %FALSE if not **/ gboolean itip_address_is_user (const gchar *address) { EAccountList *account_list; EAccount *account; g_return_val_if_fail (address != NULL, FALSE); account_list = e_get_account_list (); /* XXX EAccountList misuses const. */ account = (EAccount *) e_account_list_find ( account_list, E_ACCOUNT_FIND_ID_ADDRESS, address); return (account != NULL); } gboolean itip_organizer_is_user (ECalComponent *comp, ECalClient *cal_client) { return itip_organizer_is_user_ex (comp, cal_client, FALSE); } gboolean itip_organizer_is_user_ex (ECalComponent *comp, ECalClient *cal_client, gboolean skip_cap_test) { ECalComponentOrganizer organizer; const gchar *strip; gboolean user_org = FALSE; if (!e_cal_component_has_organizer (comp) || (!skip_cap_test && e_client_check_capability ( E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER))) return FALSE; e_cal_component_get_organizer (comp, &organizer); if (organizer.value != NULL) { strip = itip_strip_mailto (organizer.value); if (e_client_check_capability (E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) { gchar *email = NULL; if (e_client_get_backend_property_sync (E_CLIENT (cal_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &email, NULL, NULL) && !g_ascii_strcasecmp (email, strip)) { g_free (email); return TRUE; } g_free (email); return FALSE; } user_org = itip_address_is_user (strip); } return user_org; } gboolean itip_sentby_is_user (ECalComponent *comp, ECalClient *cal_client) { ECalComponentOrganizer organizer; const gchar *strip; gboolean user_sentby = FALSE; if (!e_cal_component_has_organizer (comp) || e_client_check_capability ( E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER)) return FALSE; e_cal_component_get_organizer (comp, &organizer); if (organizer.sentby != NULL) { strip = itip_strip_mailto (organizer.sentby); user_sentby = itip_address_is_user (strip); } return user_sentby; } static ECalComponentAttendee * get_attendee (GSList *attendees, gchar *address) { GSList *l; if (!address) return NULL; for (l = attendees; l; l = l->next) { ECalComponentAttendee *attendee = l->data; if (!g_ascii_strcasecmp (itip_strip_mailto (attendee->value), address)) { return attendee; } } return NULL; } static ECalComponentAttendee * get_attendee_if_attendee_sentby_is_user (GSList *attendees, gchar *address) { GSList *l; for (l = attendees; l; l = l->next) { ECalComponentAttendee *attendee = l->data; if (attendee->sentby && g_str_equal ( itip_strip_mailto (attendee->sentby), address)) { return attendee; } } return NULL; } static gchar * html_new_lines_for (const gchar *string) { gchar **lines; gchar *joined; lines = g_strsplit_set (string, "\n", -1); joined = g_strjoinv ("
", lines); g_strfreev (lines); return joined; } gchar * itip_get_comp_attendee (ECalComponent *comp, ECalClient *cal_client) { GSList *attendees; EAccountList *al; EAccount *a; EIterator *it; ECalComponentAttendee *attendee = NULL; gchar *address = NULL; e_cal_component_get_attendee_list (comp, &attendees); al = e_get_account_list (); if (cal_client) e_client_get_backend_property_sync ( E_CLIENT (cal_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &address, NULL, NULL); if (address && *address) { attendee = get_attendee (attendees, address); if (attendee) { gchar *user_email; user_email = g_strdup (itip_strip_mailto (attendee->value)); e_cal_component_free_attendee_list (attendees); g_free (address); return user_email; } attendee = get_attendee_if_attendee_sentby_is_user (attendees, address); if (attendee) { gchar *user_email; user_email = g_strdup (itip_strip_mailto (attendee->sentby)); e_cal_component_free_attendee_list (attendees); g_free (address); return user_email; } g_free (address); address = NULL; } for (it = e_list_get_iterator ((EList *) al); e_iterator_is_valid (it); e_iterator_next (it)) { a = (EAccount *) e_iterator_get (it); if (!a->enabled) continue; attendee = get_attendee (attendees, a->id->address); if (attendee) { gchar *user_email = g_strdup (itip_strip_mailto (attendee->value)); e_cal_component_free_attendee_list (attendees); return user_email; } /* If the account was not found in the attendees list, then * let's check the 'sentby' fields of the attendees if we can * find the account. */ attendee = get_attendee_if_attendee_sentby_is_user (attendees, a->id->address); if (attendee) { gchar *user_email = g_strdup (itip_strip_mailto (attendee->sentby)); e_cal_component_free_attendee_list (attendees); return user_email; } } /* We could not find the attendee in the component, so just give * the default account address if the email address is not set in * the backend. */ /* FIXME do we have a better way ? */ a = e_get_default_account (); address = g_strdup ((a != NULL) ? a->id->address : ""); e_cal_component_free_attendee_list (attendees); return address; } const gchar * itip_strip_mailto (const gchar *address) { if (address == NULL) return NULL; if (!g_ascii_strncasecmp (address, "mailto:", 7)) address += 7; return address; } static gchar * get_label (struct icaltimetype *tt, gboolean use_24_hour_format) { gchar buffer[1000]; struct tm tmp_tm; tmp_tm = icaltimetype_to_tm (tt); e_time_format_date_and_time ( &tmp_tm, use_24_hour_format, FALSE, FALSE, buffer, 1000); return g_strdup (buffer); } typedef struct { GHashTable *tzids; icalcomponent *icomp; ECalClient *client; icalcomponent *zones; } ItipUtilTZData; static void foreach_tzid_callback (icalparameter *param, gpointer data) { ItipUtilTZData *tz_data = data; const gchar *tzid; icaltimezone *zone = NULL; icalcomponent *vtimezone_comp; /* Get the TZID string from the parameter. */ tzid = icalparameter_get_tzid (param); if (!tzid || g_hash_table_lookup (tz_data->tzids, tzid)) return; /* Look for the timezone */ if (tz_data->zones != NULL) zone = icalcomponent_get_timezone (tz_data->zones, tzid); if (zone == NULL) zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); if (zone == NULL && tz_data->client != NULL) e_cal_client_get_timezone_sync (tz_data->client, tzid, &zone, NULL, NULL); if (zone == NULL) return; /* Convert it to a string and add it to the hash. */ vtimezone_comp = icaltimezone_get_component (zone); if (!vtimezone_comp) return; icalcomponent_add_component ( tz_data->icomp, icalcomponent_new_clone (vtimezone_comp)); g_hash_table_insert (tz_data->tzids, (gchar *) tzid, (gchar *) tzid); } static icalcomponent * comp_toplevel_with_zones (ECalComponentItipMethod method, ECalComponent *comp, ECalClient *cal_client, icalcomponent *zones) { icalcomponent *top_level, *icomp; icalproperty *prop; icalvalue *value; ItipUtilTZData tz_data; top_level = e_cal_util_new_top_level (); prop = icalproperty_new (ICAL_METHOD_PROPERTY); value = icalvalue_new_method (itip_methods_enum[method]); icalproperty_set_value (prop, value); icalcomponent_add_property (top_level, prop); icomp = e_cal_component_get_icalcomponent (comp); icomp = icalcomponent_new_clone (icomp); tz_data.tzids = g_hash_table_new (g_str_hash, g_str_equal); tz_data.icomp = top_level; tz_data.client = cal_client; tz_data.zones = zones; icalcomponent_foreach_tzid (icomp, foreach_tzid_callback, &tz_data); g_hash_table_destroy (tz_data.tzids); icalcomponent_add_component (top_level, icomp); return top_level; } static gboolean users_has_attendee (const GSList *users, const gchar *address) { const GSList *l; for (l = users; l != NULL; l = l->next) { if (!g_ascii_strcasecmp (address, l->data)) return TRUE; } return FALSE; } static gchar * comp_from (ECalComponentItipMethod method, ECalComponent *comp) { ECalComponentOrganizer organizer; ECalComponentAttendee *attendee; GSList *attendees; gchar *from; gchar *sender = NULL; switch (method) { case E_CAL_COMPONENT_METHOD_PUBLISH: return NULL; case E_CAL_COMPONENT_METHOD_REQUEST: return itip_get_comp_attendee (comp, NULL); case E_CAL_COMPONENT_METHOD_REPLY: sender = itip_get_comp_attendee (comp, NULL); if (sender != NULL) return sender; if (!e_cal_component_has_attendees (comp)) return NULL; case E_CAL_COMPONENT_METHOD_CANCEL: case E_CAL_COMPONENT_METHOD_ADD: e_cal_component_get_organizer (comp, &organizer); if (organizer.value == NULL) { e_notice (NULL, GTK_MESSAGE_ERROR, _("An organizer must be set.")); return NULL; } return g_strdup (itip_strip_mailto (organizer.value)); default: if (!e_cal_component_has_attendees (comp)) return NULL; e_cal_component_get_attendee_list (comp, &attendees); attendee = attendees->data; if (attendee->value != NULL) from = g_strdup (itip_strip_mailto (attendee->value)); else from = NULL; e_cal_component_free_attendee_list (attendees); return from; } } static EDestination ** comp_to_list (ECalComponentItipMethod method, ECalComponent *comp, const GSList *users, gboolean reply_all, const GSList *only_attendees) { ECalComponentOrganizer organizer; GSList *attendees, *l; GPtrArray *array = NULL; EDestination *destination; gint len; gchar *sender = NULL; union { gpointer *pdata; EDestination **destinations; } convert; switch (method) { case E_CAL_COMPONENT_METHOD_REQUEST: case E_CAL_COMPONENT_METHOD_CANCEL: e_cal_component_get_attendee_list (comp, &attendees); len = g_slist_length (attendees); if (len <= 0) { e_notice (NULL, GTK_MESSAGE_ERROR, _("At least one attendee is necessary")); e_cal_component_free_attendee_list (attendees); return NULL; } e_cal_component_get_organizer (comp, &organizer); if (organizer.value == NULL) { e_notice (NULL, GTK_MESSAGE_ERROR, _("An organizer must be set.")); return NULL; } array = g_ptr_array_new (); sender = itip_get_comp_attendee (comp, NULL); for (l = attendees; l != NULL; l = l->next) { ECalComponentAttendee *att = l->data; /* Bugfix: 688711 - Varadhan Resource is also considered as a "attendee". If the respective backend is able to successfully book resources automagically, it will appear in the users list and thereby won't get added to the list of destinations to send the meeting invite, otherwise, as a safety measure, a meeting invite will be sent to the resources as well. */ if (att->cutype != ICAL_CUTYPE_INDIVIDUAL && att->cutype != ICAL_CUTYPE_GROUP && att->cutype != ICAL_CUTYPE_RESOURCE) continue; else if (users_has_attendee (users, att->value)) continue; else if (att->sentby && users_has_attendee (users, att->sentby)) continue; else if (!g_ascii_strcasecmp ( att->value, organizer.value)) continue; else if (att->sentby && !g_ascii_strcasecmp ( att->sentby, organizer.sentby)) continue; else if (!g_ascii_strcasecmp ( itip_strip_mailto (att->value), sender)) continue; else if (att->status == ICAL_PARTSTAT_DELEGATED && (att->delto && *att->delto) && !(att->rsvp) && method == E_CAL_COMPONENT_METHOD_REQUEST) continue; else if (only_attendees && !comp_editor_have_in_new_attendees_lst ( only_attendees, itip_strip_mailto (att->value))) continue; destination = e_destination_new (); if (att->cn != NULL) e_destination_set_name (destination, att->cn); e_destination_set_email ( destination, itip_strip_mailto (att->value)); g_ptr_array_add (array, destination); d(printf ("itip-utils.c: comp_to_list: name: %s, email: %s\n", e_destination_get_name (destination), e_destination_get_email (destination))); } g_free (sender); e_cal_component_free_attendee_list (attendees); break; case E_CAL_COMPONENT_METHOD_REPLY: if (reply_all) { e_cal_component_get_attendee_list (comp, &attendees); len = g_slist_length (attendees); if (len <= 0) return NULL; array = g_ptr_array_new (); e_cal_component_get_organizer (comp, &organizer); sender = itip_get_comp_attendee (comp, NULL); for (l = attendees; l != NULL; l = l->next) { ECalComponentAttendee *att = l->data; if (att->cutype != ICAL_CUTYPE_INDIVIDUAL && att->cutype != ICAL_CUTYPE_GROUP) continue; else if (only_attendees && !comp_editor_have_in_new_attendees_lst ( only_attendees, itip_strip_mailto (att->value))) continue; destination = e_destination_new (); if (att->cn != NULL) e_destination_set_name (destination, att->cn); e_destination_set_email ( destination, itip_strip_mailto (att->value)); g_ptr_array_add (array, destination); } g_free (sender); e_cal_component_free_attendee_list (attendees); } else { array = g_ptr_array_new (); destination = e_destination_new (); e_cal_component_get_organizer (comp, &organizer); if (organizer.value) e_destination_set_email ( destination, itip_strip_mailto (organizer.value)); g_ptr_array_add (array, destination); } break; case E_CAL_COMPONENT_METHOD_ADD: case E_CAL_COMPONENT_METHOD_REFRESH: case E_CAL_COMPONENT_METHOD_COUNTER: case E_CAL_COMPONENT_METHOD_DECLINECOUNTER: e_cal_component_get_organizer (comp, &organizer); if (organizer.value == NULL) { e_notice (NULL, GTK_MESSAGE_ERROR, _("An organizer must be set.")); return NULL; } array = g_ptr_array_new (); destination = e_destination_new (); if (organizer.cn != NULL) e_destination_set_name (destination, organizer.cn); e_destination_set_email ( destination, itip_strip_mailto (organizer.value)); g_ptr_array_add (array, destination); /* send the status to delegatee to the delegate also*/ e_cal_component_get_attendee_list (comp, &attendees); sender = itip_get_comp_attendee (comp, NULL); for (l = attendees; l != NULL; l = l->next) { ECalComponentAttendee *att = l->data; if (att->cutype != ICAL_CUTYPE_INDIVIDUAL && att->cutype != ICAL_CUTYPE_GROUP) continue; if (!g_ascii_strcasecmp ( itip_strip_mailto (att->value), sender) || (att->sentby && !g_ascii_strcasecmp ( itip_strip_mailto (att->sentby), sender))) { if (!(att->delfrom && *att->delfrom)) break; destination = e_destination_new (); e_destination_set_email ( destination, itip_strip_mailto (att->delfrom)); g_ptr_array_add (array, destination); } } e_cal_component_free_attendee_list (attendees); break; case E_CAL_COMPONENT_METHOD_PUBLISH: if (users) { const GSList *list; array = g_ptr_array_new (); for (list = users; list != NULL; list = list->next) { destination = e_destination_new (); e_destination_set_email (destination, list->data); g_ptr_array_add (array, destination); } break; } default: break; } if (array == NULL) return NULL; g_ptr_array_add (array, NULL); convert.pdata = g_ptr_array_free (array, FALSE); return convert.destinations; } static gchar * comp_subject (ECalComponentItipMethod method, ECalComponent *comp) { ECalComponentText caltext; const gchar *description, *prefix = NULL; GSList *alist, *l; gchar *subject; gchar *sender; ECalComponentAttendee *a = NULL; e_cal_component_get_summary (comp, &caltext); if (caltext.value != NULL) description = caltext.value; else { switch (e_cal_component_get_vtype (comp)) { case E_CAL_COMPONENT_EVENT: description = _("Event information"); break; case E_CAL_COMPONENT_TODO: description = _("Task information"); break; case E_CAL_COMPONENT_JOURNAL: description = _("Memo information"); break; case E_CAL_COMPONENT_FREEBUSY: description = _("Free/Busy information"); break; default: description = _("Calendar information"); } } switch (method) { case E_CAL_COMPONENT_METHOD_PUBLISH: case E_CAL_COMPONENT_METHOD_REQUEST: /* FIXME: If this is an update to a previous * PUBLISH or REQUEST, then prefix = U_("Updated"); */ break; case E_CAL_COMPONENT_METHOD_REPLY: e_cal_component_get_attendee_list (comp, &alist); sender = itip_get_comp_attendee (comp, NULL); if (sender) { for (l = alist; l != NULL; l = l->next) { a = l->data; if ((sender && *sender) && (g_ascii_strcasecmp ( itip_strip_mailto (a->value), sender) || (a->sentby && g_ascii_strcasecmp ( itip_strip_mailto (a->sentby), sender)))) break; } g_free (sender); } if (alist != NULL) { switch (a->status) { case ICAL_PARTSTAT_ACCEPTED: /* Translators: This is part of the subject * line of a meeting request or update email. * The full subject line would be: * "Accepted: Meeting Name". */ prefix = C_("Meeting", "Accepted"); break; case ICAL_PARTSTAT_TENTATIVE: /* Translators: This is part of the subject * line of a meeting request or update email. * The full subject line would be: * "Tentatively Accepted: Meeting Name". */ prefix = C_("Meeting", "Tentatively Accepted"); break; case ICAL_PARTSTAT_DECLINED: /* Translators: This is part of the subject * line of a meeting request or update email. * The full subject line would be: * "Declined: Meeting Name". */ prefix = C_("Meeting", "Declined"); break; case ICAL_PARTSTAT_DELEGATED: /* Translators: This is part of the subject * line of a meeting request or update email. * The full subject line would be: * "Delegated: Meeting Name". */ prefix = C_("Meeting", "Delegated"); break; default: break; } e_cal_component_free_attendee_list (alist); } break; case E_CAL_COMPONENT_METHOD_ADD: /* Translators: This is part of the subject line of a * meeting request or update email. The full subject * line would be: "Updated: Meeting Name". */ prefix = C_("Meeting", "Updated"); break; case E_CAL_COMPONENT_METHOD_CANCEL: /* Translators: This is part of the subject line of a * meeting request or update email. The full subject * line would be: "Cancel: Meeting Name". */ prefix = C_("Meeting", "Cancel"); break; case E_CAL_COMPONENT_METHOD_REFRESH: /* Translators: This is part of the subject line of a * meeting request or update email. The full subject * line would be: "Refresh: Meeting Name". */ prefix = C_("Meeting", "Refresh"); break; case E_CAL_COMPONENT_METHOD_COUNTER: /* Translators: This is part of the subject line of a * meeting request or update email. The full subject * line would be: "Counter-proposal: Meeting Name". */ prefix = C_("Meeting", "Counter-proposal"); break; case E_CAL_COMPONENT_METHOD_DECLINECOUNTER: /* Translators: This is part of the subject line of a * meeting request or update email. The full subject * line would be: "Declined: Meeting Name". */ prefix = C_("Meeting", "Declined"); break; default: break; } if (prefix != NULL) subject = g_strdup_printf ("%s: %s", prefix, description); else subject = g_strdup (description); return subject; } static gchar * comp_content_type (ECalComponent *comp, ECalComponentItipMethod method) { const gchar *name; if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY) name = "freebusy.ifb"; else name = "calendar.ics"; return g_strdup_printf ( "text/calendar; name=\"%s\"; charset=utf-8; METHOD=%s", name, itip_methods[method]); } static const gchar * comp_filename (ECalComponent *comp) { if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY) return "freebusy.ifb"; else return "calendar.ics"; } static gchar * comp_description (ECalComponent *comp, gboolean use_24_hour_format) { gchar *description; ECalComponentDateTime dt; gchar *start = NULL, *end = NULL; switch (e_cal_component_get_vtype (comp)) { case E_CAL_COMPONENT_EVENT: description = g_strdup (_("Event information")); break; case E_CAL_COMPONENT_TODO: description = g_strdup (_("Task information")); break; case E_CAL_COMPONENT_JOURNAL: description = g_strdup (_("Memo information")); break; case E_CAL_COMPONENT_FREEBUSY: e_cal_component_get_dtstart (comp, &dt); if (dt.value) start = get_label (dt.value, use_24_hour_format); e_cal_component_free_datetime (&dt); e_cal_component_get_dtend (comp, &dt); if (dt.value) end = get_label (dt.value, use_24_hour_format); e_cal_component_free_datetime (&dt); if (start != NULL && end != NULL) description = g_strdup_printf ( _("Free/Busy information (%s to %s)"), start, end); else description = g_strdup (_("Free/Busy information")); g_free (start); g_free (end); break; default: description = g_strdup (_("iCalendar information")); break; } return description; } static gboolean comp_server_send (ECalComponentItipMethod method, ECalComponent *comp, ECalClient *cal_client, icalcomponent *zones, GSList **users) { icalcomponent *top_level, *returned_icalcomp = NULL; gboolean retval = TRUE; GError *error = NULL; top_level = comp_toplevel_with_zones (method, comp, cal_client, zones); d(printf ("itip-utils.c: comp_server_send: calling e_cal_send_objects... \n")); if (!e_cal_client_send_objects_sync (cal_client, top_level, users, &returned_icalcomp, NULL, &error)) { /* FIXME Really need a book problem status code */ d(printf ("itip-utils.c: return value from e_cal_send_objects is: %d", error->code)); if (error) { if (g_error_matches (error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_ID_ALREADY_EXISTS)) { e_notice ( NULL, GTK_MESSAGE_ERROR, _("Unable to book a resource, the " "new event collides with some other.")); } else { gchar *msg = g_strconcat ( _("Unable to book a resource, error: "), error->message, NULL); e_notice (NULL, GTK_MESSAGE_ERROR, msg); g_free (msg); } retval = FALSE; } } else { d(printf ("itip-utils.c: e_cal_send_objects returned without errors\n")); } g_clear_error (&error); if (returned_icalcomp) icalcomponent_free (returned_icalcomp); icalcomponent_free (top_level); return retval; } static gboolean comp_limit_attendees (ECalComponent *comp) { icalcomponent *icomp; icalproperty *prop; gboolean found = FALSE, match = FALSE; GSList *l, *list = NULL; icomp = e_cal_component_get_icalcomponent (comp); for (prop = icalcomponent_get_first_property (icomp, ICAL_ATTENDEE_PROPERTY); prop != NULL; prop = icalcomponent_get_next_property (icomp, ICAL_ATTENDEE_PROPERTY)) { gchar *attendee; gchar *attendee_text; icalparameter *param; const gchar *attendee_sentby; gchar *attendee_sentby_text = NULL; /* If we've already found something, just erase the rest */ if (found) { list = g_slist_prepend (list, prop); continue; } attendee = icalproperty_get_value_as_string_r (prop); if (!attendee) continue; attendee_text = g_strdup (itip_strip_mailto (attendee)); g_free (attendee); attendee_text = g_strstrip (attendee_text); found = match = itip_address_is_user (attendee_text); if (!found) { param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); if (param) { attendee_sentby = icalparameter_get_sentby (param); attendee_sentby = itip_strip_mailto (attendee_sentby); attendee_sentby_text = g_strstrip (g_strdup (attendee_sentby)); found = match = itip_address_is_user ( attendee_sentby_text); } } g_free (attendee_text); g_free (attendee_sentby_text); if (!match) list = g_slist_prepend (list, prop); } for (l = list; l != NULL; l = l->next) { prop = l->data; icalcomponent_remove_property (icomp, prop); icalproperty_free (prop); } g_slist_free (list); return found; } static void comp_sentby (ECalComponent *comp, ECalClient *cal_client) { ECalComponentOrganizer organizer; GSList * attendees, *l; gchar *user = NULL; e_cal_component_get_organizer (comp, &organizer); if (!organizer.value) { EAccount *a = e_get_default_account (); organizer.value = g_strdup_printf ("MAILTO:%s", a->id->address); organizer.sentby = NULL; organizer.cn = a->id->name; organizer.language = NULL; e_cal_component_set_organizer (comp, &organizer); g_free ((gchar *) organizer.value); return; } e_cal_component_get_attendee_list (comp, &attendees); user = itip_get_comp_attendee (comp, cal_client); for (l = attendees; l; l = l->next) { ECalComponentAttendee *a = l->data; if (!g_ascii_strcasecmp ( itip_strip_mailto (a->value), user) || (a->sentby && !g_ascii_strcasecmp ( itip_strip_mailto (a->sentby), user))) { g_free (user); return; } } if (!itip_organizer_is_user (comp, cal_client) && !itip_sentby_is_user (comp, cal_client)) { EAccount *a = e_get_default_account (); organizer.value = g_strdup (organizer.value); organizer.sentby = g_strdup_printf ("MAILTO:%s", a->id->address); organizer.cn = g_strdup (organizer.cn); organizer.language = g_strdup (organizer.language); e_cal_component_set_organizer (comp, &organizer); g_free ((gchar *) organizer.value); g_free ((gchar *) organizer.sentby); g_free ((gchar *) organizer.cn); g_free ((gchar *) organizer.language); } } static ECalComponent * comp_minimal (ECalComponent *comp, gboolean attendee) { ECalComponent *clone; icalcomponent *icomp, *icomp_clone; icalproperty *prop; ECalComponentOrganizer organizer; const gchar *uid; GSList *comments; struct icaltimetype itt; ECalComponentRange recur_id; clone = e_cal_component_new (); e_cal_component_set_new_vtype (clone, e_cal_component_get_vtype (comp)); if (attendee) { GSList *attendees; e_cal_component_get_attendee_list (comp, &attendees); e_cal_component_set_attendee_list (clone, attendees); if (!comp_limit_attendees (clone)) { e_notice (NULL, GTK_MESSAGE_ERROR, _("You must be an attendee of the event.")); goto error; } } itt = icaltime_from_timet_with_zone (time (NULL), FALSE, icaltimezone_get_utc_timezone ()); e_cal_component_set_dtstamp (clone, &itt); e_cal_component_get_organizer (comp, &organizer); if (organizer.value == NULL) goto error; e_cal_component_set_organizer (clone, &organizer); e_cal_component_get_uid (comp, &uid); e_cal_component_set_uid (clone, uid); e_cal_component_get_comment_list (comp, &comments); if (g_slist_length (comments) <= 1) { e_cal_component_set_comment_list (clone, comments); } else { GSList *l = comments; comments = g_slist_remove_link (comments, l); e_cal_component_set_comment_list (clone, l); e_cal_component_free_text_list (l); } e_cal_component_free_text_list (comments); e_cal_component_get_recurid (comp, &recur_id); if (recur_id.datetime.value != NULL) e_cal_component_set_recurid (clone, &recur_id); icomp = e_cal_component_get_icalcomponent (comp); icomp_clone = e_cal_component_get_icalcomponent (clone); for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY); prop != NULL; prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY)) { icalproperty *p; p = icalproperty_new_clone (prop); icalcomponent_add_property (icomp_clone, p); } e_cal_component_rescan (clone); return clone; error: g_object_unref (clone); return NULL; } static void strip_x_microsoft_props (ECalComponent *comp) { GSList *lst = NULL, *l; icalcomponent *icalcomp; icalproperty *icalprop; g_return_if_fail (comp != NULL); icalcomp = e_cal_component_get_icalcomponent (comp); g_return_if_fail (icalcomp != NULL); for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); icalprop; icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { const gchar *x_name = icalproperty_get_x_name (icalprop); if (x_name && g_ascii_strncasecmp (x_name, "X-MICROSOFT-", 12) == 0) lst = g_slist_prepend (lst, icalprop); } for (l = lst; l != NULL; l = l->next) { icalprop = l->data; icalcomponent_remove_property (icalcomp, icalprop); icalproperty_free (icalprop); } g_slist_free (lst); } static ECalComponent * comp_compliant (ECalComponentItipMethod method, ECalComponent *comp, ECalClient *client, icalcomponent *zones, icaltimezone *default_zone, gboolean strip_alarms) { ECalComponent *clone, *temp_clone; struct icaltimetype itt; clone = e_cal_component_clone (comp); itt = icaltime_from_timet_with_zone (time (NULL), FALSE, icaltimezone_get_utc_timezone ()); e_cal_component_set_dtstamp (clone, &itt); /* Make UNTIL date a datetime in a simple recurrence */ if (e_cal_component_has_recurrences (clone) && e_cal_component_has_simple_recurrence (clone)) { GSList *rrule_list; struct icalrecurrencetype *r; e_cal_component_get_rrule_list (clone, &rrule_list); r = rrule_list->data; if (!icaltime_is_null_time (r->until) && r->until.is_date) { ECalComponentDateTime dt; icaltimezone *from_zone = NULL, *to_zone; e_cal_component_get_dtstart (clone, &dt); if (dt.value->is_date) { from_zone = default_zone; } else if (dt.tzid == NULL) { from_zone = icaltimezone_get_utc_timezone (); } else { if (zones != NULL) from_zone = icalcomponent_get_timezone (zones, dt.tzid); if (from_zone == NULL) from_zone = icaltimezone_get_builtin_timezone_from_tzid (dt.tzid); if (from_zone == NULL && client != NULL) /* FIXME Error checking */ e_cal_client_get_timezone_sync ( client, dt.tzid, &from_zone, NULL, NULL); } to_zone = icaltimezone_get_utc_timezone (); r->until.hour = dt.value->hour; r->until.minute = dt.value->minute; r->until.second = dt.value->second; r->until.is_date = FALSE; icaltimezone_convert_time (&r->until, from_zone, to_zone); r->until.is_utc = TRUE; e_cal_component_free_datetime (&dt); e_cal_component_set_rrule_list (clone, rrule_list); e_cal_component_abort_sequence (clone); } e_cal_component_free_recur_list (rrule_list); } /* We delete incoming alarms if requested, even this helps with outlook */ if (strip_alarms) { e_cal_component_remove_all_alarms (clone); } else { /* Always strip procedure alarms, because of security */ GList *uids, *l; uids = e_cal_component_get_alarm_uids (clone); for (l = uids; l; l = l->next) { ECalComponentAlarm *alarm; ECalComponentAlarmAction action = E_CAL_COMPONENT_ALARM_UNKNOWN; alarm = e_cal_component_get_alarm (clone, (const gchar *) l->data); if (alarm) { e_cal_component_alarm_get_action (alarm, &action); e_cal_component_alarm_free (alarm); if (action == E_CAL_COMPONENT_ALARM_PROCEDURE) e_cal_component_remove_alarm (clone, (const gchar *) l->data); } } cal_obj_uid_list_free (uids); } strip_x_microsoft_props (clone); /* Strip X-LIC-ERROR stuff */ e_cal_component_strip_errors (clone); /* Comply with itip spec */ switch (method) { case E_CAL_COMPONENT_METHOD_PUBLISH: comp_sentby (clone, client); e_cal_component_set_attendee_list (clone, NULL); break; case E_CAL_COMPONENT_METHOD_REQUEST: comp_sentby (clone, client); break; case E_CAL_COMPONENT_METHOD_CANCEL: comp_sentby (clone, client); break; case E_CAL_COMPONENT_METHOD_REPLY: break; case E_CAL_COMPONENT_METHOD_ADD: break; case E_CAL_COMPONENT_METHOD_REFRESH: /* Need to remove almost everything */ temp_clone = comp_minimal (clone, TRUE); g_object_unref (clone); clone = temp_clone; break; case E_CAL_COMPONENT_METHOD_COUNTER: break; case E_CAL_COMPONENT_METHOD_DECLINECOUNTER: /* Need to remove almost everything */ temp_clone = comp_minimal (clone, FALSE); g_object_unref (clone); clone = temp_clone; break; default: break; } return clone; } static void append_cal_attachments (EMsgComposer *composer, ECalComponent *comp, GSList *attach_list) { struct CalMimeAttach *mime_attach; GSList *l; for (l = attach_list; l; l = l->next) { CamelMimePart *attachment; mime_attach = (struct CalMimeAttach *) l->data; attachment = camel_mime_part_new (); camel_mime_part_set_content ( attachment, mime_attach->encoded_data, mime_attach->length, mime_attach->content_type); if (mime_attach->content_id) camel_mime_part_set_content_id ( attachment, mime_attach->content_id); if (mime_attach->filename != NULL) camel_mime_part_set_filename ( attachment, mime_attach->filename); if (mime_attach->description != NULL) camel_mime_part_set_description ( attachment, mime_attach->description); if (mime_attach->disposition) camel_mime_part_set_disposition ( attachment, "inline"); else camel_mime_part_set_disposition ( attachment, "attachment"); e_msg_composer_attach (composer, attachment); g_object_unref (attachment); g_free (mime_attach->filename); g_free (mime_attach->content_type); g_free (mime_attach->content_id); g_free (mime_attach->description); g_free (mime_attach->encoded_data); g_free (mime_attach); } g_slist_free (attach_list); } static EAccount * find_enabled_account (EAccountList *accounts, const gchar *id_address) { EIterator *it; EAccount *account = NULL; g_return_val_if_fail (accounts != NULL, NULL); if (!id_address) return NULL; for (it = e_list_get_iterator ((EList *) accounts); e_iterator_is_valid (it); e_iterator_next (it)) { account = (EAccount *) e_iterator_get (it); if (account && account->enabled && account->id && account->id->address && g_ascii_strcasecmp (account->id->address, id_address) == 0) break; account = NULL; } return account; } static void setup_from (ECalComponentItipMethod method, ECalComponent *comp, ECalClient *cal_client, EComposerHeaderTable *table) { EAccountList *accounts; accounts = e_composer_header_table_get_account_list (table); if (accounts) { EAccount *account = NULL; /* always use organizer's email when user is an organizer */ if (itip_organizer_is_user (comp, cal_client)) { ECalComponentOrganizer organizer = {0}; e_cal_component_get_organizer (comp, &organizer); if (organizer.value != NULL) { account = find_enabled_account ( accounts, itip_strip_mailto (organizer.value)); } } if (!account) { gchar *from = comp_from (method, comp); if (from) account = find_enabled_account (accounts, from); g_free (from); } if (account) e_composer_header_table_set_account (table, account); } } gboolean itip_send_comp (ECalComponentItipMethod method, ECalComponent *send_comp, ECalClient *cal_client, icalcomponent *zones, GSList *attachments_list, GSList *users, gboolean strip_alarms, gboolean only_new_attendees) { EShell *shell; EShellSettings *shell_settings; EMsgComposer *composer; EComposerHeaderTable *table; EDestination **destinations; ECalComponent *comp = NULL; icalcomponent *top_level = NULL; icaltimezone *default_zone; gchar *ical_string = NULL; gchar *content_type = NULL; gchar *subject = NULL; gboolean use_24_hour_format; gboolean retval = FALSE; /* FIXME Pass this in. */ shell = e_shell_get_default (); shell_settings = e_shell_get_shell_settings (shell); default_zone = e_shell_settings_get_pointer ( shell_settings, "cal-timezone"); use_24_hour_format = e_shell_settings_get_boolean ( shell_settings, "cal-use-24-hour-format"); /* check whether backend could handle auto-saving requests/updates */ if (method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_client_check_save_schedules (cal_client)) return TRUE; /* Give the server a chance to manipulate the comp */ if (method != E_CAL_COMPONENT_METHOD_PUBLISH) { d(printf ("itip-utils.c: itip_send_comp: calling comp_server_send... \n")); if (!comp_server_send (method, send_comp, cal_client, zones, &users)) goto cleanup; } /* check whether backend could handle sending requests/updates */ if (method != E_CAL_COMPONENT_METHOD_PUBLISH && e_client_check_capability (E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) { if (users) { g_slist_foreach (users, (GFunc) g_free, NULL); g_slist_free (users); } return TRUE; } /* Tidy up the comp */ comp = comp_compliant ( method, send_comp, cal_client, zones, default_zone, strip_alarms); if (comp == NULL) goto cleanup; /* Recipients */ destinations = comp_to_list ( method, comp, users, FALSE, only_new_attendees ? g_object_get_data ( G_OBJECT (send_comp), "new-attendees") : NULL); if (method != E_CAL_COMPONENT_METHOD_PUBLISH) { if (destinations == NULL) { /* We sent them all via the server */ retval = TRUE; goto cleanup; } } /* Subject information */ subject = comp_subject (method, comp); composer = e_msg_composer_new (shell); table = e_msg_composer_get_header_table (composer); setup_from (method, send_comp, cal_client, table); e_composer_header_table_set_subject (table, subject); e_composer_header_table_set_destinations_to (table, destinations); e_destination_freev (destinations); /* Content type */ content_type = comp_content_type (comp, method); top_level = comp_toplevel_with_zones (method, comp, cal_client, zones); ical_string = icalcomponent_as_ical_string_r (top_level); if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) { e_msg_composer_set_body (composer, ical_string, content_type); } else { CamelMimePart *attachment; const gchar *filename; gchar *description; gchar *body; filename = comp_filename (comp); description = comp_description (comp, use_24_hour_format); body = camel_text_to_html ( description, CAMEL_MIME_FILTER_TOHTML_PRE, 0); e_msg_composer_set_body_text (composer, body, TRUE); g_free (body); attachment = camel_mime_part_new (); camel_mime_part_set_content ( attachment, ical_string, strlen (ical_string), content_type); if (filename != NULL && *filename != '\0') camel_mime_part_set_filename (attachment, filename); if (description != NULL && *description != '\0') camel_mime_part_set_description (attachment, description); camel_mime_part_set_disposition (attachment, "inline"); e_msg_composer_attach (composer, attachment); g_object_unref (attachment); g_free (description); } append_cal_attachments (composer, comp, attachments_list); if ((method == E_CAL_COMPONENT_METHOD_PUBLISH) && !users) gtk_widget_show (GTK_WIDGET (composer)); else e_msg_composer_send (composer); retval = TRUE; cleanup: if (comp != NULL) g_object_unref (comp); if (top_level != NULL) icalcomponent_free (top_level); if (users) { g_slist_foreach (users, (GFunc) g_free, NULL); g_slist_free (users); } g_free (content_type); g_free (subject); g_free (ical_string); return retval; } gboolean reply_to_calendar_comp (ECalComponentItipMethod method, ECalComponent *send_comp, ECalClient *cal_client, gboolean reply_all, icalcomponent *zones, GSList *attachments_list) { EShell *shell; EShellSettings *shell_settings; EMsgComposer *composer; EComposerHeaderTable *table; EDestination **destinations; ECalComponent *comp = NULL; icalcomponent *top_level = NULL; icaltimezone *default_zone; GSList *users = NULL; gchar *subject = NULL; gchar *ical_string = NULL; gboolean retval = FALSE; /* FIXME Pass this in. */ shell = e_shell_get_default (); shell_settings = e_shell_get_shell_settings (shell); default_zone = e_shell_settings_get_pointer ( shell_settings, "cal-timezone"); /* Tidy up the comp */ comp = comp_compliant ( method, send_comp, cal_client, zones, default_zone, TRUE); if (comp == NULL) goto cleanup; /* Recipients */ destinations = comp_to_list (method, comp, users, reply_all, NULL); /* Subject information */ subject = comp_subject (method, comp); composer = e_msg_composer_new (shell); table = e_msg_composer_get_header_table (composer); setup_from (method, send_comp, cal_client, table); e_composer_header_table_set_subject (table, subject); e_composer_header_table_set_destinations_to (table, destinations); e_destination_freev (destinations); top_level = comp_toplevel_with_zones (method, comp, cal_client, zones); ical_string = icalcomponent_as_ical_string_r (top_level); if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) { GString *body; gchar *orig_from = NULL; const gchar *description = NULL; gchar *subject = NULL; const gchar *location = NULL; gchar *time = NULL; gchar *html_description = NULL; GSList *text_list = NULL; ECalComponentOrganizer organizer; ECalComponentText text; ECalComponentDateTime dtstart; icaltimezone *start_zone = NULL; time_t start; e_cal_component_get_description_list (comp, &text_list); if (text_list) { ECalComponentText text = *((ECalComponentText *) text_list->data); if (text.value) description = text.value; else description = ""; } else { description = ""; } e_cal_component_free_text_list (text_list); e_cal_component_get_summary (comp, &text); if (text.value) subject = g_strdup (text.value); e_cal_component_get_organizer (comp, &organizer); if (organizer.value) orig_from = g_strdup (itip_strip_mailto (organizer.value)); e_cal_component_get_location (comp, &location); if (!location) location = "Unspecified"; e_cal_component_get_dtstart (comp, &dtstart); if (dtstart.value) { start_zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid); if (!start_zone && dtstart.tzid) { GError *error = NULL; e_cal_client_get_timezone_sync ( cal_client, dtstart.tzid, &start_zone, NULL, &error); if (error != NULL) { g_warning ( "%s: Couldn't get timezone '%s' from server: %s", G_STRFUNC, dtstart.tzid ? dtstart.tzid : "", error->message); g_error_free (error); } } if (!start_zone || dtstart.value->is_date) start_zone = default_zone; start = icaltime_as_timet_with_zone (*dtstart.value, start_zone); time = g_strdup (ctime (&start)); } body = g_string_new ( "



" "______ Original Appointment ______ " "

"); if (orig_from && *orig_from) g_string_append_printf (body, "" "", orig_from); g_free (orig_from); if (subject) g_string_append_printf (body, "" "", subject); g_free (subject); g_string_append_printf (body, "" "", location); if (time) g_string_append_printf (body, "" "", time); g_free (time); g_string_append_printf (body, "
From:%s
Subject:%s
Location:%s
Time:%s

"); html_description = html_new_lines_for (description); g_string_append (body, html_description); g_free (html_description); e_msg_composer_set_body_text (composer, body->str, TRUE); g_string_free (body, TRUE); } gtk_widget_show (GTK_WIDGET (composer)); retval = TRUE; cleanup: if (comp != NULL) g_object_unref (comp); if (top_level != NULL) icalcomponent_free (top_level); if (users) { g_slist_foreach (users, (GFunc) g_free, NULL); g_slist_free (users); } g_free (subject); g_free (ical_string); return retval; } gboolean itip_publish_begin (ECalComponent *pub_comp, ECalClient *cal_client, gboolean cloned, ECalComponent **clone) { icalcomponent *icomp = NULL, *icomp_clone = NULL; icalproperty *prop; if (e_cal_component_get_vtype (pub_comp) == E_CAL_COMPONENT_FREEBUSY) { if (!cloned) *clone = e_cal_component_clone (pub_comp); else { icomp = e_cal_component_get_icalcomponent (pub_comp); icomp_clone = e_cal_component_get_icalcomponent (*clone); for (prop = icalcomponent_get_first_property (icomp, ICAL_FREEBUSY_PROPERTY); prop != NULL; prop = icalcomponent_get_next_property (icomp, ICAL_FREEBUSY_PROPERTY)) { icalproperty *p; p = icalproperty_new_clone (prop); icalcomponent_add_property (icomp_clone, p); } } } return TRUE; } static void fb_sort (struct icalperiodtype *ipt, gint fb_count) { gint i,j; if (ipt == NULL || fb_count == 0) return; for (i = 0; i < fb_count - 1; i++) { for (j = i + 1; j < fb_count; j++) { struct icalperiodtype temp; if (icaltime_compare (ipt[i].start, ipt[j].start) < 0) continue; if (icaltime_compare (ipt[i].start, ipt[j].start) == 0) { if (icaltime_compare (ipt[i].end, ipt[j].start) < 0) continue; } temp = ipt[i]; ipt[i] = ipt[j]; ipt[j] = temp; } } } static icalcomponent * comp_fb_normalize (icalcomponent *icomp) { icalcomponent *iclone; icalproperty *prop, *p; const gchar *uid, *comment; struct icaltimetype itt; gint fb_count, i = 0, j; struct icalperiodtype *ipt; iclone = icalcomponent_new (ICAL_VFREEBUSY_COMPONENT); prop = icalcomponent_get_first_property (icomp, ICAL_ORGANIZER_PROPERTY); if (prop) { p = icalproperty_new_clone (prop); icalcomponent_add_property (iclone, p); } itt = icalcomponent_get_dtstart (icomp); icalcomponent_set_dtstart (iclone, itt); itt = icalcomponent_get_dtend (icomp); icalcomponent_set_dtend (iclone, itt); fb_count = icalcomponent_count_properties (icomp, ICAL_FREEBUSY_PROPERTY); ipt = g_new0 (struct icalperiodtype, fb_count + 1); for (prop = icalcomponent_get_first_property (icomp, ICAL_FREEBUSY_PROPERTY); prop != NULL; prop = icalcomponent_get_next_property (icomp, ICAL_FREEBUSY_PROPERTY)) { ipt[i] = icalproperty_get_freebusy (prop); i++; } fb_sort (ipt, fb_count); for (j = 0; j <= fb_count - 1; j++) { icalparameter *param; prop = icalproperty_new_freebusy (ipt[j]); param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY); icalproperty_add_parameter (prop, param); icalcomponent_add_property (iclone, prop); } g_free (ipt); /* Should I strip this RFC 2446 says there must not be a UID if the METHOD is PUBLISH?? */ uid = icalcomponent_get_uid (icomp); if (uid) icalcomponent_set_uid (iclone, uid); itt = icaltime_from_timet_with_zone (time (NULL), FALSE, icaltimezone_get_utc_timezone ()); icalcomponent_set_dtstamp (iclone, itt); prop = icalcomponent_get_first_property (icomp, ICAL_URL_PROPERTY); if (prop) { p = icalproperty_new_clone (prop); icalcomponent_add_property (iclone, p); } comment = icalcomponent_get_comment (icomp); if (comment) icalcomponent_set_comment (iclone, comment); for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY); prop != NULL; prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY)) { p = icalproperty_new_clone (prop); icalcomponent_add_property (iclone, p); } return iclone; } gboolean itip_publish_comp (ECalClient *cal_client, gchar *uri, gchar *username, gchar *password, ECalComponent **pub_comp) { icalcomponent *toplevel = NULL, *icalcomp = NULL; icalcomponent *icomp = NULL; SoupSession *session; SoupMessage *msg; SoupURI *real_uri; gchar *ical_string = NULL; toplevel = e_cal_util_new_top_level (); icalcomponent_set_method (toplevel, ICAL_METHOD_PUBLISH); e_cal_component_set_url (*pub_comp, uri); icalcomp = e_cal_component_get_icalcomponent (*pub_comp); icomp = comp_fb_normalize (icalcomp); icalcomponent_add_component (toplevel, icomp); /* Publish the component */ session = soup_session_async_new (); real_uri = soup_uri_new (uri); if (!real_uri || !real_uri->host) { g_warning (G_STRLOC ": Invalid URL: %s", uri); g_object_unref (session); return FALSE; } soup_uri_set_user (real_uri, username); soup_uri_set_password (real_uri, password); /* build the message */ msg = soup_message_new_from_uri (SOUP_METHOD_PUT, real_uri); soup_uri_free (real_uri); if (!msg) { g_warning (G_STRLOC ": Could not build SOAP message"); g_object_unref (session); return FALSE; } soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT); ical_string = icalcomponent_as_ical_string_r (toplevel); soup_message_set_request (msg, "text/calendar", SOUP_MEMORY_TEMPORARY, ical_string, strlen (ical_string)); /* send message to server */ soup_session_send_message (session, msg); if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { g_warning(G_STRLOC ": Could not publish Free/Busy: %d: %s", msg->status_code, msg->reason_phrase); g_object_unref (msg); g_object_unref (session); g_free (ical_string); return FALSE; } g_object_unref (msg); g_object_unref (session); g_free (ical_string); return TRUE; } static gboolean check_time (const struct icaltimetype tmval, gboolean can_null_time) { if (icaltime_is_null_time (tmval)) return can_null_time; return icaltime_is_valid_time (tmval) && tmval.month >= 1 && tmval.month <= 12 && tmval.day >= 1 && tmval.day <= 31 && tmval.hour >= 0 && tmval.hour < 24 && tmval.minute >= 0 && tmval.minute < 60 && tmval.second >= 0 && tmval.second < 60; } /* Returns whether the passed-in icalcomponent is valid or not. * It does some sanity checks on values too. */ gboolean is_icalcomp_valid (icalcomponent *icalcomp) { if (!icalcomp || !icalcomponent_is_valid (icalcomp)) return FALSE; switch (icalcomponent_isa (icalcomp)) { case ICAL_VEVENT_COMPONENT: return check_time (icalcomponent_get_dtstart (icalcomp), FALSE) && check_time (icalcomponent_get_dtend (icalcomp), TRUE); case ICAL_VTODO_COMPONENT: return check_time (icalcomponent_get_dtstart (icalcomp), TRUE) && check_time (icalcomponent_get_due (icalcomp), TRUE); case ICAL_VJOURNAL_COMPONENT: return check_time (icalcomponent_get_dtstart (icalcomp), TRUE) && check_time (icalcomponent_get_dtend (icalcomp), TRUE); default: break; } return TRUE; }