diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:54:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:54:43 +0000 |
commit | e4283f6d48b98e764b988b43bbc86b9d52e6ec94 (patch) | |
tree | c8f7f7a6c2f5faa2942d27cefc6fd46cca492656 /src/calendar-server | |
parent | Initial commit. (diff) | |
download | gnome-shell-e4283f6d48b98e764b988b43bbc86b9d52e6ec94.tar.xz gnome-shell-e4283f6d48b98e764b988b43bbc86b9d52e6ec94.zip |
Adding upstream version 43.9.upstream/43.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/calendar-server')
-rw-r--r-- | src/calendar-server/README | 1 | ||||
-rw-r--r-- | src/calendar-server/calendar-debug.h | 50 | ||||
-rw-r--r-- | src/calendar-server/calendar-sources.c | 506 | ||||
-rw-r--r-- | src/calendar-server/calendar-sources.h | 64 | ||||
-rw-r--r-- | src/calendar-server/evolution-calendar.desktop.in | 8 | ||||
-rw-r--r-- | src/calendar-server/gnome-shell-calendar-server.c | 1131 | ||||
-rw-r--r-- | src/calendar-server/meson.build | 37 | ||||
-rw-r--r-- | src/calendar-server/org.gnome.Shell.CalendarServer.service.in | 3 |
8 files changed, 1800 insertions, 0 deletions
diff --git a/src/calendar-server/README b/src/calendar-server/README new file mode 100644 index 0000000..ad9b5e3 --- /dev/null +++ b/src/calendar-server/README @@ -0,0 +1 @@ +Please keep in sync with gnome-panel. diff --git a/src/calendar-server/calendar-debug.h b/src/calendar-server/calendar-debug.h new file mode 100644 index 0000000..39befd7 --- /dev/null +++ b/src/calendar-server/calendar-debug.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Mark McLoughlin <mark@skynet.ie> + */ + +#ifndef __CALENDAR_DEBUG_H__ +#define __CALENDAR_DEBUG_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +#ifdef CALENDAR_ENABLE_DEBUG + +#include <stdio.h> + +#ifdef G_HAVE_ISO_VARARGS +# define dprintf(...) fprintf (stderr, __VA_ARGS__); +#elif defined(G_HAVE_GNUC_VARARGS) +# define dprintf(args...) fprintf (stderr, args); +#endif + +#else /* if !defined (CALENDAR_DEBUG) */ + +#ifdef G_HAVE_ISO_VARARGS +# define dprintf(...) +#elif defined(G_HAVE_GNUC_VARARGS) +# define dprintf(args...) +#endif + +#endif /* CALENDAR_ENABLE_DEBUG */ + +G_END_DECLS + +#endif /* __CALENDAR_DEBUG_H__ */ diff --git a/src/calendar-server/calendar-sources.c b/src/calendar-server/calendar-sources.c new file mode 100644 index 0000000..9c25f4e --- /dev/null +++ b/src/calendar-server/calendar-sources.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Mark McLoughlin <mark@skynet.ie> + * William Jon McCann <mccann@jhu.edu> + * Martin Grimme <martin@pycage.de> + * Christian Kellner <gicmo@xatom.net> + */ + +#include <config.h> + +#include "calendar-sources.h" + +#include <libintl.h> +#include <string.h> +#define HANDLE_LIBICAL_MEMORY +#define EDS_DISABLE_DEPRECATED +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +#include <libecal/libecal.h> +G_GNUC_END_IGNORE_DEPRECATIONS + +#undef CALENDAR_ENABLE_DEBUG +#include "calendar-debug.h" + +typedef struct _ClientData ClientData; +typedef struct _CalendarSourceData CalendarSourceData; + +struct _ClientData +{ + ECalClient *client; + gulong backend_died_id; +}; + +typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate; + +struct _CalendarSources +{ + GObject parent; + + ESourceRegistryWatcher *registry_watcher; + gulong filter_id; + gulong appeared_id; + gulong disappeared_id; + + GMutex clients_lock; + GHashTable *clients; /* ESource -> ClientData */ +}; + +G_DEFINE_TYPE (CalendarSources, calendar_sources, G_TYPE_OBJECT) + +enum +{ + CLIENT_APPEARED, + CLIENT_DISAPPEARED, + LAST_SIGNAL +}; +static guint signals [LAST_SIGNAL] = { 0, }; + +static void +calendar_sources_client_connected_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CalendarSources *sources = CALENDAR_SOURCES (source_object); + ESource *source = user_data; + EClient *client; + g_autoptr (GError) error = NULL; + + /* The calendar_sources_connect_client_sync() already stored the 'client' + * into the sources->clients */ + client = calendar_sources_connect_client_finish (sources, result, &error); + if (error) + { + g_warning ("Could not load source '%s': %s", + e_source_get_uid (source), + error->message); + } + else + { + g_signal_emit (sources, signals[CLIENT_APPEARED], 0, client, NULL); + } + + g_clear_object (&client); + g_clear_object (&source); +} + +static gboolean +registry_watcher_filter_cb (ESourceRegistryWatcher *watcher, + ESource *source, + CalendarSources *sources) +{ + return e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) && + e_source_selectable_get_selected (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR)); +} + +static void +registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher, + ESource *source, + CalendarSources *sources) +{ + ECalClientSourceType source_type; + + if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) + source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; + else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) + source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS; + else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) + source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; + else + g_return_if_reached (); + + calendar_sources_connect_client (sources, source, source_type, 30, NULL, calendar_sources_client_connected_cb, g_object_ref (source)); +} + +static void +registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher, + ESource *source, + CalendarSources *sources) +{ + gboolean emit; + + g_mutex_lock (&sources->clients_lock); + + emit = g_hash_table_remove (sources->clients, source); + + g_mutex_unlock (&sources->clients_lock); + + if (emit) + g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL); +} + +static void +client_data_free (ClientData *data) +{ + g_signal_handler_disconnect (data->client, data->backend_died_id); + g_object_unref (data->client); + g_free (data); +} + +static void +calendar_sources_constructed (GObject *object) +{ + CalendarSources *sources = CALENDAR_SOURCES (object); + ESourceRegistry *registry = NULL; + GError *error = NULL; + + G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object); + + registry = e_source_registry_new_sync (NULL, &error); + if (error != NULL) + { + /* Any error is fatal, but we don't want to crash gnome-shell-calendar-server + because of e-d-s problems. So just exit here. + */ + g_warning ("Failed to start evolution-source-registry: %s", error->message); + exit (EXIT_FAILURE); + } + + g_return_if_fail (registry != NULL); + + sources->registry_watcher = e_source_registry_watcher_new (registry, NULL); + + g_clear_object (®istry); + + sources->clients = g_hash_table_new_full ((GHashFunc) e_source_hash, + (GEqualFunc) e_source_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) client_data_free); + sources->filter_id = g_signal_connect (sources->registry_watcher, + "filter", + G_CALLBACK (registry_watcher_filter_cb), + sources); + sources->appeared_id = g_signal_connect (sources->registry_watcher, + "appeared", + G_CALLBACK (registry_watcher_source_appeared_cb), + sources); + sources->disappeared_id = g_signal_connect (sources->registry_watcher, + "disappeared", + G_CALLBACK (registry_watcher_source_disappeared_cb), + sources); + + e_source_registry_watcher_reclaim (sources->registry_watcher); +} + +static void +calendar_sources_finalize (GObject *object) +{ + CalendarSources *sources = CALENDAR_SOURCES (object); + + g_clear_pointer (&sources->clients, g_hash_table_destroy); + + if (sources->registry_watcher) + { + g_signal_handler_disconnect (sources->registry_watcher, + sources->filter_id); + g_signal_handler_disconnect (sources->registry_watcher, + sources->appeared_id); + g_signal_handler_disconnect (sources->registry_watcher, + sources->disappeared_id); + g_clear_object (&sources->registry_watcher); + } + + g_mutex_clear (&sources->clients_lock); + + G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object); +} + +static void +calendar_sources_class_init (CalendarSourcesClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->constructed = calendar_sources_constructed; + gobject_class->finalize = calendar_sources_finalize; + + signals [CLIENT_APPEARED] = + g_signal_new ("client-appeared", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + E_TYPE_CAL_CLIENT); + + signals [CLIENT_DISAPPEARED] = + g_signal_new ("client-disappeared", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); /* ESource::uid of the disappeared client */ +} + +static void +calendar_sources_init (CalendarSources *sources) +{ + g_mutex_init (&sources->clients_lock); +} + +CalendarSources * +calendar_sources_get (void) +{ + static CalendarSources *calendar_sources_singleton = NULL; + gpointer singleton_location = &calendar_sources_singleton; + + if (calendar_sources_singleton) + return g_object_ref (calendar_sources_singleton); + + calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL); + g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton), + singleton_location); + + return calendar_sources_singleton; +} + +ESourceRegistry * +calendar_sources_get_registry (CalendarSources *sources) +{ + return e_source_registry_watcher_get_registry (sources->registry_watcher); +} + +static void +gather_event_clients_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + GSList **plist = user_data; + ClientData *cd = value; + + if (cd) + *plist = g_slist_prepend (*plist, g_object_ref (cd->client)); +} + +GSList * +calendar_sources_ref_clients (CalendarSources *sources) +{ + GSList *list = NULL; + + g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); + + g_mutex_lock (&sources->clients_lock); + g_hash_table_foreach (sources->clients, gather_event_clients_cb, &list); + g_mutex_unlock (&sources->clients_lock); + + return list; +} + +gboolean +calendar_sources_has_clients (CalendarSources *sources) +{ + GHashTableIter iter; + gpointer value; + gboolean has = FALSE; + + g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE); + + g_mutex_lock (&sources->clients_lock); + + g_hash_table_iter_init (&iter, sources->clients); + while (!has && g_hash_table_iter_next (&iter, NULL, &value)) + { + ClientData *cd = value; + + has = cd != NULL; + } + + g_mutex_unlock (&sources->clients_lock); + + return has; +} + +static void +backend_died_cb (EClient *client, + CalendarSources *sources) +{ + ESource *source; + const char *display_name; + + source = e_client_get_source (client); + display_name = e_source_get_display_name (source); + g_warning ("The calendar backend for '%s' has crashed.", display_name); + g_mutex_lock (&sources->clients_lock); + g_hash_table_remove (sources->clients, source); + g_mutex_unlock (&sources->clients_lock); +} + +static EClient * +calendar_sources_connect_client_sync (CalendarSources *sources, + ESource *source, + ECalClientSourceType source_type, + guint32 wait_for_connected_seconds, + GCancellable *cancellable, + GError **error) +{ + EClient *client = NULL; + ClientData *client_data; + + g_mutex_lock (&sources->clients_lock); + client_data = g_hash_table_lookup (sources->clients, source); + if (client_data) + client = E_CLIENT (g_object_ref (client_data->client)); + g_mutex_unlock (&sources->clients_lock); + + if (client) + return client; + + client = e_cal_client_connect_sync (source, source_type, wait_for_connected_seconds, cancellable, error); + if (!client) + return NULL; + + g_mutex_lock (&sources->clients_lock); + client_data = g_hash_table_lookup (sources->clients, source); + if (client_data) + { + g_clear_object (&client); + client = E_CLIENT (g_object_ref (client_data->client)); + } + else + { + client_data = g_new0 (ClientData, 1); + client_data->client = E_CAL_CLIENT (g_object_ref (client)); + client_data->backend_died_id = g_signal_connect (client, + "backend-died", + G_CALLBACK (backend_died_cb), + sources); + + g_hash_table_insert (sources->clients, g_object_ref (source), client_data); + } + g_mutex_unlock (&sources->clients_lock); + + return client; +} + +typedef struct _AsyncContext { + ESource *source; + ECalClientSourceType source_type; + guint32 wait_for_connected_seconds; +} AsyncContext; + +static void +async_context_free (gpointer ptr) +{ + AsyncContext *ctx = ptr; + + if (ctx) + { + g_clear_object (&ctx->source); + g_free (ctx); + } +} + +static void +calendar_sources_connect_client_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + CalendarSources *sources = source_object; + AsyncContext *ctx = task_data; + EClient *client; + GError *local_error = NULL; + + client = calendar_sources_connect_client_sync (sources, ctx->source, ctx->source_type, + ctx->wait_for_connected_seconds, cancellable, &local_error); + if (!client) + { + if (local_error) + g_task_return_error (task, local_error); + else + g_task_return_pointer (task, NULL, NULL); + } else { + g_task_return_pointer (task, client, g_object_unref); + } +} + +void +calendar_sources_connect_client (CalendarSources *sources, + ESource *source, + ECalClientSourceType source_type, + guint32 wait_for_connected_seconds, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AsyncContext *ctx; + g_autoptr (GTask) task = NULL; + + ctx = g_new0 (AsyncContext, 1); + ctx->source = g_object_ref (source); + ctx->source_type = source_type; + ctx->wait_for_connected_seconds = wait_for_connected_seconds; + + task = g_task_new (sources, cancellable, callback, user_data); + g_task_set_source_tag (task, calendar_sources_connect_client); + g_task_set_task_data (task, ctx, async_context_free); + + g_task_run_in_thread (task, calendar_sources_connect_client_thread); +} + +EClient * +calendar_sources_connect_client_finish (CalendarSources *sources, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, sources), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, calendar_sources_connect_client), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + + +void +print_debug (const gchar *format, + ...) +{ + g_autofree char *s = NULL; + g_autofree char *timestamp = NULL; + va_list ap; + g_autoptr (GDateTime) now = NULL; + static size_t once_init_value = 0; + static gboolean show_debug = FALSE; + static guint pid = 0; + + if (g_once_init_enter (&once_init_value)) + { + show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL); + pid = getpid (); + g_once_init_leave (&once_init_value, 1); + } + + if (!show_debug) + goto out; + + now = g_date_time_new_now_local (); + timestamp = g_date_time_format (now, "%H:%M:%S"); + + va_start (ap, format); + s = g_strdup_vprintf (format, ap); + va_end (ap); + + g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n", + pid, timestamp, g_date_time_get_microsecond (now), s); + out: + ; +} diff --git a/src/calendar-server/calendar-sources.h b/src/calendar-server/calendar-sources.h new file mode 100644 index 0000000..1ffc8ad --- /dev/null +++ b/src/calendar-server/calendar-sources.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Mark McLoughlin <mark@skynet.ie> + * William Jon McCann <mccann@jhu.edu> + * Martin Grimme <martin@pycage.de> + * Christian Kellner <gicmo@xatom.net> + */ + +#ifndef __CALENDAR_SOURCES_H__ +#define __CALENDAR_SOURCES_H__ + +#include <glib-object.h> + +#define EDS_DISABLE_DEPRECATED +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +#include <libedataserver/libedataserver.h> +#include <libecal/libecal.h> +G_GNUC_END_IGNORE_DEPRECATIONS + +G_BEGIN_DECLS + +#define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ()) +G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources, + CALENDAR, SOURCES, GObject) + +CalendarSources *calendar_sources_get (void); +ESourceRegistry *calendar_sources_get_registry (CalendarSources *sources); +GSList *calendar_sources_ref_clients (CalendarSources *sources); +gboolean calendar_sources_has_clients (CalendarSources *sources); + +void calendar_sources_connect_client (CalendarSources *sources, + ESource *source, + ECalClientSourceType source_type, + guint32 wait_for_connected_seconds, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +EClient *calendar_sources_connect_client_finish + (CalendarSources *sources, + GAsyncResult *result, + GError **error); + +/* Set the environment variable CALENDAR_SERVER_DEBUG to show debug */ +void print_debug (const gchar *str, + ...) G_GNUC_PRINTF (1, 2); + +G_END_DECLS + +#endif /* __CALENDAR_SOURCES_H__ */ diff --git a/src/calendar-server/evolution-calendar.desktop.in b/src/calendar-server/evolution-calendar.desktop.in new file mode 100644 index 0000000..1e34997 --- /dev/null +++ b/src/calendar-server/evolution-calendar.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=Evolution Calendar +Exec=evolution -c calendar +# Translators: Do NOT translate or transliterate this text (this is an icon file name)! +Icon=evolution +NoDisplay=true +Type=Application +StartupNotify=true diff --git a/src/calendar-server/gnome-shell-calendar-server.c b/src/calendar-server/gnome-shell-calendar-server.c new file mode 100644 index 0000000..4cd28d1 --- /dev/null +++ b/src/calendar-server/gnome-shell-calendar-server.c @@ -0,0 +1,1131 @@ +/* + * Copyright (C) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: David Zeuthen <davidz@redhat.com> + * + * Based on code from gnome-panel's clock-applet, file calendar-client.c, with Authors: + * + * Mark McLoughlin <mark@skynet.ie> + * William Jon McCann <mccann@jhu.edu> + * Martin Grimme <martin@pycage.de> + * Christian Kellner <gicmo@xatom.net> + * + */ + +#include "config.h" + +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include <gio/gio.h> + +#define HANDLE_LIBICAL_MEMORY +#define EDS_DISABLE_DEPRECATED +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +#include <libecal/libecal.h> +G_GNUC_END_IGNORE_DEPRECATIONS + +#include "calendar-sources.h" + +#define BUS_NAME "org.gnome.Shell.CalendarServer" + +static const gchar introspection_xml[] = + "<node>" + " <interface name='org.gnome.Shell.CalendarServer'>" + " <method name='SetTimeRange'>" + " <arg type='x' name='since' direction='in'/>" + " <arg type='x' name='until' direction='in'/>" + " <arg type='b' name='force_reload' direction='in'/>" + " </method>" + " <signal name='EventsAddedOrUpdated'>" + " <arg type='a(ssxxa{sv})' name='events' direction='out'/>" + " </signal>" + " <signal name='EventsRemoved'>" + " <arg type='as' name='ids' direction='out'/>" + " </signal>" + " <signal name='ClientDisappeared'>" + " <arg type='s' name='source_uid' direction='out'/>" + " </signal>" + " <property name='Since' type='x' access='read'/>" + " <property name='Until' type='x' access='read'/>" + " <property name='HasCalendars' type='b' access='read'/>" + " </interface>" + "</node>"; +static GDBusNodeInfo *introspection_data = NULL; + +struct _App; +typedef struct _App App; + +static gboolean opt_replace = FALSE; +static GOptionEntry opt_entries[] = { + {"replace", 0, 0, G_OPTION_ARG_NONE, &opt_replace, "Replace existing daemon", NULL}, + {NULL } +}; +static App *_global_app = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ + +/* While the UID is usually enough to identify an event, + * only the triple of (source,UID,RID) is fully unambiguous; + * neither may contain '\n', so we can safely use it to + * create a unique ID from the triple + */ +static gchar * +create_event_id (const gchar *source_uid, + const gchar *comp_uid, + const gchar *comp_rid) +{ + return g_strconcat ( + source_uid ? source_uid : "", + "\n", + comp_uid ? comp_uid : "", + "\n", + comp_rid ? comp_rid : "", + NULL); +} + +typedef struct +{ + ECalClient *client; + GSList **pappointments; /* CalendarAppointment * */ +} CollectAppointmentsData; + +typedef struct +{ + gchar *id; + gchar *summary; + time_t start_time; + time_t end_time; +} CalendarAppointment; + +static gboolean +get_time_from_property (ECalClient *cal, + ICalComponent *icomp, + ICalPropertyKind prop_kind, + ICalTime * (* get_prop_func) (ICalProperty *prop), + ICalTimezone *default_zone, + ICalTime **out_itt, + ICalTimezone **out_timezone) +{ + ICalProperty *prop; + ICalTime *itt; + ICalTimezone *timezone = NULL; + + prop = i_cal_component_get_first_property (icomp, prop_kind); + if (!prop) + return FALSE; + + itt = get_prop_func (prop); + + if (i_cal_time_is_utc (itt)) + timezone = i_cal_timezone_get_utc_timezone (); + else + { + ICalParameter *param; + + param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER); + if (param && !e_cal_client_get_timezone_sync (cal, i_cal_parameter_get_tzid (param), &timezone, NULL, NULL)) + print_debug ("Failed to get timezone '%s'\n", i_cal_parameter_get_tzid (param)); + + g_clear_object (¶m); + } + + if (timezone == NULL) + timezone = default_zone; + + i_cal_time_set_timezone (itt, timezone); + + g_clear_object (&prop); + + *out_itt = itt; + *out_timezone = timezone; + + return TRUE; +} + +static inline time_t +get_ical_start_time (ECalClient *cal, + ICalComponent *icomp, + ICalTimezone *default_zone) +{ + ICalTime *itt; + ICalTimezone *timezone; + time_t retval; + + if (!get_time_from_property (cal, + icomp, + I_CAL_DTSTART_PROPERTY, + i_cal_property_get_dtstart, + default_zone, + &itt, + &timezone)) + { + return 0; + } + + retval = i_cal_time_as_timet_with_zone (itt, timezone); + + g_clear_object (&itt); + + return retval; +} + +static inline time_t +get_ical_end_time (ECalClient *cal, + ICalComponent *icomp, + ICalTimezone *default_zone) +{ + ICalTime *itt; + ICalTimezone *timezone; + time_t retval; + + if (!get_time_from_property (cal, + icomp, + I_CAL_DTEND_PROPERTY, + i_cal_property_get_dtend, + default_zone, + &itt, + &timezone)) + { + if (!get_time_from_property (cal, + icomp, + I_CAL_DTSTART_PROPERTY, + i_cal_property_get_dtstart, + default_zone, + &itt, + &timezone)) + { + return 0; + } + + if (i_cal_time_is_date (itt)) + i_cal_time_adjust (itt, 1, 0, 0, 0); + } + + retval = i_cal_time_as_timet_with_zone (itt, timezone); + + g_clear_object (&itt); + + return retval; +} + +static CalendarAppointment * +calendar_appointment_new (ECalClient *cal, + ECalComponent *comp) +{ + CalendarAppointment *appt; + ICalTimezone *default_zone; + ICalComponent *ical; + ECalComponentId *id; + + default_zone = e_cal_client_get_default_timezone (cal); + ical = e_cal_component_get_icalcomponent (comp); + id = e_cal_component_get_id (comp); + + appt = g_new0 (CalendarAppointment, 1); + + appt->id = create_event_id (e_source_get_uid (e_client_get_source (E_CLIENT (cal))), + id ? e_cal_component_id_get_uid (id) : NULL, + id ? e_cal_component_id_get_rid (id) : NULL); + appt->summary = g_strdup (i_cal_component_get_summary (ical)); + appt->start_time = get_ical_start_time (cal, ical, default_zone); + appt->end_time = get_ical_end_time (cal, ical, default_zone); + + e_cal_component_id_free (id); + + return appt; +} + +static void +calendar_appointment_free (gpointer ptr) +{ + CalendarAppointment *appt = ptr; + + if (appt) + { + g_free (appt->id); + g_free (appt->summary); + g_free (appt); + } +} + +static time_t +timet_from_ical_time (ICalTime *time, + ICalTimezone *default_zone) +{ + ICalTimezone *timezone = NULL; + + timezone = i_cal_time_get_timezone (time); + if (timezone == NULL) + timezone = default_zone; + return i_cal_time_as_timet_with_zone (time, timezone); +} + +static gboolean +generate_instances_cb (ICalComponent *icomp, + ICalTime *instance_start, + ICalTime *instance_end, + gpointer user_data, + GCancellable *cancellable, + GError **error) +{ + CollectAppointmentsData *data = user_data; + CalendarAppointment *appointment; + ECalComponent *comp; + ICalTimezone *default_zone; + + default_zone = e_cal_client_get_default_timezone (data->client); + comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp)); + + appointment = calendar_appointment_new (data->client, comp); + appointment->start_time = timet_from_ical_time (instance_start, default_zone); + appointment->end_time = timet_from_ical_time (instance_end, default_zone); + + *(data->pappointments) = g_slist_prepend (*(data->pappointments), appointment); + + g_clear_object (&comp); + + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct _App +{ + GDBusConnection *connection; + + time_t since; + time_t until; + + ICalTimezone *zone; + + CalendarSources *sources; + gulong client_appeared_signal_id; + gulong client_disappeared_signal_id; + + gchar *timezone_location; + + GSList *notify_appointments; /* CalendarAppointment *, for EventsAdded */ + GSList *notify_ids; /* gchar *, for EventsRemoved */ + + GSList *live_views; +}; + +static void +app_update_timezone (App *app) +{ + g_autofree char *location = NULL; + + location = e_cal_system_timezone_get_location (); + if (g_strcmp0 (location, app->timezone_location) != 0) + { + if (location == NULL) + app->zone = i_cal_timezone_get_utc_timezone (); + else + app->zone = i_cal_timezone_get_builtin_timezone (location); + g_free (app->timezone_location); + app->timezone_location = g_steal_pointer (&location); + print_debug ("Using timezone %s", app->timezone_location); + } +} + +static void +app_notify_events_added (App *app) +{ + GVariantBuilder builder, extras_builder; + GSList *events, *link; + + events = g_slist_reverse (app->notify_appointments); + app->notify_appointments = NULL; + + print_debug ("Emitting EventsAddedOrUpdated with %d events", g_slist_length (events)); + + if (!events) + return; + + /* The a{sv} is used as an escape hatch in case we want to provide more + * information in the future without breaking ABI + */ + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssxxa{sv})")); + for (link = events; link; link = g_slist_next (link)) + { + CalendarAppointment *appt = link->data; + time_t start_time = appt->start_time; + time_t end_time = appt->end_time; + + if ((start_time >= app->since && + start_time < app->until) || + (start_time <= app->since && + (end_time - 1) > app->since)) + { + g_variant_builder_init (&extras_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, + "(ssxxa{sv})", + appt->id, + appt->summary != NULL ? appt->summary : "", + (gint64) start_time, + (gint64) end_time, + &extras_builder); + } + } + + g_dbus_connection_emit_signal (app->connection, + NULL, /* destination_bus_name */ + "/org/gnome/Shell/CalendarServer", + "org.gnome.Shell.CalendarServer", + "EventsAddedOrUpdated", + g_variant_new ("(a(ssxxa{sv}))", &builder), + NULL); + + g_variant_builder_clear (&builder); + + g_slist_free_full (events, calendar_appointment_free); +} + +static void +app_notify_events_removed (App *app) +{ + GVariantBuilder builder; + GSList *ids, *link; + + ids = app->notify_ids; + app->notify_ids = NULL; + + print_debug ("Emitting EventsRemoved with %d ids", g_slist_length (ids)); + + if (!ids) + return; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + for (link = ids; link; link = g_slist_next (link)) + { + const gchar *id = link->data; + + g_variant_builder_add (&builder, "s", id); + } + + g_dbus_connection_emit_signal (app->connection, + NULL, /* destination_bus_name */ + "/org/gnome/Shell/CalendarServer", + "org.gnome.Shell.CalendarServer", + "EventsRemoved", + g_variant_new ("(as)", &builder), + NULL); + g_variant_builder_clear (&builder); + + g_slist_free_full (ids, g_free); + + return; +} + +static void +app_process_added_modified_objects (App *app, + ECalClientView *view, + GSList *objects) /* ICalComponent * */ +{ + ECalClient *cal_client; + g_autoptr(GHashTable) covered_uids = NULL; + GSList *link; + gboolean expand_recurrences; + + cal_client = e_cal_client_view_ref_client (view); + covered_uids = g_hash_table_new (g_str_hash, g_str_equal); + expand_recurrences = e_cal_client_get_source_type (cal_client) == E_CAL_CLIENT_SOURCE_TYPE_EVENTS; + + for (link = objects; link; link = g_slist_next (link)) + { + ECalComponent *comp; + ICalComponent *icomp = link->data; + const gchar *uid; + gboolean fallback = FALSE; + + if (!icomp) + continue; + + uid = i_cal_component_get_uid (icomp); + if (!uid || g_hash_table_contains (covered_uids, uid)) + continue; + + g_hash_table_add (covered_uids, (gpointer) uid); + + if (expand_recurrences && + !e_cal_util_component_is_instance (icomp) && + e_cal_util_component_has_recurrences (icomp)) + { + CollectAppointmentsData data; + + data.client = cal_client; + data.pappointments = &app->notify_appointments; + + e_cal_client_generate_instances_for_object_sync (cal_client, icomp, app->since, app->until, NULL, + generate_instances_cb, &data); + } + else if (expand_recurrences && + e_cal_util_component_is_instance (icomp)) + { + ICalComponent *main_comp = NULL; + + /* Always pass whole series of the recurring events, because + * the calendar removes events with the same UID first. */ + if (e_cal_client_get_object_sync (cal_client, uid, NULL, &main_comp, NULL, NULL)) + { + CollectAppointmentsData data; + + data.client = cal_client; + data.pappointments = &app->notify_appointments; + + e_cal_client_generate_instances_for_object_sync (cal_client, main_comp, app->since, app->until, NULL, + generate_instances_cb, &data); + + g_clear_object (&main_comp); + } + else + { + fallback = TRUE; + } + } + else + { + fallback = TRUE; + } + + if (fallback) + { + comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp)); + if (!comp) + continue; + + app->notify_appointments = g_slist_prepend (app->notify_appointments, + calendar_appointment_new (cal_client, comp)); + g_object_unref (comp); + } + } + + g_clear_object (&cal_client); + + if (app->notify_appointments) + app_notify_events_added (app); +} + +static void +on_objects_added (ECalClientView *view, + GSList *objects, + gpointer user_data) +{ + App *app = user_data; + ECalClient *client; + + client = e_cal_client_view_ref_client (view); + print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (objects), e_source_get_uid (e_client_get_source (E_CLIENT (client)))); + g_clear_object (&client); + + app_process_added_modified_objects (app, view, objects); +} + +static void +on_objects_modified (ECalClientView *view, + GSList *objects, + gpointer user_data) +{ + App *app = user_data; + ECalClient *client; + + client = e_cal_client_view_ref_client (view); + print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (objects), e_source_get_uid (e_client_get_source (E_CLIENT (client)))); + g_clear_object (&client); + + app_process_added_modified_objects (app, view, objects); +} + +static void +on_objects_removed (ECalClientView *view, + GSList *uids, + gpointer user_data) +{ + App *app = user_data; + ECalClient *client; + GSList *link; + const gchar *source_uid; + + client = e_cal_client_view_ref_client (view); + source_uid = e_source_get_uid (e_client_get_source (E_CLIENT (client))); + + print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (uids), source_uid); + + for (link = uids; link; link = g_slist_next (link)) + { + ECalComponentId *id = link->data; + + if (!id) + continue; + + app->notify_ids = g_slist_prepend (app->notify_ids, + create_event_id (source_uid, + e_cal_component_id_get_uid (id), + e_cal_component_id_get_rid (id))); + } + + g_clear_object (&client); + + if (app->notify_ids) + app_notify_events_removed (app); +} + +static gboolean +app_has_calendars (App *app) +{ + return app->live_views != NULL; +} + +static ECalClientView * +app_start_view (App *app, + ECalClient *cal_client) +{ + g_autofree char *since_iso8601 = NULL; + g_autofree char *until_iso8601 = NULL; + g_autofree char *query = NULL; + const gchar *tz_location; + ECalClientView *view = NULL; + g_autoptr (GError) error = NULL; + + if (app->since <= 0 || app->since >= app->until) + return NULL; + + if (!app->since || !app->until) + { + print_debug ("Skipping load of events, no time interval set yet"); + return NULL; + } + + /* timezone could have changed */ + app_update_timezone (app); + + since_iso8601 = isodate_from_time_t (app->since); + until_iso8601 = isodate_from_time_t (app->until); + tz_location = i_cal_timezone_get_location (app->zone); + + print_debug ("Loading events since %s until %s for calendar '%s'", + since_iso8601, + until_iso8601, + e_source_get_uid (e_client_get_source (E_CLIENT (cal_client)))); + + query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") " + "(make-time \"%s\") \"%s\"", + since_iso8601, + until_iso8601, + tz_location); + + e_cal_client_set_default_timezone (cal_client, app->zone); + + if (!e_cal_client_get_view_sync (cal_client, query, &view, NULL /* cancellable */, &error)) + { + g_warning ("Error setting up live-query '%s' on calendar: %s\n", query, error ? error->message : "Unknown error"); + view = NULL; + } + else + { + g_signal_connect (view, + "objects-added", + G_CALLBACK (on_objects_added), + app); + g_signal_connect (view, + "objects-modified", + G_CALLBACK (on_objects_modified), + app); + g_signal_connect (view, + "objects-removed", + G_CALLBACK (on_objects_removed), + app); + e_cal_client_view_start (view, NULL); + } + + return view; +} + +static void +app_stop_view (App *app, + ECalClientView *view) +{ + e_cal_client_view_stop (view, NULL); + + g_signal_handlers_disconnect_by_func (view, on_objects_added, app); + g_signal_handlers_disconnect_by_func (view, on_objects_modified, app); + g_signal_handlers_disconnect_by_func (view, on_objects_removed, app); +} + +static void +app_notify_has_calendars (App *app) +{ + GVariantBuilder dict_builder; + + g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&dict_builder, "{sv}", "HasCalendars", + g_variant_new_boolean (app_has_calendars (app))); + + g_dbus_connection_emit_signal (app->connection, + NULL, + "/org/gnome/Shell/CalendarServer", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new ("(sa{sv}as)", + "org.gnome.Shell.CalendarServer", + &dict_builder, + NULL), + NULL); + g_variant_builder_clear (&dict_builder); +} + +static void +app_update_views (App *app) +{ + GSList *link, *clients; + gboolean had_views, has_views; + + had_views = app->live_views != NULL; + + for (link = app->live_views; link; link = g_slist_next (link)) + { + app_stop_view (app, link->data); + } + + g_slist_free_full (app->live_views, g_object_unref); + app->live_views = NULL; + + clients = calendar_sources_ref_clients (app->sources); + + for (link = clients; link; link = g_slist_next (link)) + { + ECalClient *cal_client = link->data; + ECalClientView *view; + + if (!cal_client) + continue; + + view = app_start_view (app, cal_client); + if (view) + app->live_views = g_slist_prepend (app->live_views, view); + } + + has_views = app->live_views != NULL; + + if (has_views != had_views) + app_notify_has_calendars (app); + + g_slist_free_full (clients, g_object_unref); +} + +static void +on_client_appeared_cb (CalendarSources *sources, + ECalClient *client, + gpointer user_data) +{ + App *app = user_data; + ECalClientView *view; + GSList *link; + const gchar *source_uid; + + source_uid = e_source_get_uid (e_client_get_source (E_CLIENT (client))); + + print_debug ("Client appeared '%s'", source_uid); + + for (link = app->live_views; link; link = g_slist_next (link)) + { + ECalClientView *view = link->data; + ECalClient *cal_client; + ESource *source; + + cal_client = e_cal_client_view_ref_client (view); + source = e_client_get_source (E_CLIENT (cal_client)); + + if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0) + { + g_clear_object (&cal_client); + return; + } + + g_clear_object (&cal_client); + } + + view = app_start_view (app, client); + + if (view) + { + app->live_views = g_slist_prepend (app->live_views, view); + + /* It's the first view, notify that it has calendars now */ + if (!g_slist_next (app->live_views)) + app_notify_has_calendars (app); + } +} + +static void +on_client_disappeared_cb (CalendarSources *sources, + const gchar *source_uid, + gpointer user_data) +{ + App *app = user_data; + GSList *link; + + print_debug ("Client disappeared '%s'", source_uid); + + for (link = app->live_views; link; link = g_slist_next (link)) + { + ECalClientView *view = link->data; + ECalClient *cal_client; + ESource *source; + + cal_client = e_cal_client_view_ref_client (view); + source = e_client_get_source (E_CLIENT (cal_client)); + + if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0) + { + g_clear_object (&cal_client); + app_stop_view (app, view); + app->live_views = g_slist_remove (app->live_views, view); + g_object_unref (view); + + print_debug ("Emitting ClientDisappeared for '%s'", source_uid); + + g_dbus_connection_emit_signal (app->connection, + NULL, /* destination_bus_name */ + "/org/gnome/Shell/CalendarServer", + "org.gnome.Shell.CalendarServer", + "ClientDisappeared", + g_variant_new ("(s)", source_uid), + NULL); + + /* It was the last view, notify that it doesn't have calendars now */ + if (!app->live_views) + app_notify_has_calendars (app); + + break; + } + + g_clear_object (&cal_client); + } +} + +static App * +app_new (GDBusConnection *connection) +{ + App *app; + + app = g_new0 (App, 1); + app->connection = g_object_ref (connection); + app->sources = calendar_sources_get (); + app->client_appeared_signal_id = g_signal_connect (app->sources, + "client-appeared", + G_CALLBACK (on_client_appeared_cb), + app); + app->client_disappeared_signal_id = g_signal_connect (app->sources, + "client-disappeared", + G_CALLBACK (on_client_disappeared_cb), + app); + + app_update_timezone (app); + + return app; +} + +static void +app_free (App *app) +{ + GSList *ll; + + for (ll = app->live_views; ll != NULL; ll = g_slist_next (ll)) + { + ECalClientView *view = E_CAL_CLIENT_VIEW (ll->data); + + app_stop_view (app, view); + } + + g_signal_handler_disconnect (app->sources, + app->client_appeared_signal_id); + g_signal_handler_disconnect (app->sources, + app->client_disappeared_signal_id); + + g_free (app->timezone_location); + + g_slist_free_full (app->live_views, g_object_unref); + g_slist_free_full (app->notify_appointments, calendar_appointment_free); + g_slist_free_full (app->notify_ids, g_free); + + g_object_unref (app->connection); + g_object_unref (app->sources); + + g_free (app); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +handle_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + App *app = user_data; + + if (g_strcmp0 (method_name, "SetTimeRange") == 0) + { + gint64 since; + gint64 until; + gboolean force_reload = FALSE; + gboolean window_changed = FALSE; + + g_variant_get (parameters, + "(xxb)", + &since, + &until, + &force_reload); + + if (until < since) + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gnome.Shell.CalendarServer.Error.Failed", + "until cannot be before since"); + goto out; + } + + print_debug ("Handling SetTimeRange (since=%" G_GINT64_FORMAT ", until=%" G_GINT64_FORMAT ", force_reload=%s)", + since, + until, + force_reload ? "true" : "false"); + + if (app->until != until || app->since != since) + { + GVariantBuilder *builder; + GVariantBuilder *invalidated_builder; + + app->until = until; + app->since = since; + window_changed = TRUE; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); + g_variant_builder_add (builder, "{sv}", + "Until", g_variant_new_int64 (app->until)); + g_variant_builder_add (builder, "{sv}", + "Since", g_variant_new_int64 (app->since)); + g_dbus_connection_emit_signal (app->connection, + NULL, /* destination_bus_name */ + "/org/gnome/Shell/CalendarServer", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new ("(sa{sv}as)", + "org.gnome.Shell.CalendarServer", + builder, + invalidated_builder), + NULL); /* GError** */ + + g_variant_builder_unref (builder); + g_variant_builder_unref (invalidated_builder); + } + + g_dbus_method_invocation_return_value (invocation, NULL); + + if (window_changed || force_reload) + app_update_views (app); + } + else + { + g_assert_not_reached (); + } + + out: + ; +} + +static GVariant * +handle_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + App *app = user_data; + GVariant *ret; + + ret = NULL; + if (g_strcmp0 (property_name, "Since") == 0) + { + ret = g_variant_new_int64 (app->since); + } + else if (g_strcmp0 (property_name, "Until") == 0) + { + ret = g_variant_new_int64 (app->until); + } + else if (g_strcmp0 (property_name, "HasCalendars") == 0) + { + ret = g_variant_new_boolean (app_has_calendars (app)); + } + else + { + g_assert_not_reached (); + } + return ret; +} + +static const GDBusInterfaceVTable interface_vtable = +{ + handle_method_call, + handle_get_property, + NULL /* handle_set_property */ +}; + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GMainLoop *main_loop = user_data; + guint registration_id; + g_autoptr (GError) error = NULL; + + _global_app = app_new (connection); + + registration_id = g_dbus_connection_register_object (connection, + "/org/gnome/Shell/CalendarServer", + introspection_data->interfaces[0], + &interface_vtable, + _global_app, + NULL, /* user_data_free_func */ + &error); + if (registration_id == 0) + { + g_printerr ("Error exporting object: %s (%s %d)\n", + error->message, + g_quark_to_string (error->domain), + error->code); + g_main_loop_quit (main_loop); + return; + } + + print_debug ("Connected to the session bus"); +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GMainLoop *main_loop = user_data; + + g_print ("gnome-shell-calendar-server[%d]: Lost (or failed to acquire) the name " BUS_NAME " - exiting\n", + (gint) getpid ()); + g_main_loop_quit (main_loop); +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + print_debug ("Acquired the name " BUS_NAME); +} + +static gboolean +stdin_channel_io_func (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + GMainLoop *main_loop = data; + + if (condition & G_IO_HUP) + { + g_debug ("gnome-shell-calendar-server[%d]: Got HUP on stdin - exiting\n", + (gint) getpid ()); + g_main_loop_quit (main_loop); + } + else + { + g_warning ("Unhandled condition %d on GIOChannel for stdin", condition); + } + return FALSE; /* remove source */ +} + +int +main (int argc, + char **argv) +{ + g_autoptr (GError) error = NULL; + GOptionContext *opt_context; + GMainLoop *main_loop; + gint ret; + guint name_owner_id; + GIOChannel *stdin_channel; + + ret = 1; + opt_context = NULL; + name_owner_id = 0; + stdin_channel = NULL; + + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + opt_context = g_option_context_new ("gnome-shell calendar server"); + g_option_context_add_main_entries (opt_context, opt_entries, NULL); + if (!g_option_context_parse (opt_context, &argc, &argv, &error)) + { + g_printerr ("Error parsing options: %s\n", error->message); + goto out; + } + + main_loop = g_main_loop_new (NULL, FALSE); + + stdin_channel = g_io_channel_unix_new (STDIN_FILENO); + g_io_add_watch_full (stdin_channel, + G_PRIORITY_DEFAULT, + G_IO_HUP, + stdin_channel_io_func, + g_main_loop_ref (main_loop), + (GDestroyNotify) g_main_loop_unref); + + name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0), + on_bus_acquired, + on_name_acquired, + on_name_lost, + g_main_loop_ref (main_loop), + (GDestroyNotify) g_main_loop_unref); + + g_main_loop_run (main_loop); + + g_main_loop_unref (main_loop); + + ret = 0; + + out: + if (stdin_channel != NULL) + g_io_channel_unref (stdin_channel); + if (_global_app != NULL) + app_free (_global_app); + if (name_owner_id != 0) + g_bus_unown_name (name_owner_id); + if (opt_context != NULL) + g_option_context_free (opt_context); + + return ret; +} diff --git a/src/calendar-server/meson.build b/src/calendar-server/meson.build new file mode 100644 index 0000000..8b4ef41 --- /dev/null +++ b/src/calendar-server/meson.build @@ -0,0 +1,37 @@ +calendar_sources = [ + 'gnome-shell-calendar-server.c', + 'calendar-debug.h', + 'calendar-sources.c', + 'calendar-sources.h' +] + +calendar_server = executable('gnome-shell-calendar-server', calendar_sources, + dependencies: [ecal_dep, eds_dep, gio_dep], + include_directories: include_directories('..', '../..'), + c_args: [ + '-DPREFIX="@0@"'.format(prefix), + '-DLIBDIR="@0@"'.format(libdir), + '-DDATADIR="@0@"'.format(datadir), + '-DG_LOG_DOMAIN="ShellCalendarServer"' + ], + install_dir: libexecdir, + install: true +) + +service_file = 'org.gnome.Shell.CalendarServer.service' + +configure_file( + input: service_file + '.in', + output: service_file, + configuration: service_data, + install_dir: servicedir +) + +i18n.merge_file( + input: 'evolution-calendar.desktop.in', + output: 'evolution-calendar.desktop', + po_dir: po_dir, + install: true, + install_dir: desktopdir, + type: 'desktop' +) diff --git a/src/calendar-server/org.gnome.Shell.CalendarServer.service.in b/src/calendar-server/org.gnome.Shell.CalendarServer.service.in new file mode 100644 index 0000000..5addce6 --- /dev/null +++ b/src/calendar-server/org.gnome.Shell.CalendarServer.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.gnome.Shell.CalendarServer +Exec=@libexecdir@/gnome-shell-calendar-server |