/* * * 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: * Chenthill Palanisamy (pchenthill@novell.com) * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include typedef struct { ECal *ecal; icalcomponent *icalcomp; } ReceiveData; ECalendarView *c_view; void org_gnome_accept(EPlugin *ep, ECalPopupTargetSelect *target); void org_gnome_retract_resend (EPlugin *ep, ECalPopupTargetSelect *target); static void on_accept_meeting (EPopup *ep, EPopupItem *pitem, gpointer data); static void on_accept_meeting_tentative (EPopup *ep, EPopupItem *pitem, gpointer data); static void on_decline_meeting (EPopup *ep, EPopupItem *pitem, gpointer data); static void on_resend_meeting (EPopup *ep, EPopupItem *pitem, gpointer data); static EPopupItem popup_items[] = { { E_POPUP_ITEM, (gchar *) "41.accept", (gchar *) N_("Accept"), on_accept_meeting, NULL, (gchar *) GTK_STOCK_APPLY, 0, E_CAL_POPUP_SELECT_NOTEDITING | E_CAL_POPUP_SELECT_MEETING | E_CAL_POPUP_SELECT_ACCEPTABLE}, { E_POPUP_ITEM, (gchar *) "42.accept", (gchar *) N_("Accept Tentatively"), on_accept_meeting_tentative, NULL, (gchar *) GTK_STOCK_DIALOG_QUESTION, 0, E_CAL_POPUP_SELECT_NOTEDITING | E_CAL_POPUP_SELECT_MEETING | E_CAL_POPUP_SELECT_ACCEPTABLE}, { E_POPUP_ITEM, (gchar *) "43.decline", (gchar *) N_("Decline"), on_decline_meeting, NULL, (gchar *) GTK_STOCK_CANCEL, 0, E_CAL_POPUP_SELECT_NOTEDITING | E_CAL_POPUP_SELECT_MEETING} }; static void popup_free (EPopup *ep, GSList *items, gpointer data) { g_slist_free (items); items = NULL; } void org_gnome_accept (EPlugin *ep, ECalPopupTargetSelect *target) { GSList *menus = NULL; GList *selected; gint i = 0; static gint first = 0; const gchar *uri = NULL; ECalendarView *cal_view = E_CALENDAR_VIEW (target->target.widget); c_view = cal_view; selected = e_calendar_view_get_selected_events (cal_view); if (selected) { ECalendarViewEvent *event = (ECalendarViewEvent *) selected->data; uri = e_cal_get_uri (event->comp_data->client); } else return; if (!uri) return; if (! g_strrstr (uri, "groupwise://")) return; /* for translation*/ if (!first) { popup_items[0].label = _(popup_items[0].label); } first++; for (i = 0; i < sizeof (popup_items) / sizeof (popup_items[0]); i++) menus = g_slist_prepend (menus, &popup_items[i]); e_popup_add_items (target->target.popup, menus, NULL, popup_free, NULL); } static void finalize_receive_data (ReceiveData *r_data) { if (r_data->ecal) { g_object_unref (r_data->ecal); r_data->ecal = NULL; } if (r_data->ecal) { icalcomponent_free (r_data->icalcomp); r_data->icalcomp = NULL; } g_free (r_data); } static gboolean receive_objects (gpointer data) { GError *error = NULL; ReceiveData *r_data = data; icalcomponent_set_method (r_data->icalcomp, ICAL_METHOD_REQUEST); if (!e_cal_receive_objects (r_data->ecal, r_data->icalcomp, &error)) { /* FIXME show an error dialog */ g_error_free (error); } finalize_receive_data (r_data); return TRUE; } static icalproperty * find_attendee (icalcomponent *ical_comp, const gchar *address) { icalproperty *prop; if (address == NULL) return NULL; for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); prop != NULL; prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { icalvalue *value; const gchar *attendee; gchar *text; value = icalproperty_get_value (prop); if (!value) continue; attendee = icalvalue_get_string (value); text = g_strdup (itip_strip_mailto (attendee)); text = g_strstrip (text); if (!g_ascii_strcasecmp (address, text)) { g_free (text); break; } g_free (text); } return prop; } static void change_status (icalcomponent *ical_comp, const gchar *address, icalparameter_partstat status) { icalproperty *prop; prop = find_attendee (ical_comp, address); if (prop) { icalparameter *param; icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER); param = icalparameter_new_partstat (status); icalproperty_add_parameter (prop, param); } else { icalparameter *param; prop = icalproperty_new_attendee (address); icalcomponent_add_property (ical_comp, prop); param = icalparameter_new_role (ICAL_ROLE_OPTPARTICIPANT); icalproperty_add_parameter (prop, param); param = icalparameter_new_partstat (status); icalproperty_add_parameter (prop, param); } } static void process_meeting (ECalendarView *cal_view, icalparameter_partstat status) { GList *selected; icalcomponent *clone; selected = e_calendar_view_get_selected_events (cal_view); if (selected) { ECalendarViewEvent *event = (ECalendarViewEvent *) selected->data; ECalComponent *comp = e_cal_component_new (); ReceiveData *r_data = g_new0 (ReceiveData, 1); gboolean recurring = FALSE; GThread *thread = NULL; GError *error = NULL; gchar *address = NULL; e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); address = itip_get_comp_attendee (comp, event->comp_data->client); if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp)) recurring = TRUE; /* Free comp */ g_object_unref (comp); comp = NULL; clone = icalcomponent_new_clone (event->comp_data->icalcomp); change_status (clone, address, status); r_data->ecal = g_object_ref (event->comp_data->client); r_data->icalcomp = clone; if (recurring) { gint response; const gchar *msg; if (status == ICAL_PARTSTAT_ACCEPTED || status == ICAL_PARTSTAT_TENTATIVE) msg = "org.gnome.evolution.process_meeting:recurrence-accept"; else msg = "org.gnome.evolution.process_meeting:recurrence-decline"; response = e_error_run (NULL, msg, NULL); if (response == GTK_RESPONSE_YES) { icalproperty *prop; const gchar *uid = icalcomponent_get_uid (r_data->icalcomp); prop = icalproperty_new_x ("All"); icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE"); icalcomponent_add_property (r_data->icalcomp, prop); prop = icalproperty_new_x (uid); icalproperty_set_x_name (prop, "X-GW-RECURRENCE-KEY"); icalcomponent_add_property (r_data->icalcomp, prop); } else if (response == GTK_RESPONSE_CANCEL) { finalize_receive_data (r_data); return; } } thread = g_thread_create ((GThreadFunc) receive_objects, r_data , FALSE, &error); if (!thread) { g_warning (G_STRLOC ": %s", error->message); g_error_free (error); } } } /*FIXME the data does not give us the ECalendarView object. we should remove the global c_view variable once we get it from the data*/ static void on_accept_meeting (EPopup *ep, EPopupItem *pitem, gpointer data) { ECalendarView *cal_view = c_view; process_meeting (cal_view, ICAL_PARTSTAT_ACCEPTED); } static void on_accept_meeting_tentative (EPopup *ep, EPopupItem *pitem, gpointer data) { ECalendarView *cal_view = c_view; process_meeting (cal_view, ICAL_PARTSTAT_TENTATIVE); } static void on_decline_meeting (EPopup *ep, EPopupItem *pitem, gpointer data) { ECalendarView *cal_view = c_view; process_meeting (cal_view, ICAL_PARTSTAT_DECLINED); } static gboolean is_meeting_owner (ECalComponent *comp, ECal *client) { ECalComponentOrganizer org; gchar *email = NULL; const gchar *strip = NULL; gboolean ret_val = FALSE; if (!(e_cal_component_has_attendees (comp) && e_cal_get_save_schedules (client))) return ret_val; e_cal_component_get_organizer (comp, &org); strip = itip_strip_mailto (org.value); if (e_cal_get_cal_address (client, &email, NULL) && !g_ascii_strcasecmp (email, strip)) { ret_val = TRUE; } if (!ret_val) ret_val = e_account_list_find(itip_addresses_get(), E_ACCOUNT_FIND_ID_ADDRESS, strip) != NULL; g_free (email); return ret_val; } typedef struct { ECal *client; ECalComponent *comp; CalObjModType mod; } ThreadData; static EPopupItem retract_popup_items[] = { { E_POPUP_ITEM, (gchar *) "49.resend", (gchar *) N_("Rese_nd Meeting..."), on_resend_meeting, NULL, (gchar *) GTK_STOCK_EDIT, 0, E_CAL_POPUP_SELECT_NOTEDITING | E_CAL_POPUP_SELECT_MEETING} }; void org_gnome_retract_resend (EPlugin *ep, ECalPopupTargetSelect *target) { GSList *menus = NULL; GList *selected; gint i = 0; static gint first = 0; const gchar *uri = NULL; ECalendarView *cal_view = E_CALENDAR_VIEW (target->target.widget); ECalComponent *comp = NULL; ECalendarViewEvent *event = NULL; c_view = cal_view; selected = e_calendar_view_get_selected_events (cal_view); if (selected) { event = (ECalendarViewEvent *) selected->data; uri = e_cal_get_uri (event->comp_data->client); } else return; if (!uri) return; if (! g_strrstr (uri, "groupwise://")) return; comp = e_cal_component_new (); e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); if (!is_meeting_owner (comp, event->comp_data->client)) { g_object_unref (comp); return; } /* for translation*/ if (!first) { retract_popup_items[0].label = _(retract_popup_items[0].label); } first++; for (i = 0; i < sizeof (retract_popup_items) / sizeof (retract_popup_items[0]); i++) menus = g_slist_prepend (menus, &retract_popup_items[i]); e_popup_add_items (target->target.popup, menus, NULL, popup_free, NULL); g_object_unref (comp); } static void add_retract_data (ECalComponent *comp, const gchar *retract_comment, CalObjModType mod) { icalcomponent *icalcomp = NULL; icalproperty *icalprop = NULL; icalcomp = e_cal_component_get_icalcomponent (comp); if (retract_comment && *retract_comment) icalprop = icalproperty_new_x (retract_comment); else icalprop = icalproperty_new_x ("0"); icalproperty_set_x_name (icalprop, "X-EVOLUTION-RETRACT-COMMENT"); icalcomponent_add_property (icalcomp, icalprop); if (mod == CALOBJ_MOD_ALL) icalprop = icalproperty_new_x ("All"); else icalprop = icalproperty_new_x ("This"); icalproperty_set_x_name (icalprop, "X-EVOLUTION-RECUR-MOD"); icalcomponent_add_property (icalcomp, icalprop); } static void free_thread_data (ThreadData *data) { if (data == NULL) return; if (data->client) g_object_unref (data->client); if (data->comp) g_object_unref (data->comp); g_free (data); } static gpointer retract_object (gpointer val) { ThreadData *data = val; icalcomponent *icalcomp = NULL, *mod_comp = NULL; GList *users = NULL; gchar *rid = NULL; const gchar *uid; GError *error = NULL; add_retract_data (data->comp, NULL, data->mod); icalcomp = e_cal_component_get_icalcomponent (data->comp); icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL); if (!e_cal_send_objects (data->client, icalcomp, &users, &mod_comp, &error)) { /* FIXME report error */ g_warning ("Unable to retract the meeting \n"); g_clear_error (&error); return GINT_TO_POINTER (1); } if (mod_comp) icalcomponent_free (mod_comp); if (users) { g_list_foreach (users, (GFunc) g_free, NULL); g_list_free (users); } rid = e_cal_component_get_recurid_as_string (data->comp); e_cal_component_get_uid (data->comp, &uid); if (!e_cal_remove_object_with_mod (data->client, uid, rid, data->mod, &error)) { g_warning ("Unable to remove the item \n"); g_clear_error (&error); return GINT_TO_POINTER (1); } g_free (rid); free_thread_data (data); return GINT_TO_POINTER (0); } static void object_created_cb (CompEditor *ce, gpointer data) { GThread *thread = NULL; gint response; GError *error = NULL; gtk_widget_hide (GTK_WIDGET (ce)); response = e_error_run (NULL, "org.gnome.evolution.process_meeting:resend-retract", NULL); if (response == GTK_RESPONSE_NO) { free_thread_data (data); return; } thread = g_thread_create ((GThreadFunc) retract_object, data , FALSE, &error); if (!thread) { g_warning (G_STRLOC ": %s", error->message); g_error_free (error); } } static void on_resend_meeting (EPopup *ep, EPopupItem *pitem, gpointer data) { ECalendarView *cal_view = c_view; GList *selected; selected = e_calendar_view_get_selected_events (cal_view); if (selected) { ECalendarViewEvent *event = (ECalendarViewEvent *) selected->data; ECalComponent *comp = e_cal_component_new (); ECalComponent *new_comp = NULL; gboolean recurring = FALSE; CalObjModType mod = CALOBJ_MOD_THIS; ThreadData *data = NULL; gint response; const gchar *msg; /* inserting the boolean to share the code between resend and retract */ gboolean resend = TRUE; e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp)) recurring = TRUE; if (recurring == TRUE) msg = "org.gnome.evolution.process_meeting:resend-recurrence"; else msg = "org.gnome.evolution.process_meeting:resend"; response = e_error_run (NULL, msg, NULL); if (response == GTK_RESPONSE_YES) { mod = CALOBJ_MOD_ALL; } else if (response == GTK_RESPONSE_CANCEL) { g_object_unref (comp); return; } data = g_new0 (ThreadData, 1); data->client = g_object_ref (event->comp_data->client); data->comp = comp; data->mod = mod; if (resend) { guint flags = 0; gchar *new_uid = NULL; CompEditor *ce; icalcomponent *icalcomp; flags |= COMP_EDITOR_NEW_ITEM; flags |= COMP_EDITOR_MEETING; flags |= COMP_EDITOR_USER_ORG; new_comp = e_cal_component_clone (comp); new_uid = e_cal_component_gen_uid (); e_cal_component_set_recurid (new_comp, NULL); e_cal_component_set_uid (new_comp, new_uid); icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (new_comp)); ce = e_calendar_view_open_event_with_flags (cal_view, data->client, icalcomp, flags); g_signal_connect (ce, "object_created", G_CALLBACK (object_created_cb), data); g_object_unref (new_comp); g_free (new_uid); } } }