%s
" "%s
", primary, secondary); itip_view_set_error (view, error, save_btn); g_free (error); } static gboolean extract_itip_data (ItipPURI *pitip, ItipView *view, gboolean *have_alarms) { EShell *shell; EShellSettings *shell_settings; icalproperty *prop; icalcomponent_kind kind = ICAL_NO_COMPONENT; icalcomponent *tz_comp; icalcompiter tz_iter; icalcomponent *alarm_comp; icalcompiter alarm_iter; ECalComponent *comp; gboolean use_default_reminder; shell = e_shell_get_default (); shell_settings = e_shell_get_shell_settings (shell); if (!pitip->vcalendar) { set_itip_error (view, _("The calendar attached is not valid"), _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."), FALSE); return FALSE; } pitip->top_level = e_cal_util_new_top_level (); pitip->main_comp = icalparser_parse_string (pitip->vcalendar); if (pitip->main_comp == NULL || !is_icalcomp_valid (pitip->main_comp)) { set_itip_error (view, _("The calendar attached is not valid"), _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."), FALSE); if (pitip->main_comp) { icalcomponent_free (pitip->main_comp); pitip->main_comp = NULL; } return FALSE; } prop = icalcomponent_get_first_property (pitip->main_comp, ICAL_METHOD_PROPERTY); if (prop == NULL) { pitip->method = ICAL_METHOD_PUBLISH; } else { pitip->method = icalproperty_get_method (prop); } tz_iter = icalcomponent_begin_component (pitip->main_comp, ICAL_VTIMEZONE_COMPONENT); while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) { icalcomponent *clone; clone = icalcomponent_new_clone (tz_comp); icalcomponent_add_component (pitip->top_level, clone); icalcompiter_next (&tz_iter); } pitip->iter = icalcomponent_begin_component (pitip->main_comp, ICAL_ANY_COMPONENT); pitip->ical_comp = icalcompiter_deref (&pitip->iter); if (pitip->ical_comp != NULL) { kind = icalcomponent_isa (pitip->ical_comp); if (kind != ICAL_VEVENT_COMPONENT && kind != ICAL_VTODO_COMPONENT && kind != ICAL_VFREEBUSY_COMPONENT && kind != ICAL_VJOURNAL_COMPONENT) pitip->ical_comp = get_next (&pitip->iter); } if (pitip->ical_comp == NULL) { set_itip_error (view, _("The item in the calendar is not valid"), _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"), FALSE); return FALSE; } switch (icalcomponent_isa (pitip->ical_comp)) { case ICAL_VEVENT_COMPONENT: pitip->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; pitip->has_organizer = icalcomponent_get_first_property (pitip->ical_comp, ICAL_ORGANIZER_PROPERTY) != NULL; if (icalcomponent_get_first_property (pitip->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) { /* no attendees: assume that that this is not a meeting and organizer doesn't want a reply */ pitip->no_reply_wanted = TRUE; } else { /* * if we have attendees, then find_to_address() will check for our RSVP * and set no_reply_wanted=TRUE if RSVP=FALSE for the current user */ } break; case ICAL_VTODO_COMPONENT: pitip->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; break; case ICAL_VJOURNAL_COMPONENT: pitip->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS; break; default: set_itip_error (view, _("The item in the calendar is not valid"), _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"), FALSE); return FALSE; } pitip->total = icalcomponent_count_components (pitip->main_comp, ICAL_VEVENT_COMPONENT); pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VTODO_COMPONENT); pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VFREEBUSY_COMPONENT); pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VJOURNAL_COMPONENT); if (pitip->total > 1) { set_itip_error (view, _("The calendar attached contains multiple items"), _("To process all of these items, the file should be saved and the calendar imported"), TRUE); } if (pitip->total > 0) { pitip->current = 1; } else { pitip->current = 0; } if (icalcomponent_isa (pitip->ical_comp) != ICAL_VJOURNAL_COMPONENT) { gchar *my_address; prop = NULL; comp = e_cal_component_new (); e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (pitip->ical_comp)); my_address = itip_get_comp_attendee (comp, NULL); g_object_unref (comp); comp = NULL; if (!prop) prop = find_attendee (pitip->ical_comp, my_address); if (!prop) prop = find_attendee_if_sentby (pitip->ical_comp, my_address); if (prop) { icalparameter *param; const gchar * delfrom; if ((param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER))) { delfrom = icalparameter_get_delegatedfrom (param); pitip->delegator_address = g_strdup (itip_strip_mailto (delfrom)); } } g_free (my_address); prop = NULL; /* Determine any delegate sections */ prop = icalcomponent_get_first_property (pitip->ical_comp, ICAL_X_PROPERTY); while (prop) { const gchar *x_name, *x_val; x_name = icalproperty_get_x_name (prop); x_val = icalproperty_get_x (prop); if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-UID")) pitip->calendar_uid = g_strdup (x_val); else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-URI")) g_warning (G_STRLOC ": X-EVOLUTION-DELEGATOR-CALENDAR-URI used"); else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-ADDRESS")) pitip->delegator_address = g_strdup (x_val); else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME")) pitip->delegator_name = g_strdup (x_val); prop = icalcomponent_get_next_property (pitip->ical_comp, ICAL_X_PROPERTY); } /* Strip out procedural alarms for security purposes */ alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT); while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) { icalproperty *p; icalcompiter_next (&alarm_iter); p = icalcomponent_get_first_property (alarm_comp, ICAL_ACTION_PROPERTY); if (!p || icalproperty_get_action (p) == ICAL_ACTION_PROCEDURE) icalcomponent_remove_component (pitip->ical_comp, alarm_comp); icalcomponent_free (alarm_comp); } if (have_alarms) { alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT); *have_alarms = icalcompiter_deref (&alarm_iter) != NULL; } } pitip->comp = e_cal_component_new (); if (!e_cal_component_set_icalcomponent (pitip->comp, pitip->ical_comp)) { g_object_unref (pitip->comp); pitip->comp = NULL; set_itip_error (view, _("The item in the calendar is not valid"), _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"), FALSE); return FALSE; }; /* Add default reminder if the config says so */ use_default_reminder = e_shell_settings_get_boolean ( shell_settings, "cal-use-default-reminder"); if (use_default_reminder) { ECalComponentAlarm *acomp; gint interval; EDurationType units; ECalComponentAlarmTrigger trigger; interval = e_shell_settings_get_int ( shell_settings, "cal-default-reminder-interval"); units = e_shell_settings_get_int ( shell_settings, "cal-default-reminder-units"); acomp = e_cal_component_alarm_new (); e_cal_component_alarm_set_action (acomp, E_CAL_COMPONENT_ALARM_DISPLAY); trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START; memset (&trigger.u.rel_duration, 0, sizeof (trigger.u.rel_duration)); trigger.u.rel_duration.is_neg = TRUE; switch (units) { case E_DURATION_MINUTES: trigger.u.rel_duration.minutes = interval; break; case E_DURATION_HOURS: trigger.u.rel_duration.hours = interval; break; case E_DURATION_DAYS: trigger.u.rel_duration.days = interval; break; default: g_assert_not_reached (); } e_cal_component_alarm_set_trigger (acomp, trigger); e_cal_component_add_alarm (pitip->comp, acomp); e_cal_component_alarm_free (acomp); } find_from_address (pitip, pitip->ical_comp); find_to_address (pitip, pitip->ical_comp, NULL); return TRUE; } struct _opencal_msg { MailMsg base; gchar *command; /* command line to run */ }; static gchar * open_calendar__desc (struct _opencal_msg *m, gint complete) { return g_strdup (_("Opening calendar")); } static void open_calendar__exec (struct _opencal_msg *m, GCancellable *cancellable, GError **error) { if (!g_spawn_command_line_async (m->command, NULL)) { g_warning ("Could not launch %s", m->command); } } static void open_calendar__free (struct _opencal_msg *m) { g_free (m->command); m->command = NULL; } static MailMsgInfo open_calendar_info = { sizeof (struct _opencal_msg), (MailMsgDescFunc) open_calendar__desc, (MailMsgExecFunc) open_calendar__exec, (MailMsgDoneFunc) NULL, (MailMsgFreeFunc) open_calendar__free, }; static gboolean idle_open_cb (gpointer data) { ItipPURI *pitip = data; struct _opencal_msg *m; gchar *start, *end; start = isodate_from_time_t (pitip->start_time); end = isodate_from_time_t (pitip->end_time); m = mail_msg_new (&open_calendar_info); m->command = g_strdup_printf ("evolution \"calendar:///?startdate=%s&enddate=%s\"", start, end); mail_msg_slow_ordered_push (m); g_free (start); g_free (end); return FALSE; } static void view_response_cb (ItipView *view, ItipViewResponse response, gpointer data) { ItipPURI *pitip = data; gboolean status = FALSE; icalproperty *prop; ECalComponentTransparency trans; if (response == ITIP_VIEW_RESPONSE_SAVE) { save_vcalendar_cb (pitip); return; } pitip->can_delete_invitation_from_cache = FALSE; if (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) { if (itip_view_get_free_time_check_state (view)) e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT); else e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE); } else { e_cal_component_get_transparency (pitip->comp, &trans); if (trans == E_CAL_COMPONENT_TRANSP_NONE) e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE); } if (!pitip->to_address && pitip->current_client != NULL) e_client_get_backend_property_sync (E_CLIENT (pitip->current_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &pitip->to_address, NULL, NULL); /* check if it is a recur instance (no master object) and * add a property */ if (itip_view_get_recur_check_state (view)) { prop = icalproperty_new_x ("All"); icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE"); icalcomponent_add_property (pitip->ical_comp, prop); } switch (response) { case ITIP_VIEW_RESPONSE_ACCEPT: if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) status = change_status (pitip->ical_comp, pitip->to_address, ICAL_PARTSTAT_ACCEPTED); else status = TRUE; if (status) { e_cal_component_rescan (pitip->comp); pitip->can_delete_invitation_from_cache = TRUE; update_item (pitip, view, response); } break; case ITIP_VIEW_RESPONSE_TENTATIVE: status = change_status (pitip->ical_comp, pitip->to_address, ICAL_PARTSTAT_TENTATIVE); if (status) { e_cal_component_rescan (pitip->comp); pitip->can_delete_invitation_from_cache = TRUE; update_item (pitip, view, response); } break; case ITIP_VIEW_RESPONSE_DECLINE: if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) status = change_status (pitip->ical_comp, pitip->to_address, ICAL_PARTSTAT_DECLINED); else { prop = icalproperty_new_x ("1"); icalproperty_set_x_name (prop, "X-GW-DECLINED"); icalcomponent_add_property (pitip->ical_comp, prop); status = TRUE; } if (status) { e_cal_component_rescan (pitip->comp); pitip->can_delete_invitation_from_cache = TRUE; update_item (pitip, view, response); } break; case ITIP_VIEW_RESPONSE_UPDATE: update_attendee_status (pitip, view); break; case ITIP_VIEW_RESPONSE_CANCEL: update_item (pitip, view, response); break; case ITIP_VIEW_RESPONSE_REFRESH: send_item (pitip, view); break; case ITIP_VIEW_RESPONSE_OPEN: g_idle_add (idle_open_cb, pitip); return; default: break; } } static gboolean check_is_instance (icalcomponent *icalcomp) { icalproperty *icalprop; icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); while (icalprop) { const gchar *x_name; x_name = icalproperty_get_x_name (icalprop); if (!strcmp (x_name, "X-GW-RECURRENCE-KEY")) { return TRUE; } icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); } return FALSE; } static gboolean in_proper_folder (CamelFolder *folder) { EShell *shell; EShellBackend *shell_backend; EMailBackend *backend; EMailSession *session; MailFolderCache *folder_cache; gboolean res = TRUE; CamelFolderInfoFlags flags = 0; if (!folder) return FALSE; shell = e_shell_get_default (); shell_backend = e_shell_get_backend_by_name (shell, "mail"); backend = E_MAIL_BACKEND (shell_backend); session = e_mail_backend_get_session (backend); folder_cache = e_mail_session_get_folder_cache (session); if (mail_folder_cache_get_folder_info_flags (folder_cache, folder, &flags)) { /* it should be neither trash nor junk folder, */ res = ((flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_TRASH && (flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_JUNK && /* it can be Inbox */ ( (flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX || /* or any other virtual folder */ CAMEL_IS_VEE_FOLDER (folder) || /* or anything else except of sent, outbox or drafts folder */ (!em_utils_folder_is_sent (folder) && !em_utils_folder_is_outbox (folder) && !em_utils_folder_is_drafts (folder)) )); } else { /* cannot check for Inbox folder here */ res = (folder->folder_flags & (CAMEL_FOLDER_IS_TRASH | CAMEL_FOLDER_IS_JUNK)) == 0 && ( (CAMEL_IS_VEE_FOLDER (folder)) || ( !em_utils_folder_is_sent (folder) && !em_utils_folder_is_outbox (folder) && !em_utils_folder_is_drafts (folder))); } return res; } static void init_itip_view (ItipPURI *info, ItipView *view) { EShell *shell; EShellSettings *shell_settings; ECalComponentText text; ECalComponentOrganizer organizer; ECalComponentDateTime datetime; icaltimezone *from_zone, *to_zone; GString *gstring = NULL; GSList *list, *l; icalcomponent *icalcomp; const gchar *string, *org; gint i; gboolean response_enabled; gboolean have_alarms = FALSE; EMFormat *emf = info->puri.emf; shell = e_shell_get_default (); shell_settings = e_shell_get_shell_settings (shell); /* Reset current client before initializing view */ info->current_client = NULL; /* Accounts */ info->accounts = e_get_account_list (); /* Source Lists and open ecal clients */ for (i = 0; i < E_CAL_CLIENT_SOURCE_TYPE_LAST; i++) { if (!e_cal_client_get_sources (&info->source_lists[i], i, NULL)) /* FIXME More error handling? */ info->source_lists[i] = NULL; /* Initialize the ecal hashes */ info->clients[i] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */ if (!extract_itip_data (info, view, &have_alarms)) return; response_enabled = in_proper_folder (emf->folder); if (!response_enabled) { itip_view_set_mode (view, ITIP_VIEW_MODE_HIDE_ALL); } else { itip_view_set_show_inherit_alarm_check ( view, have_alarms && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST)); switch (info->method) { case ICAL_METHOD_PUBLISH: case ICAL_METHOD_REQUEST: /* * Treat meeting request (sent by organizer directly) and * published evend (forwarded by organizer or attendee) alike: * if the event has an organizer, then it can be replied to and * we show the "accept/tentative/decline" choice. * Otherwise only show "accept". */ itip_view_set_mode (view, info->has_organizer ? ITIP_VIEW_MODE_REQUEST : ITIP_VIEW_MODE_PUBLISH); break; case ICAL_METHOD_REPLY: itip_view_set_mode (view, ITIP_VIEW_MODE_REPLY); break; case ICAL_METHOD_ADD: itip_view_set_mode (view, ITIP_VIEW_MODE_ADD); break; case ICAL_METHOD_CANCEL: itip_view_set_mode (view, ITIP_VIEW_MODE_CANCEL); break; case ICAL_METHOD_REFRESH: itip_view_set_mode (view, ITIP_VIEW_MODE_REFRESH); break; case ICAL_METHOD_COUNTER: itip_view_set_mode (view, ITIP_VIEW_MODE_COUNTER); break; case ICAL_METHOD_DECLINECOUNTER: itip_view_set_mode (view, ITIP_VIEW_MODE_DECLINECOUNTER); break; case ICAL_METHOD_X : /* Handle appointment requests from Microsoft Live. This is * a best-at-hand-now handling. Must be revisited when we have * better access to the source of such meetings */ info->method = ICAL_METHOD_REQUEST; itip_view_set_mode (view, ITIP_VIEW_MODE_REQUEST); break; default: return; } } itip_view_set_item_type (view, info->type); if (response_enabled) { switch (info->method) { case ICAL_METHOD_REQUEST: /* FIXME What about the name? */ itip_view_set_delegator (view, info->delegator_name ? info->delegator_name : info->delegator_address); case ICAL_METHOD_PUBLISH: case ICAL_METHOD_ADD: case ICAL_METHOD_CANCEL: case ICAL_METHOD_DECLINECOUNTER: itip_view_set_show_update_check (view, FALSE); /* An organizer sent this */ e_cal_component_get_organizer (info->comp, &organizer); org = organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value); itip_view_set_organizer (view, org); if (organizer.sentby) itip_view_set_organizer_sentby ( view, itip_strip_mailto (organizer.sentby)); if (info->my_address) { if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto (organizer.value), info->my_address)) && !(organizer.sentby && !g_ascii_strcasecmp (itip_strip_mailto (organizer.sentby), info->my_address)) && (info->to_address && g_ascii_strcasecmp (info->to_address, info->my_address))) itip_view_set_proxy (view, info->to_name ? info->to_name : info->to_address); } break; case ICAL_METHOD_REPLY: case ICAL_METHOD_REFRESH: case ICAL_METHOD_COUNTER: itip_view_set_show_update_check (view, TRUE); /* An attendee sent this */ e_cal_component_get_attendee_list (info->comp, &list); if (list != NULL) { ECalComponentAttendee *attendee; attendee = list->data; itip_view_set_attendee (view, attendee->cn ? attendee->cn : itip_strip_mailto (attendee->value)); if (attendee->sentby) itip_view_set_attendee_sentby (view, itip_strip_mailto (attendee->sentby)); if (info->my_address) { if (!(attendee->value && !g_ascii_strcasecmp (itip_strip_mailto (attendee->value), info->my_address)) && !(attendee->sentby && !g_ascii_strcasecmp (itip_strip_mailto (attendee->sentby), info->my_address)) && (info->from_address && g_ascii_strcasecmp (info->from_address, info->my_address))) itip_view_set_proxy (view, info->from_name ? info->from_name : info->from_address); } e_cal_component_free_attendee_list (list); } break; default: g_assert_not_reached (); break; } } e_cal_component_get_summary (info->comp, &text); itip_view_set_summary (view, text.value ? text.value : C_("cal-itip", "None")); e_cal_component_get_location (info->comp, &string); itip_view_set_location (view, string); /* Status really only applies for REPLY */ if (response_enabled && info->method == ICAL_METHOD_REPLY) { e_cal_component_get_attendee_list (info->comp, &list); if (list != NULL) { ECalComponentAttendee *a = list->data; switch (a->status) { case ICAL_PARTSTAT_ACCEPTED: itip_view_set_status (view, _("Accepted")); break; case ICAL_PARTSTAT_TENTATIVE: itip_view_set_status (view, _("Tentatively Accepted")); break; case ICAL_PARTSTAT_DECLINED: itip_view_set_status (view, _("Declined")); break; case ICAL_PARTSTAT_DELEGATED: itip_view_set_status (view, _("Delegated")); break; default: itip_view_set_status (view, _("Unknown")); } } e_cal_component_free_attendee_list (list); } if (info->method == ICAL_METHOD_REPLY || info->method == ICAL_METHOD_COUNTER || info->method == ICAL_METHOD_DECLINECOUNTER) { /* FIXME Check spec to see if multiple comments are actually valid */ /* Comments for iTIP are limited to one per object */ e_cal_component_get_comment_list (info->comp, &list); if (list) { ECalComponentText *text = list->data; if (text->value) { gchar *html; html = camel_text_to_html ( text->value, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0); itip_view_set_comment (view, html); g_free (html); } } e_cal_component_free_text_list (list); } e_cal_component_get_description_list (info->comp, &list); for (l = list; l; l = l->next) { ECalComponentText *text = l->data; if (!gstring && text->value) gstring = g_string_new (text->value); else if (text->value) g_string_append_printf (gstring, "\n\n%s", text->value); } e_cal_component_free_text_list (list); if (gstring) { gchar *html; html = camel_text_to_html ( gstring->str, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_MARK_CITATION | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0); itip_view_set_description (view, html); g_string_free (gstring, TRUE); g_free (html); } to_zone = e_shell_settings_get_pointer (shell_settings, "cal-timezone"); e_cal_component_get_dtstart (info->comp, &datetime); info->start_time = 0; if (datetime.value) { struct tm start_tm; /* If the timezone is not in the component, guess the local time */ /* Should we guess if the timezone is an olsen name somehow? */ if (datetime.value->is_utc) from_zone = icaltimezone_get_utc_timezone (); else if (!datetime.value->is_utc && datetime.tzid) from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid); else from_zone = NULL; start_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone); itip_view_set_start (view, &start_tm, datetime.value->is_date); info->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone); } icalcomp = e_cal_component_get_icalcomponent (info->comp); /* Set the recurrence id */ if (check_is_instance (icalcomp) && datetime.value) { ECalComponentRange *recur_id; struct icaltimetype icaltime = icaltime_convert_to_zone (*datetime.value, to_zone); recur_id = g_new0 (ECalComponentRange, 1); recur_id->type = E_CAL_COMPONENT_RANGE_SINGLE; recur_id->datetime.value = &icaltime; recur_id->datetime.tzid = icaltimezone_get_tzid (to_zone); e_cal_component_set_recurid (info->comp, recur_id); g_free (recur_id); /* it's ok to call g_free here */ } e_cal_component_free_datetime (&datetime); e_cal_component_get_dtend (info->comp, &datetime); info->end_time = 0; if (datetime.value) { struct tm end_tm; /* If the timezone is not in the component, guess the local time */ /* Should we guess if the timezone is an olsen name somehow? */ if (datetime.value->is_utc) from_zone = icaltimezone_get_utc_timezone (); else if (!datetime.value->is_utc && datetime.tzid) from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid); else from_zone = NULL; if (datetime.value->is_date) { /* RFC says the DTEND is not inclusive, thus subtract one day * if we have a date */ icaltime_adjust (datetime.value, -1, 0, 0, 0); } end_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone); itip_view_set_end (view, &end_tm, datetime.value->is_date); info->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone); } e_cal_component_free_datetime (&datetime); /* Recurrence info */ /* FIXME Better recurring description */ if (e_cal_component_has_recurrences (info->comp)) { /* FIXME Tell the user we don't support recurring tasks */ switch (info->type) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This meeting recurs")); break; case E_CAL_CLIENT_SOURCE_TYPE_TASKS: itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This task recurs")); break; case E_CAL_CLIENT_SOURCE_TYPE_MEMOS: itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This memo recurs")); break; default: g_assert_not_reached (); break; } } if (response_enabled) { g_signal_connect ( view, "response", G_CALLBACK (view_response_cb), info); itip_view_set_show_free_time_check (view, info->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST)); if (info->calendar_uid) { start_calendar_server_by_uid (info, view, info->calendar_uid, info->type); } else { find_server (info, view, info->comp); set_buttons_sensitive (info, view); } } } static void puri_free (EMFormatPURI *puri) { ItipPURI *pitip = (ItipPURI *) puri; gint i; g_cancellable_cancel (pitip->cancellable); g_object_unref (pitip->cancellable); for (i = 0; i < E_CAL_CLIENT_SOURCE_TYPE_LAST; i++) { if (pitip->source_lists[i]) g_object_unref (pitip->source_lists[i]); pitip->source_lists[i] = NULL; g_hash_table_destroy (pitip->clients[i]); pitip->clients[i] = NULL; } g_free (pitip->vcalendar); pitip->vcalendar = NULL; if (pitip->comp) { g_object_unref (pitip->comp); pitip->comp = NULL; } if (pitip->top_level) { icalcomponent_free (pitip->top_level); pitip->top_level = NULL; } if (pitip->main_comp) { icalcomponent_free (pitip->main_comp); pitip->main_comp = NULL; } pitip->ical_comp = NULL; g_free (pitip->calendar_uid); pitip->calendar_uid = NULL; g_free (pitip->from_address); pitip->from_address = NULL; g_free (pitip->from_name); pitip->from_name = NULL; g_free (pitip->to_address); pitip->to_address = NULL; g_free (pitip->to_name); pitip->to_name = NULL; g_free (pitip->delegator_address); pitip->delegator_address = NULL; g_free (pitip->delegator_name); pitip->delegator_name = NULL; g_free (pitip->my_address); pitip->my_address = NULL; g_free (pitip->uid); g_hash_table_destroy (pitip->real_comps); } static void write_itip_view (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable) { GString *buffer; if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) { ItipView *view; ItipPURI *ipuri; buffer = g_string_sized_new (1024); ipuri = (ItipPURI *) puri; view = itip_view_new (ipuri); init_itip_view (ipuri, view); itip_view_write_for_printing (view, buffer); /* Destroy the view when the formatter is destroyed */ g_object_weak_ref (G_OBJECT (emf), (GWeakNotify) g_object_unref, view); } else if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { buffer = g_string_sized_new (2048); itip_view_write (buffer); } else { gchar *uri; uri = em_format_build_mail_uri ( emf->folder, emf->message_uid, "part_id", G_TYPE_STRING, puri->uri, "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, NULL); buffer = g_string_sized_new (256); g_string_append_printf (buffer, "