2604 lines
66 KiB
C
2604 lines
66 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>
|
|
*
|
|
*
|
|
* Authors:
|
|
* JP Rosevear <jpr@novell.com>
|
|
*
|
|
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
#include <gtkhtml/gtkhtml.h>
|
|
#include <gtkhtml/gtkhtml-embedded.h>
|
|
#include <libedataserver/e-source-list.h>
|
|
#include <libedataserverui/e-source-combo-box.h>
|
|
#include <libical/ical.h>
|
|
#include <libecal/e-cal-component.h>
|
|
#include <libecal/e-cal-time-util.h>
|
|
#include <libecal/e-cal.h>
|
|
#include <libedataserver/e-time-utils.h>
|
|
#include <e-util/e-dialog-widgets.h>
|
|
#include <e-util/e-html-utils.h>
|
|
#include <e-util/e-icon-factory.h>
|
|
#include <e-util/e-util-private.h>
|
|
#include "dialogs/delete-error.h"
|
|
#include "calendar-config.h"
|
|
#include "itip-utils.h"
|
|
#include "e-itip-control.h"
|
|
#include "common/authentication.h"
|
|
#include "widgets/misc/e-web-view.h"
|
|
#include <shell/e-shell.h>
|
|
|
|
struct _EItipControlPrivate {
|
|
GtkWidget *web_view;
|
|
|
|
ESourceList *source_lists[E_CAL_SOURCE_TYPE_LAST];
|
|
GHashTable *ecals[E_CAL_SOURCE_TYPE_LAST];
|
|
|
|
ECal *current_ecal;
|
|
ECalSourceType type;
|
|
|
|
gchar action;
|
|
gboolean rsvp;
|
|
|
|
/* Use the gpointer variants for weak pointers. */
|
|
union {
|
|
GtkWidget *widget;
|
|
gpointer pointer;
|
|
} ok;
|
|
union {
|
|
GtkWidget *widget;
|
|
gpointer pointer;
|
|
} hbox;
|
|
union {
|
|
GtkWidget *widget;
|
|
gpointer pointer;
|
|
} vbox;
|
|
|
|
gchar *vcalendar;
|
|
ECalComponent *comp;
|
|
icalcomponent *main_comp;
|
|
icalcomponent *ical_comp;
|
|
icalcomponent *top_level;
|
|
icalcompiter iter;
|
|
icalproperty_method method;
|
|
|
|
gint current;
|
|
gint total;
|
|
|
|
gchar *calendar_uid;
|
|
|
|
EAccountList *accounts;
|
|
|
|
gchar *from_address;
|
|
gchar *delegator_address;
|
|
gchar *delegator_name;
|
|
gchar *my_address;
|
|
gint view_only;
|
|
};
|
|
|
|
/* HTML Strings */
|
|
#define HTML_BODY_START "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#336699\">"
|
|
#define HTML_SEP "<hr color=#336699 align=\"left\" width=450>"
|
|
#define HTML_BODY_END "</body>"
|
|
#define HTML_FOOTER "</html>"
|
|
|
|
static void e_itip_control_destroy (GtkObject *obj);
|
|
|
|
static void find_my_address (EItipControl *itip, icalcomponent *ical_comp, icalparameter_partstat *status);
|
|
static gboolean object_requested_cb (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data);
|
|
static void ok_clicked_cb (GtkWidget *widget, gpointer data);
|
|
|
|
G_DEFINE_TYPE (EItipControl, e_itip_control, GTK_TYPE_VBOX)
|
|
|
|
static void
|
|
e_itip_control_class_init (EItipControlClass *klass)
|
|
{
|
|
GtkObjectClass *gtkobject_class;
|
|
|
|
gtkobject_class = GTK_OBJECT_CLASS (klass);
|
|
|
|
gtkobject_class->destroy = e_itip_control_destroy;
|
|
}
|
|
|
|
static void
|
|
set_ok_sens (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
gboolean read_only = TRUE;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (!priv->ok.widget)
|
|
return;
|
|
|
|
if (priv->current_ecal)
|
|
e_cal_is_read_only (priv->current_ecal, &read_only, NULL);
|
|
|
|
gtk_widget_set_sensitive (priv->ok.widget, priv->current_ecal != NULL && !read_only);
|
|
}
|
|
|
|
static void
|
|
cal_opened_cb (ECal *ecal, const GError *error, gpointer data)
|
|
{
|
|
EItipControl *itip = data;
|
|
EItipControlPrivate *priv;
|
|
ESource *source;
|
|
ECalSourceType source_type;
|
|
|
|
priv = itip->priv;
|
|
|
|
source_type = e_cal_get_source_type (ecal);
|
|
source = e_cal_get_source (ecal);
|
|
|
|
g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, cal_opened_cb, NULL);
|
|
|
|
if (error) {
|
|
g_hash_table_remove (priv->ecals[source_type], e_source_peek_uid (source));
|
|
|
|
return;
|
|
}
|
|
|
|
priv->current_ecal = ecal;
|
|
set_ok_sens (itip);
|
|
}
|
|
|
|
typedef void (* EItipControlOpenFunc) (ECal *ecal, const GError *error, gpointer data);
|
|
|
|
static ECal *
|
|
start_calendar_server (EItipControl *itip, ESource *source, ECalSourceType type, EItipControlOpenFunc func, gpointer data)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
ECal *ecal;
|
|
icaltimezone *zone;
|
|
|
|
priv = itip->priv;
|
|
|
|
ecal = g_hash_table_lookup (priv->ecals[type], e_source_peek_uid (source));
|
|
if (ecal) {
|
|
priv->current_ecal = ecal;
|
|
set_ok_sens (itip);
|
|
return ecal;
|
|
}
|
|
|
|
ecal = e_auth_new_cal_from_source (source, type);
|
|
|
|
zone = calendar_config_get_icaltimezone ();
|
|
e_cal_set_default_timezone (ecal, zone, NULL);
|
|
|
|
g_signal_connect (G_OBJECT (ecal), "cal_opened_ex", G_CALLBACK (func), data);
|
|
|
|
g_hash_table_insert (priv->ecals[type], g_strdup (e_source_peek_uid (source)), ecal);
|
|
|
|
e_cal_open_async (ecal, TRUE);
|
|
|
|
return ecal;
|
|
}
|
|
|
|
static ECal *
|
|
start_calendar_server_by_uid (EItipControl *itip, const gchar *uid, ECalSourceType type)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
gint i;
|
|
|
|
priv = itip->priv;
|
|
|
|
for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) {
|
|
ESource *source;
|
|
|
|
source = e_source_list_peek_source_by_uid (priv->source_lists[i], uid);
|
|
if (source)
|
|
return start_calendar_server (itip, source, type, cal_opened_cb, itip);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedef struct {
|
|
EItipControl *itip;
|
|
gchar *uid;
|
|
gint count;
|
|
gboolean show_selector;
|
|
} EItipControlFindData;
|
|
|
|
static void
|
|
source_changed_cb (ESourceComboBox *escb, EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv = itip->priv;
|
|
ESource *source;
|
|
|
|
source = e_source_combo_box_get_active (escb);
|
|
|
|
if (priv->ok.widget)
|
|
gtk_widget_set_sensitive (priv->ok.widget, FALSE);
|
|
|
|
start_calendar_server (itip, source, priv->type, cal_opened_cb, itip);
|
|
}
|
|
|
|
static void
|
|
find_cal_opened_cb (ECal *ecal, const GError *error, gpointer data)
|
|
{
|
|
EShell *shell;
|
|
EShellSettings *shell_settings;
|
|
EItipControlFindData *fd = data;
|
|
EItipControlPrivate *priv;
|
|
ESource *source;
|
|
ECalSourceType source_type;
|
|
icalcomponent *icalcomp;
|
|
|
|
/* FIXME Pass this in. */
|
|
shell = e_shell_get_default ();
|
|
shell_settings = e_shell_get_shell_settings (shell);
|
|
|
|
source_type = e_cal_get_source_type (ecal);
|
|
source = e_cal_get_source (ecal);
|
|
|
|
priv = fd->itip->priv;
|
|
|
|
fd->count--;
|
|
|
|
g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, find_cal_opened_cb, NULL);
|
|
|
|
if (error) {
|
|
g_hash_table_remove (priv->ecals[source_type], e_source_peek_uid (source));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (e_cal_get_object (ecal, fd->uid, NULL, &icalcomp, NULL)) {
|
|
icalcomponent_free (icalcomp);
|
|
|
|
priv->current_ecal = ecal;
|
|
set_ok_sens (fd->itip);
|
|
}
|
|
|
|
cleanup:
|
|
if (fd->count == 0) {
|
|
if (fd->show_selector && !priv->current_ecal && priv->vbox.widget) {
|
|
GtkWidget *escb;
|
|
const gchar *property_name;
|
|
gchar *uid;
|
|
|
|
switch (priv->type) {
|
|
case E_CAL_SOURCE_TYPE_EVENT:
|
|
property_name = "cal-primary-calendar";
|
|
break;
|
|
case E_CAL_SOURCE_TYPE_TODO:
|
|
property_name = "cal-primary-tasks";
|
|
break;
|
|
default:
|
|
uid = NULL;
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
uid = e_shell_settings_get_string (
|
|
shell_settings, property_name);
|
|
|
|
if (uid) {
|
|
source = e_source_list_peek_source_by_uid (priv->source_lists[priv->type], uid);
|
|
g_free (uid);
|
|
}
|
|
|
|
/* Try to create a default if there isn't one */
|
|
if (!source)
|
|
source = e_source_list_peek_source_any (priv->source_lists[priv->type]);
|
|
|
|
escb = e_source_combo_box_new (priv->source_lists[priv->type]);
|
|
g_signal_connect_object (
|
|
escb, "changed",
|
|
G_CALLBACK (source_changed_cb), fd->itip, 0);
|
|
|
|
gtk_box_pack_start (GTK_BOX (priv->vbox.widget), escb, FALSE, TRUE, 0);
|
|
gtk_widget_show (escb);
|
|
|
|
/* FIXME What if there is no source? */
|
|
if (source)
|
|
e_source_combo_box_set_active (E_SOURCE_COMBO_BOX (escb), source);
|
|
} else {
|
|
/* FIXME Display error message to user */
|
|
}
|
|
|
|
g_free (fd->uid);
|
|
g_free (fd);
|
|
}
|
|
}
|
|
|
|
static void
|
|
find_server (EItipControl *itip, ECalComponent *comp, gboolean show_selector)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
EItipControlFindData *fd = NULL;
|
|
GSList *groups, *l;
|
|
const gchar *uid;
|
|
|
|
priv = itip->priv;
|
|
|
|
e_cal_component_get_uid (comp, &uid);
|
|
|
|
groups = e_source_list_peek_groups (priv->source_lists[priv->type]);
|
|
for (l = groups; l; l = l->next) {
|
|
ESourceGroup *group;
|
|
GSList *sources, *m;
|
|
|
|
group = l->data;
|
|
|
|
sources = e_source_group_peek_sources (group);
|
|
for (m = sources; m; m = m->next) {
|
|
ESource *source;
|
|
|
|
source = m->data;
|
|
|
|
if (!fd) {
|
|
fd = g_new0 (EItipControlFindData, 1);
|
|
fd->itip = itip;
|
|
fd->uid = g_strdup (uid);
|
|
fd->show_selector = show_selector;
|
|
}
|
|
fd->count++;
|
|
/* Check this return too? */
|
|
start_calendar_server (itip, source, priv->type, find_cal_opened_cb, fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
cleanup_ecal (gpointer data)
|
|
{
|
|
ECal *ecal = data;
|
|
|
|
/* Clean up any signals */
|
|
g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, cal_opened_cb, NULL);
|
|
g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, find_cal_opened_cb, NULL);
|
|
|
|
g_object_unref (ecal);
|
|
}
|
|
|
|
static void
|
|
html_destroyed (gpointer data)
|
|
{
|
|
EItipControl *itip = data;
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
priv->web_view = NULL;
|
|
}
|
|
|
|
static void
|
|
e_itip_control_init (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *scrolled_window;
|
|
gint i;
|
|
|
|
priv = g_new0 (EItipControlPrivate, 1);
|
|
itip->priv = priv;
|
|
|
|
/* Addresses */
|
|
priv->accounts = itip_addresses_get ();
|
|
|
|
/* Source Lists */
|
|
for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++)
|
|
priv->source_lists[i] = NULL;
|
|
|
|
priv->source_lists[E_CAL_SOURCE_TYPE_EVENT] = e_source_list_new_for_gconf_default ("/apps/evolution/calendar/sources");
|
|
priv->source_lists[E_CAL_SOURCE_TYPE_TODO] = e_source_list_new_for_gconf_default ("/apps/evolution/tasks/sources");
|
|
|
|
/* Initialize the ecal hashes */
|
|
for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++)
|
|
priv->ecals[i] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, cleanup_ecal);
|
|
priv->current_ecal = NULL;
|
|
|
|
/* Other fields to init */
|
|
priv->calendar_uid = NULL;
|
|
priv->from_address = NULL;
|
|
priv->delegator_address = NULL;
|
|
priv->delegator_name = NULL;
|
|
priv->my_address = NULL;
|
|
priv->view_only = 0;
|
|
|
|
/* Html Widget */
|
|
priv->web_view = e_web_view_new ();
|
|
gtk_widget_show (priv->web_view);
|
|
|
|
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
gtk_widget_show (scrolled_window);
|
|
|
|
gtk_container_add (GTK_CONTAINER (scrolled_window), priv->web_view);
|
|
g_object_weak_ref (G_OBJECT (priv->web_view), (GWeakNotify)html_destroyed, itip);
|
|
gtk_widget_set_size_request (scrolled_window, 600, 400);
|
|
gtk_box_pack_start (GTK_BOX (itip), scrolled_window, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect (
|
|
priv->web_view, "object-requested",
|
|
G_CALLBACK (object_requested_cb), itip);
|
|
g_signal_connect (
|
|
priv->web_view, "submit",
|
|
G_CALLBACK (ok_clicked_cb), itip);
|
|
}
|
|
|
|
static void
|
|
clean_up (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
if (!priv)
|
|
return;
|
|
|
|
g_free (priv->vcalendar);
|
|
priv->vcalendar = NULL;
|
|
|
|
if (priv->comp) {
|
|
g_object_unref (priv->comp);
|
|
priv->comp = NULL;
|
|
}
|
|
|
|
if (priv->top_level) {
|
|
icalcomponent_free (priv->top_level);
|
|
priv->top_level = NULL;
|
|
}
|
|
|
|
if (priv->main_comp) {
|
|
icalcomponent_free (priv->main_comp);
|
|
priv->main_comp = NULL;
|
|
}
|
|
priv->ical_comp = NULL;
|
|
|
|
priv->current = 0;
|
|
priv->total = 0;
|
|
|
|
g_free (priv->calendar_uid);
|
|
priv->calendar_uid = NULL;
|
|
|
|
g_free (priv->from_address);
|
|
priv->from_address = NULL;
|
|
g_free (priv->delegator_address);
|
|
priv->delegator_address = NULL;
|
|
g_free (priv->delegator_name);
|
|
priv->delegator_name = NULL;
|
|
g_free (priv->my_address);
|
|
priv->my_address = NULL;
|
|
}
|
|
|
|
static void
|
|
e_itip_control_destroy (GtkObject *obj)
|
|
{
|
|
EItipControl *itip = E_ITIP_CONTROL (obj);
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (priv) {
|
|
gint i;
|
|
|
|
clean_up (itip);
|
|
|
|
priv->accounts = NULL;
|
|
|
|
for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) {
|
|
if (priv->ecals[i]) {
|
|
g_hash_table_destroy (priv->ecals[i]);
|
|
priv->ecals[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if (priv->web_view) {
|
|
g_signal_handlers_disconnect_matched (priv->web_view, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, itip);
|
|
g_object_weak_unref (G_OBJECT (priv->web_view), (GWeakNotify)html_destroyed, itip);
|
|
}
|
|
|
|
g_free (priv);
|
|
itip->priv = NULL;
|
|
}
|
|
|
|
(* GTK_OBJECT_CLASS (e_itip_control_parent_class)->destroy) (obj);
|
|
}
|
|
|
|
GtkWidget *
|
|
e_itip_control_new (void)
|
|
{
|
|
return g_object_new (E_TYPE_ITIP_CONTROL, NULL);
|
|
}
|
|
|
|
static void
|
|
find_my_address (EItipControl *itip, icalcomponent *ical_comp, icalparameter_partstat *status)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
icalproperty *prop;
|
|
gchar *my_alt_address = NULL;
|
|
|
|
priv = itip->priv;
|
|
|
|
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;
|
|
icalparameter *param;
|
|
const gchar *attendee, *name;
|
|
gchar *attendee_clean, *name_clean;
|
|
EIterator *it;
|
|
|
|
value = icalproperty_get_value (prop);
|
|
if (value != NULL) {
|
|
attendee = icalvalue_get_string (value);
|
|
attendee_clean = g_strdup (itip_strip_mailto (attendee));
|
|
attendee_clean = g_strstrip (attendee_clean);
|
|
} else {
|
|
attendee = NULL;
|
|
attendee_clean = NULL;
|
|
}
|
|
|
|
param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
|
|
if (param != NULL) {
|
|
name = icalparameter_get_cn (param);
|
|
name_clean = g_strdup (name);
|
|
name_clean = g_strstrip (name_clean);
|
|
} else {
|
|
name = NULL;
|
|
name_clean = NULL;
|
|
}
|
|
|
|
if (priv->delegator_address) {
|
|
gchar *delegator_clean;
|
|
|
|
delegator_clean = g_strdup (itip_strip_mailto (attendee));
|
|
delegator_clean = g_strstrip (delegator_clean);
|
|
|
|
/* If the mailer told us the address to use, use that */
|
|
if (delegator_clean != NULL
|
|
&& !g_ascii_strcasecmp (attendee_clean, delegator_clean)) {
|
|
priv->my_address = g_strdup (itip_strip_mailto (priv->delegator_address));
|
|
priv->my_address = g_strstrip (priv->my_address);
|
|
|
|
if (status) {
|
|
param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
|
|
*status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
|
|
}
|
|
}
|
|
|
|
g_free (delegator_clean);
|
|
} else {
|
|
it = e_list_get_iterator((EList *)priv->accounts);
|
|
while (e_iterator_is_valid(it)) {
|
|
const EAccount *account = e_iterator_get(it);
|
|
|
|
/* Check for a matching address */
|
|
if (attendee_clean != NULL
|
|
&& !g_ascii_strcasecmp (account->id->address, attendee_clean)) {
|
|
priv->my_address = g_strdup (account->id->address);
|
|
if (status) {
|
|
param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
|
|
*status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
|
|
}
|
|
g_free (attendee_clean);
|
|
g_free (name_clean);
|
|
g_free (my_alt_address);
|
|
g_object_unref(it);
|
|
return;
|
|
}
|
|
|
|
/* Check for a matching cname to fall back on */
|
|
if (name_clean != NULL
|
|
&& !g_ascii_strcasecmp (account->id->name, name_clean))
|
|
my_alt_address = g_strdup (attendee_clean);
|
|
|
|
e_iterator_next(it);
|
|
}
|
|
g_object_unref(it);
|
|
}
|
|
|
|
g_free (attendee_clean);
|
|
g_free (name_clean);
|
|
}
|
|
|
|
priv->my_address = my_alt_address;
|
|
if (status)
|
|
*status = ICAL_PARTSTAT_NEEDSACTION;
|
|
}
|
|
|
|
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
|
|
write_label_piece (EItipControl *itip, ECalComponentDateTime *dt,
|
|
GString *buffer,
|
|
const gchar *stext, const gchar *etext,
|
|
gboolean just_date)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
struct tm tmp_tm;
|
|
gchar time_buf[64];
|
|
icaltimezone *zone = NULL;
|
|
const gchar *display_name;
|
|
|
|
priv = itip->priv;
|
|
|
|
/* UTC times get converted to the current timezone. This is done for
|
|
the COMPLETED property, which is always in UTC, and also because
|
|
Outlook sends simple events as UTC times. */
|
|
if (dt->value->is_utc) {
|
|
zone = calendar_config_get_icaltimezone ();
|
|
icaltimezone_convert_time (dt->value, icaltimezone_get_utc_timezone (), zone);
|
|
}
|
|
|
|
tmp_tm = icaltimetype_to_tm (dt->value);
|
|
if (just_date)
|
|
tmp_tm.tm_hour = tmp_tm.tm_min = tmp_tm.tm_sec = 0;
|
|
|
|
if (stext != NULL)
|
|
g_string_append (buffer, stext);
|
|
|
|
e_time_format_date_and_time (&tmp_tm,
|
|
calendar_config_get_24_hour_format (),
|
|
FALSE, FALSE,
|
|
time_buf, sizeof (time_buf));
|
|
g_string_append (buffer, time_buf);
|
|
|
|
if (!dt->value->is_utc && dt->tzid) {
|
|
zone = icalcomponent_get_timezone (priv->top_level, dt->tzid);
|
|
}
|
|
|
|
/* Output timezone after time, e.g. " America/New_York". */
|
|
if (zone && !just_date) {
|
|
/* Note that this returns UTF-8, since all iCalendar data is
|
|
UTF-8. But it probably is not translated. */
|
|
display_name = icaltimezone_get_display_name (zone);
|
|
if (display_name && *display_name) {
|
|
g_string_append_len (buffer, " <font size=-1>[", 16);
|
|
|
|
/* We check if it is one of our builtin timezone names,
|
|
in which case we call gettext to translate it. */
|
|
if (icaltimezone_get_builtin_timezone (display_name)) {
|
|
g_string_append_printf (buffer, "%s", _(display_name));
|
|
} else {
|
|
g_string_append_printf (buffer, "%s", display_name);
|
|
}
|
|
g_string_append_len (buffer, "]</font>", 8);
|
|
}
|
|
}
|
|
|
|
if (etext != NULL)
|
|
g_string_append (buffer, etext);
|
|
}
|
|
|
|
static const gchar *
|
|
nth (gint n)
|
|
{
|
|
if (n == -1)
|
|
return "last";
|
|
else if (n < 1 || n > 31)
|
|
return "?";
|
|
else
|
|
return e_cal_recur_nth[n];
|
|
}
|
|
|
|
static const gchar *dayname[] = {
|
|
N_("Sunday"),
|
|
N_("Monday"),
|
|
N_("Tuesday"),
|
|
N_("Wednesday"),
|
|
N_("Thursday"),
|
|
N_("Friday"),
|
|
N_("Saturday")
|
|
};
|
|
|
|
static const gchar *
|
|
get_dayname (struct icalrecurrencetype *r, gint i)
|
|
{
|
|
enum icalrecurrencetype_weekday day;
|
|
|
|
day = icalrecurrencetype_day_day_of_week (r->by_day[i]);
|
|
g_return_val_if_fail (day > 0 && day < 8, "?");
|
|
|
|
return _(dayname[day - 1]);
|
|
}
|
|
|
|
static void
|
|
write_recurrence_piece (EItipControl *itip, ECalComponent *comp,
|
|
GString *buffer)
|
|
{
|
|
GSList *rrules;
|
|
struct icalrecurrencetype *r;
|
|
gint i;
|
|
|
|
g_string_append_printf (buffer, "<b>%s</b> ", _("Recurring:"));
|
|
|
|
if (!e_cal_component_has_simple_recurrence (comp)) {
|
|
g_string_append_printf (
|
|
buffer, "%s", _("Yes. (Complex Recurrence)"));
|
|
return;
|
|
}
|
|
|
|
e_cal_component_get_rrule_list (comp, &rrules);
|
|
g_return_if_fail (rrules && !rrules->next);
|
|
|
|
r = rrules->data;
|
|
|
|
switch (r->freq) {
|
|
case ICAL_DAILY_RECURRENCE:
|
|
/* For Translators: In this can also be translated as "With the period of %d
|
|
day/days", where %d is a number. The entire sentence is of the form "Recurring:
|
|
Every %d day/days" */
|
|
/* For Translators : 'Every day' is event Recurring every day */
|
|
/* For Translators : 'Every %d days' is event Recurring every %d days. %d is a digit */
|
|
g_string_append_printf (
|
|
buffer, ngettext ("Every day",
|
|
"Every %d days", r->interval),
|
|
r->interval);
|
|
break;
|
|
|
|
case ICAL_WEEKLY_RECURRENCE:
|
|
if (r->by_day[0] == ICAL_RECURRENCE_ARRAY_MAX) {
|
|
/* For Translators: In this can also be translated as "With the period of %d
|
|
week/weeks", where %d is a number. The entire sentence is of the form "Recurring:
|
|
Every %d week/weeks" */
|
|
/* For Translators : 'Every week' is event Recurring every week */
|
|
/* For Translators : 'Every %d weeks' is event Recurring every %d weeks. %d is a digit */
|
|
g_string_append_printf (
|
|
buffer, ngettext ("Every week",
|
|
"Every %d weeks", r->interval),
|
|
r->interval);
|
|
} else {
|
|
/* For Translators : 'Every week on' is event Recurring every week on (dayname) and (dayname) and (dayname) */
|
|
/* For Translators : 'Every %d weeks on' is event Recurring: every %d weeks on (dayname) and (dayname). %d is a digit */
|
|
g_string_append_printf (
|
|
buffer, ngettext ("Every week on ",
|
|
"Every %d weeks on ", r->interval),
|
|
r->interval);
|
|
|
|
for (i = 1; i < 8 && r->by_day[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
|
|
if (i > 1)
|
|
g_string_append_len (buffer, ", ", 2);
|
|
g_string_append (buffer, get_dayname (r, i - 1));
|
|
}
|
|
if (i > 1)
|
|
/* For Translators : 'and' is part of the sentence 'event recurring every week on (dayname) and (dayname)' */
|
|
g_string_append_printf (buffer, "%s", _(" and "));
|
|
g_string_append (buffer, get_dayname (r, i - 1));
|
|
}
|
|
break;
|
|
|
|
case ICAL_MONTHLY_RECURRENCE:
|
|
if (r->by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX) {
|
|
/* For Translators : 'The %s day of' is part of the sentence 'event recurring on the (nth) day of every month.' */
|
|
g_string_append_printf (
|
|
buffer, _("The %s day of "),
|
|
nth (r->by_month_day[0]));
|
|
} else {
|
|
gint pos;
|
|
|
|
/* Outlook 2000 uses BYDAY=TU;BYSETPOS=2, and will not
|
|
accept BYDAY=2TU. So we now use the same as Outlook
|
|
by default. */
|
|
|
|
pos = icalrecurrencetype_day_position (r->by_day[0]);
|
|
if (pos == 0)
|
|
pos = r->by_set_pos[0];
|
|
|
|
/* For Translators : 'The %s %s of' is part of the sentence 'event recurring on the (nth) (dayname) of every month.'
|
|
eg,third monday of every month */
|
|
g_string_append_printf (
|
|
buffer, _("The %s %s of "),
|
|
nth (pos), get_dayname (r, 0));
|
|
}
|
|
|
|
/* For Translators: In this can also be translated as "With the period of %d
|
|
month/months", where %d is a number. The entire sentence is of the form "Recurring:
|
|
Every %d month/months" */
|
|
/* For Translators : 'every month' is part of the sentence 'event recurring on the (nth) day of every month.' */
|
|
/* For Translators : 'every %d months' is part of the sentence 'event recurring on the (nth) day of every %d months.'
|
|
%d is a digit */
|
|
g_string_append_printf (
|
|
buffer, ngettext ("every month",
|
|
"every %d months", r->interval),
|
|
r->interval);
|
|
break;
|
|
|
|
case ICAL_YEARLY_RECURRENCE:
|
|
/* For Translators: In this can also be translated as "With the period of %d
|
|
year/years", where %d is a number. The entire sentence is of the form "Recurring:
|
|
Every %d year/years" */
|
|
/* For Translators : 'Every year' is event Recurring every year */
|
|
/* For Translators : 'Every %d years' is event Recurring every %d years. %d is a digit */
|
|
g_string_append_printf (
|
|
buffer, ngettext ("Every year",
|
|
"Every %d years", r->interval),
|
|
r->interval);
|
|
break;
|
|
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
if (r->count) {
|
|
/* For Translators:'a total of %d time' is part of the sentence of the form 'event recurring every day,a total of % time.' %d is a digit*/
|
|
/* For Translators:'a total of %d times' is part of the sentence of the form 'event recurring every day,a total of % times.' %d is a digit*/
|
|
g_string_append_printf (
|
|
buffer, ngettext ("a total of %d time",
|
|
"a total of %d times", r->count), r->count);
|
|
} else if (!icaltime_is_null_time (r->until)) {
|
|
ECalComponentDateTime dt;
|
|
|
|
/* FIXME This should get the tzid id, not the whole zone */
|
|
dt.value = &r->until;
|
|
dt.tzid = icaltimezone_get_tzid ((icaltimezone *)r->until.zone);
|
|
|
|
write_label_piece (itip, &dt, buffer,
|
|
/* For Translators : ', ending on' is part of the sentence of the form 'event recurring every day, ending on (date).'*/
|
|
_(", ending on "), NULL, TRUE);
|
|
}
|
|
|
|
g_string_append_len (buffer, "<br>", 4);
|
|
}
|
|
|
|
static void
|
|
set_date_label (EItipControl *itip,
|
|
GString *buffer,
|
|
ECalComponent *comp)
|
|
{
|
|
ECalComponentDateTime datetime;
|
|
gchar *str;
|
|
gboolean wrote = FALSE, task_completed = FALSE;
|
|
ECalComponentVType type;
|
|
|
|
type = e_cal_component_get_vtype (comp);
|
|
|
|
e_cal_component_get_dtstart (comp, &datetime);
|
|
if (datetime.value) {
|
|
/* For Translators : 'Starts' is part of "Starts: date", showing when the event starts */
|
|
str = g_strdup_printf ("<b>%s:</b>", _("Starts"));
|
|
write_label_piece (itip, &datetime, buffer, str, "<br>", FALSE);
|
|
wrote = TRUE;
|
|
g_free (str);
|
|
}
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
e_cal_component_get_dtend (comp, &datetime);
|
|
if (datetime.value) {
|
|
/* For Translators : 'Ends' is part of "Ends: date", showing when the event ends */
|
|
str = g_strdup_printf ("<b>%s:</b>", _("Ends"));
|
|
write_label_piece (itip, &datetime, buffer, str, "<br>", FALSE);
|
|
wrote = TRUE;
|
|
g_free (str);
|
|
}
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
if (e_cal_component_has_recurrences (comp)) {
|
|
write_recurrence_piece (itip, comp, buffer);
|
|
wrote = TRUE;
|
|
}
|
|
|
|
datetime.tzid = NULL;
|
|
e_cal_component_get_completed (comp, &datetime.value);
|
|
if (type == E_CAL_COMPONENT_TODO && datetime.value) {
|
|
str = g_strdup_printf ("<b>%s:</b>", _("Completed"));
|
|
/* Pass TRUE as is_utc, so it gets converted to the current timezone. */
|
|
datetime.value->is_utc = TRUE;
|
|
write_label_piece (itip, &datetime, buffer, str, "<br>", FALSE);
|
|
wrote = TRUE;
|
|
task_completed = TRUE;
|
|
g_free (str);
|
|
}
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
e_cal_component_get_due (comp, &datetime);
|
|
if (type == E_CAL_COMPONENT_TODO && !task_completed && datetime.value) {
|
|
str = g_strdup_printf ("<b>%s:</b>", _("Due"));
|
|
write_label_piece (itip, &datetime, buffer, str, "<br>", FALSE);
|
|
wrote = TRUE;
|
|
g_free (str);
|
|
}
|
|
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
if (wrote)
|
|
g_string_append (buffer, "<br>");
|
|
}
|
|
|
|
static void
|
|
set_message (GString *buffer,
|
|
const gchar *message,
|
|
gboolean err)
|
|
{
|
|
if (message == NULL)
|
|
return;
|
|
|
|
if (err) {
|
|
g_string_append_printf (
|
|
buffer, "<b><font color=\"#ff0000\">%s</font></b>"
|
|
"<br><br>", message);
|
|
} else {
|
|
g_string_append_printf (
|
|
buffer, "<b>%s</b><br><br>", message);
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_error_html (EItipControl *itip, const gchar *itip_err)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GString *buffer;
|
|
gchar *filename;
|
|
|
|
priv = itip->priv;
|
|
|
|
buffer = g_string_sized_new (1024);
|
|
|
|
g_string_append_printf (
|
|
buffer, "<html><head><title>%s</title></head>",
|
|
_("iCalendar Information"));
|
|
|
|
g_string_append (buffer, HTML_BODY_START);
|
|
|
|
/* The table */
|
|
g_string_append (
|
|
buffer, "<table width=450 cellspacing=\"0\" "
|
|
"cellpadding=\"4\" border=\"0\">");
|
|
/* The column for the image */
|
|
g_string_append (
|
|
buffer, "<tr><td width=48 align=\"center\" "
|
|
"valign=\"top\" rowspan=\"8\">");
|
|
/* The image */
|
|
filename = e_icon_factory_get_icon_filename (
|
|
"stock_new-meeting", GTK_ICON_SIZE_DIALOG);
|
|
g_string_append_printf (
|
|
buffer, "<img src=\"%s\"></td>", filename);
|
|
g_free (filename);
|
|
|
|
g_string_append (buffer, "<td align=\"left\" valign=\"top\">");
|
|
|
|
/* Title */
|
|
set_message (buffer, _("iCalendar Error"), TRUE);
|
|
|
|
/* Error */
|
|
g_string_append_printf (buffer, "%s", itip_err);
|
|
|
|
/* Clean up */
|
|
g_string_append (buffer, "</td></tr></table>");
|
|
|
|
g_string_append (buffer, HTML_BODY_END);
|
|
g_string_append (buffer, HTML_FOOTER);
|
|
|
|
e_web_view_load_string (E_WEB_VIEW (priv->web_view), buffer->str);
|
|
|
|
g_string_free (buffer, TRUE);
|
|
}
|
|
|
|
static gchar *
|
|
dupe_first_bold (const gchar *format, const gchar *organizer, const gchar *delegator)
|
|
{
|
|
gchar *tmp, *res;
|
|
|
|
tmp = g_strconcat ("<b>", organizer ? organizer : "", "</b>", NULL);
|
|
res = g_strdup_printf (format, tmp, delegator ? delegator : "");
|
|
g_free (tmp);
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
write_html (EItipControl *itip, const gchar *itip_desc, const gchar *itip_title, const gchar *options)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
ECalComponentText text;
|
|
ECalComponentOrganizer organizer;
|
|
ECalComponentAttendee *attendee;
|
|
GSList *attendees, *l = NULL;
|
|
GString *buffer;
|
|
const gchar *string;
|
|
gchar *html;
|
|
gchar *filename;
|
|
gchar *str;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (priv->web_view == NULL)
|
|
return;
|
|
|
|
buffer = g_string_sized_new (4096);
|
|
|
|
g_string_append_printf (
|
|
buffer, "<html><head><title>%s</title></head>",
|
|
_("iCalendar Information"));
|
|
g_string_append (buffer, HTML_BODY_START);
|
|
|
|
/* The table */
|
|
g_string_append (
|
|
buffer, "<table width=450 cellspacing=\"0\" "
|
|
"cellpadding=\"4\" border=\"0\">");
|
|
|
|
/* The column for the image */
|
|
g_string_append (
|
|
buffer, "<tr><td width=48 align=\"center\" "
|
|
"valign=\"top\" rowspan=\"8\">");
|
|
|
|
/* The image */
|
|
filename = e_icon_factory_get_icon_filename (
|
|
"stock_new-meeting", GTK_ICON_SIZE_DIALOG);
|
|
g_string_append_printf (buffer, "<img src=\"%s\"></td>", filename);
|
|
g_free (filename);
|
|
|
|
g_string_append (buffer, "<td align=\"left\" valign=\"top\">");
|
|
|
|
switch (priv->method) {
|
|
case ICAL_METHOD_REFRESH:
|
|
case ICAL_METHOD_REPLY:
|
|
/* An attendee sent this */
|
|
e_cal_component_get_attendee_list (priv->comp, &attendees);
|
|
if (attendees != NULL) {
|
|
attendee = attendees->data;
|
|
html = dupe_first_bold (itip_desc,
|
|
attendee->cn ?
|
|
attendee->cn :
|
|
itip_strip_mailto (attendee->value), NULL);
|
|
} else {
|
|
html = dupe_first_bold (itip_desc, _("An unknown person"), NULL);
|
|
}
|
|
break;
|
|
case ICAL_METHOD_REQUEST:
|
|
/* The organizer sent this */
|
|
e_cal_component_get_organizer (priv->comp, &organizer);
|
|
if (priv->delegator_address != NULL) {
|
|
if (organizer.value != NULL)
|
|
html = dupe_first_bold (itip_desc,
|
|
organizer.cn ?
|
|
organizer.cn :
|
|
itip_strip_mailto (organizer.value),
|
|
priv->delegator_name ?
|
|
priv->delegator_name :
|
|
priv->delegator_address);
|
|
else
|
|
html = dupe_first_bold (itip_desc, _("An unknown person"),
|
|
priv->delegator_name ?
|
|
priv->delegator_name :
|
|
priv->delegator_address);
|
|
} else {
|
|
if (organizer.value != NULL)
|
|
html = dupe_first_bold (itip_desc,
|
|
organizer.cn ?
|
|
organizer.cn :
|
|
itip_strip_mailto (organizer.value), NULL);
|
|
else
|
|
html = dupe_first_bold (itip_desc, _("An unknown person"), NULL);
|
|
}
|
|
|
|
break;
|
|
|
|
case ICAL_METHOD_PUBLISH:
|
|
case ICAL_METHOD_ADD:
|
|
case ICAL_METHOD_CANCEL:
|
|
default:
|
|
/* The organizer sent this */
|
|
e_cal_component_get_organizer (priv->comp, &organizer);
|
|
if (organizer.value != NULL)
|
|
html = dupe_first_bold (itip_desc,
|
|
organizer.cn ?
|
|
organizer.cn :
|
|
itip_strip_mailto (organizer.value), NULL);
|
|
else
|
|
html = dupe_first_bold (itip_desc, _("An unknown person"), NULL);
|
|
break;
|
|
}
|
|
g_string_append_printf (buffer, "%s", html);
|
|
g_free (html);
|
|
|
|
g_string_append (buffer, "<br> ");
|
|
/* Describe what the user can do */
|
|
g_string_append (
|
|
buffer, _("Please review the following information, "
|
|
"and then select an action from the menu below."));
|
|
|
|
/* Separator */
|
|
g_string_append (buffer, HTML_SEP);
|
|
|
|
/* Title */
|
|
set_message (buffer, itip_title, FALSE);
|
|
|
|
/* Date information */
|
|
set_date_label (itip, buffer, priv->comp);
|
|
|
|
/* Summary */
|
|
e_cal_component_get_summary (priv->comp, &text);
|
|
/* Translators: "None" used as a default value for events without Summary received by mail */
|
|
str = g_strdup_printf ("<i>%s</i>", C_("cal-itip", "None"));
|
|
|
|
html = text.value ? e_text_to_html_full (text.value, E_TEXT_TO_HTML_CONVERT_NL, 0) : str;
|
|
g_string_append_printf (
|
|
buffer, "<b>%s</b><br>%s<br><br>", _("Summary:"), html);
|
|
g_free (str);
|
|
if (text.value)
|
|
g_free (html);
|
|
|
|
/* Location */
|
|
e_cal_component_get_location (priv->comp, &string);
|
|
if (string != NULL) {
|
|
html = e_text_to_html_full (string, E_TEXT_TO_HTML_CONVERT_NL, 0);
|
|
g_string_append_printf (
|
|
buffer, "<b>%s</b><br>%s<br><br>",
|
|
_("Location:"), html);
|
|
g_free (html);
|
|
}
|
|
|
|
/* Status */
|
|
if (priv->method == ICAL_METHOD_REPLY) {
|
|
GSList *alist;
|
|
|
|
e_cal_component_get_attendee_list (priv->comp, &alist);
|
|
|
|
if (alist != NULL) {
|
|
ECalComponentAttendee *a = alist->data;
|
|
|
|
g_string_append_printf (
|
|
buffer, "<b>%s</b><br>", _("Status:"));
|
|
|
|
switch (a->status) {
|
|
case ICAL_PARTSTAT_ACCEPTED:
|
|
g_string_append_printf (
|
|
buffer, "%s<br><br>",
|
|
_("Accepted"));
|
|
break;
|
|
case ICAL_PARTSTAT_TENTATIVE:
|
|
g_string_append_printf (
|
|
buffer, "%s<br><br>",
|
|
_("Tentatively Accepted"));
|
|
break;
|
|
case ICAL_PARTSTAT_DECLINED:
|
|
g_string_append_printf (
|
|
buffer, "%s<br><br>",
|
|
_("Declined"));
|
|
break;
|
|
default:
|
|
g_string_append_printf (
|
|
buffer, "%s<br><br>",
|
|
_("Unknown"));
|
|
}
|
|
}
|
|
|
|
e_cal_component_free_attendee_list (alist);
|
|
}
|
|
|
|
/* Description */
|
|
e_cal_component_get_description_list (priv->comp, &l);
|
|
if (l)
|
|
text = *((ECalComponentText *)l->data);
|
|
|
|
if (l && text.value) {
|
|
html = e_text_to_html_full (
|
|
text.value, E_TEXT_TO_HTML_CONVERT_NL, 0);
|
|
g_string_append_printf (
|
|
buffer, "<b>%s</b><br>%s",
|
|
_("Description:"), html);
|
|
g_free (html);
|
|
}
|
|
e_cal_component_free_text_list (l);
|
|
|
|
/* Separator */
|
|
g_string_append (buffer, HTML_SEP);
|
|
|
|
/* Options */
|
|
if (!e_itip_control_get_view_only (itip)) {
|
|
if (options != NULL) {
|
|
g_string_append (
|
|
buffer, "</td></tr><tr><td valign=\"center\">");
|
|
g_string_append_printf (buffer, "%s", options);
|
|
}
|
|
}
|
|
|
|
g_string_append (buffer, "</td></tr></table>");
|
|
|
|
g_string_append (buffer, HTML_BODY_END);
|
|
g_string_append (buffer, HTML_FOOTER);
|
|
|
|
e_web_view_load_string (E_WEB_VIEW (priv->web_view), buffer->str);
|
|
|
|
g_string_free (buffer, TRUE);
|
|
}
|
|
|
|
static gchar *
|
|
get_publish_options (void)
|
|
{
|
|
return g_strdup_printf ("<object classid=\"itip:publish_options\"></object>");
|
|
}
|
|
|
|
static gchar *
|
|
get_request_options (void)
|
|
{
|
|
return g_strdup_printf ("<object classid=\"itip:request_options\"></object>");
|
|
}
|
|
|
|
static gchar *
|
|
get_request_fb_options (void)
|
|
{
|
|
return g_strdup_printf ("<object classid=\"itip:freebusy_options\"></object>");
|
|
}
|
|
|
|
static gchar *
|
|
get_reply_options (void)
|
|
{
|
|
return g_strdup_printf ("<object classid=\"itip:reply_options\"></object>");
|
|
}
|
|
|
|
static gchar *
|
|
get_refresh_options (void)
|
|
{
|
|
return g_strdup_printf ("<object classid=\"itip:refresh_options\"></object>");
|
|
}
|
|
|
|
static gchar *
|
|
get_cancel_options (gboolean found, icalcomponent_kind kind)
|
|
{
|
|
if (!found) {
|
|
switch (kind) {
|
|
case ICAL_VEVENT_COMPONENT:
|
|
return g_strdup_printf ("<i>%s</i>", _("The meeting has been canceled, however it could not be found in your calendars"));
|
|
case ICAL_VTODO_COMPONENT:
|
|
return g_strdup_printf ("<i>%s</i>", _("The task has been canceled, however it could not be found in your task lists"));
|
|
default:
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
}
|
|
|
|
return g_strdup_printf ("<object classid=\"itip:cancel_options\"></object>");
|
|
}
|
|
|
|
static ECalComponent *
|
|
get_real_item (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
ECalComponent *comp;
|
|
icalcomponent *icalcomp;
|
|
gboolean found = FALSE;
|
|
const gchar *uid;
|
|
|
|
priv = itip->priv;
|
|
|
|
e_cal_component_get_uid (priv->comp, &uid);
|
|
|
|
found = e_cal_get_object (priv->current_ecal, uid, NULL, &icalcomp, NULL);
|
|
if (!found)
|
|
return NULL;
|
|
|
|
comp = e_cal_component_new ();
|
|
if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
|
|
g_object_unref (comp);
|
|
icalcomponent_free (icalcomp);
|
|
return NULL;
|
|
}
|
|
|
|
return comp;
|
|
}
|
|
|
|
static void
|
|
adjust_item (EItipControl *itip, ECalComponent *comp)
|
|
{
|
|
ECalComponent *real_comp;
|
|
|
|
real_comp = get_real_item (itip);
|
|
if (real_comp != NULL) {
|
|
ECalComponentText text;
|
|
const gchar *string;
|
|
GSList *l;
|
|
|
|
e_cal_component_get_summary (real_comp, &text);
|
|
e_cal_component_set_summary (comp, &text);
|
|
e_cal_component_get_location (real_comp, &string);
|
|
e_cal_component_set_location (comp, string);
|
|
e_cal_component_get_description_list (real_comp, &l);
|
|
e_cal_component_set_description_list (comp, l);
|
|
e_cal_component_free_text_list (l);
|
|
|
|
g_object_unref (real_comp);
|
|
} else {
|
|
ECalComponentText text = {_("Unknown"), NULL};
|
|
|
|
e_cal_component_set_summary (comp, &text);
|
|
}
|
|
}
|
|
|
|
static void
|
|
show_current_event (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
const gchar *itip_title, *itip_desc;
|
|
gchar *options;
|
|
gboolean show_selector = FALSE;
|
|
|
|
priv = itip->priv;
|
|
|
|
priv->type = E_CAL_SOURCE_TYPE_EVENT;
|
|
|
|
switch (priv->method) {
|
|
case ICAL_METHOD_PUBLISH:
|
|
itip_desc = _("%s has published meeting information.");
|
|
itip_title = _("Meeting Information");
|
|
options = get_publish_options ();
|
|
show_selector = TRUE;
|
|
break;
|
|
case ICAL_METHOD_REQUEST:
|
|
if (priv->delegator_address != NULL)
|
|
itip_desc = _("%s requests the presence of %s at a meeting.");
|
|
else
|
|
itip_desc = _("%s requests your presence at a meeting.");
|
|
itip_title = _("Meeting Proposal");
|
|
options = get_request_options ();
|
|
show_selector = TRUE;
|
|
break;
|
|
case ICAL_METHOD_ADD:
|
|
/* FIXME Whats going on here? */
|
|
itip_desc = _("%s wishes to be added to an existing meeting.");
|
|
itip_title = _("Meeting Update");
|
|
options = get_publish_options ();
|
|
break;
|
|
case ICAL_METHOD_REFRESH:
|
|
itip_desc = _("%s wishes to receive the latest meeting information.");
|
|
itip_title = _("Meeting Update Request");
|
|
options = get_refresh_options ();
|
|
|
|
/* Provide extra info, since its not in the component */
|
|
adjust_item (itip, priv->comp);
|
|
break;
|
|
case ICAL_METHOD_REPLY:
|
|
itip_desc = _("%s has replied to a meeting request.");
|
|
itip_title = _("Meeting Reply");
|
|
options = get_reply_options ();
|
|
|
|
/* Provide extra info, since might not be in the component */
|
|
adjust_item (itip, priv->comp);
|
|
break;
|
|
case ICAL_METHOD_CANCEL:
|
|
itip_desc = _("%s has canceled a meeting.");
|
|
itip_title = _("Meeting Cancelation");
|
|
/* FIXME priv->current_ecal will always be NULL so the
|
|
* user won't see an error message, the OK button will
|
|
* just be de-sensitized */
|
|
options = get_cancel_options (TRUE, ICAL_VEVENT_COMPONENT);
|
|
|
|
/* Provide extra info, since might not be in the component */
|
|
adjust_item (itip, priv->comp);
|
|
break;
|
|
default:
|
|
itip_desc = _("%s has sent an unintelligible message.");
|
|
itip_title = _("Bad Meeting Message");
|
|
options = NULL;
|
|
}
|
|
|
|
write_html (itip, itip_desc, itip_title, options);
|
|
g_free (options);
|
|
|
|
if (priv->calendar_uid)
|
|
priv->current_ecal = start_calendar_server_by_uid (itip, priv->calendar_uid, priv->type);
|
|
else
|
|
find_server (itip, priv->comp, show_selector);
|
|
}
|
|
|
|
static void
|
|
show_current_todo (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
const gchar *itip_title, *itip_desc;
|
|
gchar *options;
|
|
gboolean show_selector = FALSE;
|
|
|
|
priv = itip->priv;
|
|
|
|
priv->type = E_CAL_SOURCE_TYPE_TODO;
|
|
|
|
switch (priv->method) {
|
|
case ICAL_METHOD_PUBLISH:
|
|
itip_desc = _("%s has published task information.");
|
|
itip_title = _("Task Information");
|
|
options = get_publish_options ();
|
|
show_selector = TRUE;
|
|
break;
|
|
case ICAL_METHOD_REQUEST:
|
|
/* FIXME Does this need to handle like events above? */
|
|
if (priv->delegator_address != NULL)
|
|
itip_desc = _("%s requests %s to perform a task.");
|
|
else
|
|
itip_desc = _("%s requests you perform a task.");
|
|
itip_title = _("Task Proposal");
|
|
options = get_request_options ();
|
|
show_selector = TRUE;
|
|
break;
|
|
case ICAL_METHOD_ADD:
|
|
/* FIXME Whats going on here? */
|
|
itip_desc = _("%s wishes to be added to an existing task.");
|
|
itip_title = _("Task Update");
|
|
options = get_publish_options ();
|
|
break;
|
|
case ICAL_METHOD_REFRESH:
|
|
itip_desc = _("%s wishes to receive the latest task information.");
|
|
itip_title = _("Task Update Request");
|
|
options = get_refresh_options ();
|
|
|
|
/* Provide extra info, since its not in the component */
|
|
adjust_item (itip, priv->comp);
|
|
break;
|
|
case ICAL_METHOD_REPLY:
|
|
itip_desc = _("%s has replied to a task assignment.");
|
|
itip_title = _("Task Reply");
|
|
options = get_reply_options ();
|
|
|
|
/* Provide extra info, since might not be in the component */
|
|
adjust_item (itip, priv->comp);
|
|
break;
|
|
case ICAL_METHOD_CANCEL:
|
|
itip_desc = _("%s has canceled a task.");
|
|
itip_title = _("Task Cancelation");
|
|
/* FIXME priv->current_ecal will always be NULL so the
|
|
* user won't see an error message, the OK button will
|
|
* just be de-sensitized */
|
|
options = get_cancel_options (TRUE, ICAL_VTODO_COMPONENT);
|
|
|
|
/* Provide extra info, since might not be in the component */
|
|
adjust_item (itip, priv->comp);
|
|
break;
|
|
default:
|
|
itip_desc = _("%s has sent an unintelligible message.");
|
|
itip_title = _("Bad Task Message");
|
|
options = NULL;
|
|
}
|
|
|
|
write_html (itip, itip_desc, itip_title, options);
|
|
g_free (options);
|
|
|
|
if (priv->calendar_uid)
|
|
priv->current_ecal = start_calendar_server_by_uid (itip, priv->calendar_uid, priv->type);
|
|
else
|
|
find_server (itip, priv->comp, show_selector);
|
|
}
|
|
|
|
static void
|
|
show_current_freebusy (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
const gchar *itip_title, *itip_desc;
|
|
gchar *options;
|
|
|
|
priv = itip->priv;
|
|
|
|
switch (priv->method) {
|
|
case ICAL_METHOD_PUBLISH:
|
|
itip_desc = _("%s has published free/busy information.");
|
|
itip_title = _("Free/Busy Information");
|
|
options = NULL;
|
|
break;
|
|
case ICAL_METHOD_REQUEST:
|
|
itip_desc = _("%s requests your free/busy information.");
|
|
itip_title = _("Free/Busy Request");
|
|
options = get_request_fb_options ();
|
|
break;
|
|
case ICAL_METHOD_REPLY:
|
|
itip_desc = _("%s has replied to a free/busy request.");
|
|
itip_title = _("Free/Busy Reply");
|
|
options = NULL;
|
|
break;
|
|
default:
|
|
itip_desc = _("%s has sent an unintelligible message.");
|
|
itip_title = _("Bad Free/Busy Message");
|
|
options = NULL;
|
|
}
|
|
|
|
write_html (itip, itip_desc, itip_title, options);
|
|
g_free (options);
|
|
}
|
|
|
|
static icalcomponent *
|
|
get_next (icalcompiter *iter)
|
|
{
|
|
icalcomponent *ret = NULL;
|
|
icalcomponent_kind kind;
|
|
|
|
do {
|
|
icalcompiter_next (iter);
|
|
ret = icalcompiter_deref (iter);
|
|
if (ret == NULL)
|
|
break;
|
|
kind = icalcomponent_isa (ret);
|
|
} while (ret != NULL
|
|
&& kind != ICAL_VEVENT_COMPONENT
|
|
&& kind != ICAL_VTODO_COMPONENT
|
|
&& kind != ICAL_VFREEBUSY_COMPONENT);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
show_current (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
ECalComponentVType type;
|
|
icalcomponent *alarm_comp;
|
|
icalcompiter alarm_iter;
|
|
icalproperty *prop;
|
|
|
|
priv = itip->priv;
|
|
|
|
g_object_ref (itip);
|
|
|
|
if (priv->comp)
|
|
g_object_unref (priv->comp);
|
|
priv->current_ecal = NULL;
|
|
|
|
/* Determine any delegate sections */
|
|
prop = icalcomponent_get_first_property (priv->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"))
|
|
e_itip_control_set_calendar_uid (itip, 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"))
|
|
e_itip_control_set_delegator_address (itip, x_val);
|
|
else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME"))
|
|
e_itip_control_set_delegator_name (itip, x_val);
|
|
|
|
prop = icalcomponent_get_next_property (priv->ical_comp, ICAL_X_PROPERTY);
|
|
}
|
|
|
|
/* Strip out alarms for security purposes */
|
|
alarm_iter = icalcomponent_begin_component (priv->ical_comp, ICAL_VALARM_COMPONENT);
|
|
while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
|
|
icalcompiter_next (&alarm_iter);
|
|
|
|
icalcomponent_remove_component (priv->ical_comp, alarm_comp);
|
|
icalcomponent_free (alarm_comp);
|
|
}
|
|
|
|
priv->comp = e_cal_component_new ();
|
|
if (!e_cal_component_set_icalcomponent (priv->comp, priv->ical_comp)) {
|
|
write_error_html (itip, _("The message does not appear to be properly formed"));
|
|
g_object_unref (priv->comp);
|
|
priv->comp = NULL;
|
|
g_object_unref (itip);
|
|
return;
|
|
};
|
|
|
|
/* Add default reminder if the config says so */
|
|
if (calendar_config_get_use_default_reminder ()) {
|
|
ECalComponentAlarm *acomp;
|
|
gint interval;
|
|
CalUnits units;
|
|
ECalComponentAlarmTrigger trigger;
|
|
|
|
interval = calendar_config_get_default_reminder_interval ();
|
|
units = calendar_config_get_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 CAL_MINUTES:
|
|
trigger.u.rel_duration.minutes = interval;
|
|
break;
|
|
case CAL_HOURS:
|
|
trigger.u.rel_duration.hours = interval;
|
|
break;
|
|
case CAL_DAYS:
|
|
trigger.u.rel_duration.days = interval;
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
e_cal_component_alarm_set_trigger (acomp, trigger);
|
|
e_cal_component_add_alarm (priv->comp, acomp);
|
|
|
|
e_cal_component_alarm_free (acomp);
|
|
}
|
|
|
|
type = e_cal_component_get_vtype (priv->comp);
|
|
|
|
switch (type) {
|
|
case E_CAL_COMPONENT_EVENT:
|
|
show_current_event (itip);
|
|
break;
|
|
case E_CAL_COMPONENT_TODO:
|
|
show_current_todo (itip);
|
|
break;
|
|
case E_CAL_COMPONENT_FREEBUSY:
|
|
show_current_freebusy (itip);
|
|
break;
|
|
default:
|
|
write_error_html (itip, _("The message contains only unsupported requests."));
|
|
}
|
|
|
|
find_my_address (itip, priv->ical_comp, NULL);
|
|
|
|
g_object_unref (itip);
|
|
}
|
|
|
|
void
|
|
e_itip_control_set_data (EItipControl *itip, const gchar *text)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
icalproperty *prop;
|
|
icalcomponent_kind kind = ICAL_NO_COMPONENT;
|
|
icalcomponent *tz_comp;
|
|
icalcompiter tz_iter;
|
|
|
|
priv = itip->priv;
|
|
if (priv == NULL)
|
|
return;
|
|
|
|
clean_up (itip);
|
|
|
|
if (text == NULL || *text == '\0') {
|
|
e_web_view_clear (E_WEB_VIEW (priv->web_view));
|
|
return;
|
|
}
|
|
|
|
priv->vcalendar = g_strdup (text);
|
|
priv->top_level = e_cal_util_new_top_level ();
|
|
|
|
priv->main_comp = icalparser_parse_string (priv->vcalendar);
|
|
if (priv->main_comp == NULL || !is_icalcomp_valid (priv->main_comp)) {
|
|
write_error_html (itip, _("The attachment does not contain a valid calendar message"));
|
|
|
|
if (priv->main_comp) {
|
|
icalcomponent_free (priv->main_comp);
|
|
priv->main_comp = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
prop = icalcomponent_get_first_property (priv->main_comp, ICAL_METHOD_PROPERTY);
|
|
if (prop == NULL) {
|
|
priv->method = ICAL_METHOD_PUBLISH;
|
|
} else {
|
|
priv->method = icalproperty_get_method (prop);
|
|
}
|
|
|
|
tz_iter = icalcomponent_begin_component (priv->main_comp, ICAL_VTIMEZONE_COMPONENT);
|
|
while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) {
|
|
icalcomponent *clone;
|
|
|
|
clone = icalcomponent_new_clone (tz_comp);
|
|
icalcomponent_add_component (priv->top_level, clone);
|
|
|
|
icalcompiter_next (&tz_iter);
|
|
}
|
|
|
|
priv->iter = icalcomponent_begin_component (priv->main_comp, ICAL_ANY_COMPONENT);
|
|
priv->ical_comp = icalcompiter_deref (&priv->iter);
|
|
if (priv->ical_comp != NULL) {
|
|
kind = icalcomponent_isa (priv->ical_comp);
|
|
if (kind != ICAL_VEVENT_COMPONENT
|
|
&& kind != ICAL_VTODO_COMPONENT
|
|
&& kind != ICAL_VFREEBUSY_COMPONENT)
|
|
priv->ical_comp = get_next (&priv->iter);
|
|
}
|
|
|
|
if (priv->ical_comp == NULL) {
|
|
write_error_html (itip, _("The attachment has no viewable calendar items"));
|
|
return;
|
|
}
|
|
|
|
priv->total = icalcomponent_count_components (priv->main_comp, ICAL_VEVENT_COMPONENT);
|
|
priv->total += icalcomponent_count_components (priv->main_comp, ICAL_VTODO_COMPONENT);
|
|
priv->total += icalcomponent_count_components (priv->main_comp, ICAL_VFREEBUSY_COMPONENT);
|
|
|
|
if (priv->total > 0)
|
|
priv->current = 1;
|
|
else
|
|
priv->current = 0;
|
|
|
|
show_current (itip);
|
|
}
|
|
|
|
gchar *
|
|
e_itip_control_get_data (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
return g_strdup (priv->vcalendar);
|
|
}
|
|
|
|
gint
|
|
e_itip_control_get_data_size (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (priv->vcalendar == NULL)
|
|
return 0;
|
|
|
|
return strlen (priv->vcalendar);
|
|
}
|
|
|
|
void
|
|
e_itip_control_set_from_address (EItipControl *itip, const gchar *address)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (priv->from_address)
|
|
g_free (priv->from_address);
|
|
|
|
priv->from_address = g_strdup (address);
|
|
}
|
|
|
|
const gchar *
|
|
e_itip_control_get_from_address (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
return priv->from_address;
|
|
}
|
|
|
|
void
|
|
e_itip_control_set_view_only (EItipControl *itip, gint view_only)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
priv->view_only = view_only;
|
|
}
|
|
|
|
gint
|
|
e_itip_control_get_view_only (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
return priv->view_only;
|
|
}
|
|
|
|
void
|
|
e_itip_control_set_delegator_address (EItipControl *itip, const gchar *address)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (priv->delegator_address)
|
|
g_free (priv->delegator_address);
|
|
|
|
priv->delegator_address = g_strdup (address);
|
|
}
|
|
|
|
const gchar *
|
|
e_itip_control_get_delegator_address (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
return priv->delegator_address;
|
|
}
|
|
|
|
void
|
|
e_itip_control_set_delegator_name (EItipControl *itip, const gchar *name)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (priv->delegator_name)
|
|
g_free (priv->delegator_name);
|
|
|
|
priv->delegator_name = g_strdup (name);
|
|
}
|
|
|
|
const gchar *
|
|
e_itip_control_get_delegator_name (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
return priv->delegator_name;
|
|
}
|
|
|
|
void
|
|
e_itip_control_set_calendar_uid (EItipControl *itip, const gchar *uri)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (priv->calendar_uid)
|
|
g_free (priv->calendar_uid);
|
|
|
|
priv->calendar_uid = g_strdup (uri);
|
|
}
|
|
|
|
const gchar *
|
|
e_itip_control_get_calendar_uid (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
return priv->calendar_uid;
|
|
}
|
|
|
|
static gboolean
|
|
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;
|
|
|
|
if (address != NULL) {
|
|
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);
|
|
} else {
|
|
EAccount *a;
|
|
|
|
a = itip_addresses_get_default ();
|
|
|
|
prop = icalproperty_new_attendee (a->id->address);
|
|
icalcomponent_add_property (ical_comp, prop);
|
|
|
|
param = icalparameter_new_cn (a->id->name);
|
|
icalproperty_add_parameter (prop, param);
|
|
|
|
param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
|
|
icalproperty_add_parameter (prop, param);
|
|
|
|
param = icalparameter_new_partstat (status);
|
|
icalproperty_add_parameter (prop, param);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
update_item (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
struct icaltimetype stamp;
|
|
gchar *str;
|
|
icalproperty *prop;
|
|
icalcomponent *clone;
|
|
GtkWidget *dialog;
|
|
GError *error = NULL;
|
|
|
|
priv = itip->priv;
|
|
|
|
/* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
|
|
* the user accepted/declined the request. (Outlook ignores
|
|
* SEQUENCE in REPLY reponses and instead requires that each
|
|
* updated response have a later REPLYTIME than the previous
|
|
* one.) This also ends up getting saved in our own copy of
|
|
* the meeting, though there's currently no way to see that
|
|
* information (unless it's being saved to an Exchange folder
|
|
* and you then look at it in Outlook).
|
|
*/
|
|
stamp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
|
|
str = icaltime_as_ical_string_r (stamp);
|
|
prop = icalproperty_new_x (str);
|
|
g_free (str);
|
|
icalproperty_set_x_name (prop, "X-MICROSOFT-CDO-REPLYTIME");
|
|
icalcomponent_add_property (priv->ical_comp, prop);
|
|
|
|
clone = icalcomponent_new_clone (priv->ical_comp);
|
|
icalcomponent_add_component (priv->top_level, clone);
|
|
icalcomponent_set_method (priv->top_level, priv->method);
|
|
|
|
if (!e_cal_receive_objects (priv->current_ecal, priv->top_level, &error)) {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_OK,
|
|
"%s", error->message);
|
|
g_error_free (error);
|
|
} else {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_INFO,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("Update complete\n"));
|
|
}
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
|
|
icalcomponent_remove_component (priv->top_level, clone);
|
|
icalcomponent_free (clone);
|
|
}
|
|
|
|
static void
|
|
update_attendee_status (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
ECalComponent *comp = NULL;
|
|
icalcomponent *icalcomp = NULL;
|
|
const gchar *uid;
|
|
GtkWidget *dialog;
|
|
GError *error = NULL;
|
|
|
|
priv = itip->priv;
|
|
|
|
/* Obtain our version */
|
|
e_cal_component_get_uid (priv->comp, &uid);
|
|
if (e_cal_get_object (priv->current_ecal, uid, NULL, &icalcomp, NULL)) {
|
|
GSList *attendees;
|
|
|
|
comp = e_cal_component_new ();
|
|
if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
|
|
icalcomponent_free (icalcomp);
|
|
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("Object is invalid and "
|
|
"cannot be updated\n"));
|
|
goto run;
|
|
} else {
|
|
e_cal_component_get_attendee_list (priv->comp, &attendees);
|
|
if (attendees != NULL) {
|
|
ECalComponentAttendee *a = attendees->data;
|
|
icalproperty *prop;
|
|
|
|
prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
|
|
|
|
if (prop == NULL) {
|
|
gint response;
|
|
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_QUESTION,
|
|
GTK_BUTTONS_YES_NO,
|
|
"%s", _("This response is not from a "
|
|
"current attendee. Add as an attendee?"));
|
|
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
|
|
if (response == GTK_RESPONSE_YES) {
|
|
change_status (icalcomp,
|
|
itip_strip_mailto (a->value),
|
|
a->status);
|
|
e_cal_component_rescan (comp);
|
|
} else {
|
|
goto cleanup;
|
|
}
|
|
} else if (a->status == ICAL_PARTSTAT_NONE || a->status == ICAL_PARTSTAT_X) {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("Attendee status could not be "
|
|
"updated because of an invalid status!\n"));
|
|
goto run;
|
|
} else {
|
|
change_status (icalcomp,
|
|
itip_strip_mailto (a->value),
|
|
a->status);
|
|
e_cal_component_rescan (comp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!e_cal_modify_object (priv->current_ecal, icalcomp, CALOBJ_MOD_ALL, &error)) {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_OK,
|
|
"%s", error->message);
|
|
g_error_free (error);
|
|
} else {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_INFO,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("Attendee status updated\n"));
|
|
}
|
|
} else {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("Attendee status can not be updated "
|
|
"because the item no longer exists"));
|
|
}
|
|
|
|
run:
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
|
|
cleanup:
|
|
if (comp != NULL)
|
|
g_object_unref (comp);
|
|
}
|
|
|
|
static void
|
|
send_item (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
ECalComponent *comp;
|
|
GtkWidget *dialog;
|
|
|
|
priv = itip->priv;
|
|
|
|
comp = get_real_item (itip);
|
|
|
|
if (comp != NULL) {
|
|
itip_send_comp (E_CAL_COMPONENT_METHOD_REQUEST, comp, priv->current_ecal, NULL, NULL, NULL, TRUE, FALSE);
|
|
g_object_unref (comp);
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_INFO,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("Item sent!\n"));
|
|
} else {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("The item could not be sent!\n"));
|
|
}
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
}
|
|
|
|
static void
|
|
send_freebusy (EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
ECalComponentDateTime datetime;
|
|
time_t start, end;
|
|
GtkWidget *dialog;
|
|
GList *comp_list = NULL;
|
|
icaltimezone *zone;
|
|
|
|
priv = itip->priv;
|
|
|
|
e_cal_component_get_dtstart (priv->comp, &datetime);
|
|
if (datetime.tzid) {
|
|
zone = icalcomponent_get_timezone (priv->top_level,
|
|
datetime.tzid);
|
|
} else {
|
|
zone = NULL;
|
|
}
|
|
start = icaltime_as_timet_with_zone (*datetime.value, zone);
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
e_cal_component_get_dtend (priv->comp, &datetime);
|
|
if (datetime.tzid) {
|
|
zone = icalcomponent_get_timezone (priv->top_level,
|
|
datetime.tzid);
|
|
} else {
|
|
zone = NULL;
|
|
}
|
|
end = icaltime_as_timet_with_zone (*datetime.value, zone);
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
if (e_cal_get_free_busy (priv->current_ecal, NULL, start, end, &comp_list, NULL)) {
|
|
GList *l;
|
|
|
|
for (l = comp_list; l; l = l->next) {
|
|
ECalComponent *comp = E_CAL_COMPONENT (l->data);
|
|
itip_send_comp (E_CAL_COMPONENT_METHOD_REPLY, comp, priv->current_ecal, NULL, NULL, NULL, TRUE, FALSE);
|
|
|
|
g_object_unref (comp);
|
|
}
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_INFO,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("Item sent!\n"));
|
|
|
|
g_list_free (comp_list);
|
|
} else {
|
|
dialog = gtk_message_dialog_new (
|
|
NULL, 0,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_OK,
|
|
"%s", _("The item could not be sent!\n"));
|
|
}
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
}
|
|
|
|
static GtkWidget *
|
|
create_combo_box (void)
|
|
{
|
|
GtkComboBox *combo;
|
|
GtkCellRenderer *cell;
|
|
GtkListStore *store;
|
|
|
|
combo = GTK_COMBO_BOX (gtk_combo_box_new ());
|
|
|
|
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
|
|
gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
|
|
g_object_unref (store);
|
|
|
|
gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
|
|
|
|
cell = gtk_cell_renderer_text_new ();
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
|
|
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
|
|
"text", 0,
|
|
NULL);
|
|
|
|
return GTK_WIDGET (combo);
|
|
}
|
|
|
|
static void
|
|
option_activated_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
EItipControl *itip = E_ITIP_CONTROL (data);
|
|
EItipControlPrivate *priv;
|
|
GtkTreeIter iter;
|
|
gint act;
|
|
|
|
priv = itip->priv;
|
|
|
|
g_return_if_fail (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter));
|
|
|
|
gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (widget)), &iter, 1, &act, -1);
|
|
|
|
priv->action = act;
|
|
}
|
|
|
|
static void
|
|
add_option (EItipControl *itip, GtkWidget *combo, const gchar *text, gchar action)
|
|
{
|
|
GtkTreeIter iter;
|
|
GtkListStore *store;
|
|
|
|
store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)));
|
|
|
|
gtk_list_store_append (store, &iter);
|
|
gtk_list_store_set (
|
|
store, &iter,
|
|
0, text,
|
|
1, (gint) action,
|
|
-1);
|
|
|
|
if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo)) == -1) {
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
|
|
g_signal_connect (combo, "changed", G_CALLBACK (option_activated_cb), itip);
|
|
}
|
|
}
|
|
|
|
static void
|
|
insert_boxes (GtkHTMLEmbedded *eb, EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
priv->vbox.widget = gtk_vbox_new (FALSE, 12);
|
|
g_object_add_weak_pointer (G_OBJECT (priv->vbox.widget), &priv->vbox.pointer);
|
|
gtk_container_add (GTK_CONTAINER (eb), priv->vbox.widget);
|
|
gtk_widget_show (priv->vbox.widget);
|
|
|
|
priv->hbox.widget = gtk_hbox_new (FALSE, 6);
|
|
g_object_add_weak_pointer (G_OBJECT (priv->hbox.widget), &priv->hbox.pointer);
|
|
|
|
gtk_box_pack_start (GTK_BOX (priv->vbox.widget), priv->hbox.widget, FALSE, TRUE, 0);
|
|
gtk_widget_show (priv->hbox.widget);
|
|
}
|
|
|
|
static void
|
|
insert_label (GtkWidget *hbox)
|
|
{
|
|
GtkWidget *label;
|
|
gchar *text;
|
|
|
|
text = g_strdup_printf ("<b>%s</b>", _("Choose an action:"));
|
|
label = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (label), text);
|
|
g_free (text);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
|
|
gtk_widget_show (label);
|
|
}
|
|
|
|
static void
|
|
rsvp_clicked_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
EItipControl *itip = E_ITIP_CONTROL (data);
|
|
EItipControlPrivate *priv;
|
|
|
|
priv = itip->priv;
|
|
|
|
priv->rsvp = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
|
|
}
|
|
|
|
static void
|
|
insert_rsvp (GtkWidget *hbox, EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *btn;
|
|
|
|
priv = itip->priv;
|
|
|
|
/* To translators: RSVP means "please reply" */
|
|
btn = gtk_check_button_new_with_label (_("RSVP"));
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (btn), TRUE);
|
|
priv->rsvp = TRUE;
|
|
|
|
g_signal_connect (btn, "clicked", G_CALLBACK (rsvp_clicked_cb), itip);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, TRUE, 0);
|
|
gtk_widget_show (btn);
|
|
}
|
|
|
|
static void
|
|
insert_ok (GtkWidget *hbox, EItipControl *itip)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
priv = itip->priv;
|
|
|
|
priv->ok.widget = gtk_button_new_from_stock (GTK_STOCK_OK);
|
|
g_object_add_weak_pointer (G_OBJECT (priv->ok.widget), &priv->ok.pointer);
|
|
|
|
g_signal_connect (priv->ok.widget, "clicked", G_CALLBACK (ok_clicked_cb), itip);
|
|
|
|
set_ok_sens (itip);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), priv->ok.widget, FALSE, TRUE, 0);
|
|
gtk_widget_show (priv->ok.widget);
|
|
}
|
|
|
|
static gboolean
|
|
publish_options_object (EItipControl *itip, GtkHTML *html, GtkHTMLEmbedded *eb)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *combo;
|
|
|
|
priv = itip->priv;
|
|
|
|
insert_boxes (eb, itip);
|
|
insert_label (priv->hbox.widget);
|
|
|
|
combo = create_combo_box ();
|
|
|
|
add_option (itip, combo, _("Update"), 'U');
|
|
priv->action = 'U';
|
|
|
|
gtk_box_pack_start (GTK_BOX (priv->hbox.widget), combo, FALSE, TRUE, 0);
|
|
gtk_widget_show (combo);
|
|
|
|
insert_ok (priv->hbox.widget, itip);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
request_options_object (EItipControl *itip, GtkHTML *html, GtkHTMLEmbedded *eb)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *combo;
|
|
|
|
priv = itip->priv;
|
|
|
|
insert_boxes (eb, itip);
|
|
insert_label (priv->hbox.widget);
|
|
|
|
combo = create_combo_box ();
|
|
|
|
add_option (itip, combo, _("Accept"), 'A');
|
|
add_option (itip, combo, _("Tentatively accept"), 'T');
|
|
add_option (itip, combo, _("Decline"), 'D');
|
|
priv->action = 'A';
|
|
|
|
gtk_box_pack_start (GTK_BOX (priv->hbox.widget), combo, FALSE, TRUE, 0);
|
|
gtk_widget_show (combo);
|
|
|
|
insert_rsvp (priv->hbox.widget, itip);
|
|
insert_ok (priv->hbox.widget, itip);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
freebusy_options_object (EItipControl *itip, GtkHTML *html, GtkHTMLEmbedded *eb)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *combo;
|
|
|
|
priv = itip->priv;
|
|
|
|
insert_boxes (eb, itip);
|
|
insert_label (priv->hbox.widget);
|
|
|
|
combo = create_combo_box ();
|
|
|
|
add_option (itip, combo, _("Send Free/Busy Information"), 'F');
|
|
priv->action = 'F';
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->hbox.widget), combo);
|
|
gtk_widget_show (combo);
|
|
|
|
insert_ok (priv->hbox.widget, itip);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
reply_options_object (EItipControl *itip, GtkHTML *html, GtkHTMLEmbedded *eb)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *combo;
|
|
|
|
priv = itip->priv;
|
|
|
|
insert_boxes (eb, itip);
|
|
insert_label (priv->hbox.widget);
|
|
|
|
combo = create_combo_box ();
|
|
|
|
add_option (itip, combo, _("Update respondent status"), 'R');
|
|
priv->action = 'R';
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->hbox.widget), combo);
|
|
gtk_widget_show (combo);
|
|
|
|
insert_ok (priv->hbox.widget, itip);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
refresh_options_object (EItipControl *itip, GtkHTML *html, GtkHTMLEmbedded *eb)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *combo;
|
|
|
|
priv = itip->priv;
|
|
|
|
insert_boxes (eb, itip);
|
|
insert_label (priv->hbox.widget);
|
|
|
|
combo = create_combo_box ();
|
|
|
|
add_option (itip, combo, _("Send Latest Information"), 'S');
|
|
priv->action = 'S';
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->hbox.widget), combo);
|
|
gtk_widget_show (combo);
|
|
|
|
insert_ok (priv->hbox.widget, itip);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
cancel_options_object (EItipControl *itip, GtkHTML *html, GtkHTMLEmbedded *eb)
|
|
{
|
|
EItipControlPrivate *priv;
|
|
GtkWidget *combo;
|
|
|
|
priv = itip->priv;
|
|
|
|
insert_boxes (eb, itip);
|
|
insert_label (priv->hbox.widget);
|
|
|
|
combo = create_combo_box ();
|
|
|
|
add_option (itip, combo, _("Cancel"), 'C');
|
|
priv->action = 'C';
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->hbox.widget), combo);
|
|
gtk_widget_show (combo);
|
|
|
|
insert_ok (priv->hbox.widget, itip);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
object_requested_cb (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data)
|
|
{
|
|
EItipControl *itip = E_ITIP_CONTROL (data);
|
|
|
|
if (!strcmp (eb->classid, "itip:publish_options"))
|
|
return publish_options_object (itip, html, eb);
|
|
else if (!strcmp (eb->classid, "itip:request_options"))
|
|
return request_options_object (itip, html, eb);
|
|
else if (!strcmp (eb->classid, "itip:freebusy_options"))
|
|
return freebusy_options_object (itip, html, eb);
|
|
else if (!strcmp (eb->classid, "itip:reply_options"))
|
|
return reply_options_object (itip, html, eb);
|
|
else if (!strcmp (eb->classid, "itip:refresh_options"))
|
|
return refresh_options_object (itip, html, eb);
|
|
else if (!strcmp (eb->classid, "itip:cancel_options"))
|
|
return cancel_options_object (itip, html, eb);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
ok_clicked_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
EItipControl *itip = E_ITIP_CONTROL (data);
|
|
EItipControlPrivate *priv;
|
|
gboolean status = FALSE;
|
|
|
|
priv = itip->priv;
|
|
|
|
if (!priv->my_address && priv->current_ecal != NULL)
|
|
e_cal_get_cal_address (priv->current_ecal, &priv->my_address, NULL);
|
|
|
|
switch (priv->action) {
|
|
case 'U':
|
|
update_item (itip);
|
|
break;
|
|
case 'A':
|
|
status = change_status (priv->ical_comp, priv->my_address,
|
|
ICAL_PARTSTAT_ACCEPTED);
|
|
if (status) {
|
|
e_cal_component_rescan (priv->comp);
|
|
update_item (itip);
|
|
}
|
|
break;
|
|
case 'T':
|
|
status = change_status (priv->ical_comp, priv->my_address,
|
|
ICAL_PARTSTAT_TENTATIVE);
|
|
if (status) {
|
|
e_cal_component_rescan (priv->comp);
|
|
update_item (itip);
|
|
}
|
|
break;
|
|
case 'D':
|
|
status = change_status (priv->ical_comp, priv->my_address,
|
|
ICAL_PARTSTAT_DECLINED);
|
|
if (status) {
|
|
e_cal_component_rescan (priv->comp);
|
|
update_item (itip);
|
|
}
|
|
break;
|
|
case 'F':
|
|
send_freebusy (itip);
|
|
break;
|
|
case 'R':
|
|
update_attendee_status (itip);
|
|
break;
|
|
case 'S':
|
|
send_item (itip);
|
|
break;
|
|
case 'C':
|
|
update_item (itip);
|
|
break;
|
|
}
|
|
|
|
if (e_cal_get_save_schedules (priv->current_ecal))
|
|
return;
|
|
|
|
if (priv->rsvp && status) {
|
|
ECalComponent *comp = NULL;
|
|
icalcomponent *ical_comp;
|
|
icalproperty *prop;
|
|
icalvalue *value;
|
|
const gchar *attendee;
|
|
GSList *l, *list = NULL;
|
|
|
|
comp = e_cal_component_clone (priv->comp);
|
|
if (comp == NULL)
|
|
return;
|
|
|
|
if (priv->my_address == NULL)
|
|
find_my_address (itip, priv->ical_comp, NULL);
|
|
g_return_if_fail (priv->my_address != NULL);
|
|
|
|
ical_comp = e_cal_component_get_icalcomponent (comp);
|
|
|
|
for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
|
|
prop != NULL;
|
|
prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY))
|
|
{
|
|
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 (priv->my_address, text))
|
|
list = g_slist_prepend (list, prop);
|
|
g_free (text);
|
|
}
|
|
|
|
for (l = list; l; l = l->next) {
|
|
prop = l->data;
|
|
icalcomponent_remove_property (ical_comp, prop);
|
|
icalproperty_free (prop);
|
|
}
|
|
g_slist_free (list);
|
|
|
|
e_cal_component_rescan (comp);
|
|
itip_send_comp (E_CAL_COMPONENT_METHOD_REPLY, comp, priv->current_ecal, priv->top_level, NULL, NULL, TRUE, FALSE);
|
|
|
|
g_object_unref (comp);
|
|
}
|
|
}
|