diff options
Diffstat (limited to 'calendar/pcs/cal-backend-db.c')
-rw-r--r-- | calendar/pcs/cal-backend-db.c | 1521 |
1 files changed, 0 insertions, 1521 deletions
diff --git a/calendar/pcs/cal-backend-db.c b/calendar/pcs/cal-backend-db.c deleted file mode 100644 index 73aef5d054..0000000000 --- a/calendar/pcs/cal-backend-db.c +++ /dev/null @@ -1,1521 +0,0 @@ -/* Evolution calendar - iCalendar DB backend - * - * Copyright (C) 2001 Ximian, Inc. - * - * Author: Rodrigo Moya <rodrigo@ximian.com> - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <config.h> -#include <gtk/gtksignal.h> -#include "cal-util/cal-recur.h" -#include "cal-backend-db.h" -#include <db.h> -#if DB_VERSION_MAJOR < 3 -# error "You need libdb3 to compile the DB backend" -#endif - -#define ENVIRONMENT_DIRECTORY "%s/evolution/local/Calendar/db.environment" - -/* structure to identify an open cursor */ -typedef struct { - gint ref; - DBC* dbc; - DB* parent_db; - - /* data in the cursor */ - GList *keys; - GList *data; -} CalBackendDBCursor; - -/* private part of the CalBackendDB structure */ -struct _CalBackendDBPrivate { - /* URI where the calendar data is stored */ - GnomeVFSURI *uri; - - /* Berkeley DB's library handles */ - DB_ENV *environment; - DB *objects_db; - DB *history_db; - - /* list of open cursors */ - GList *cursors; - - /* list of clients using this backend */ - GList *clients; -}; - -static void cal_backend_db_class_init (CalBackendDBClass *klass); -static void cal_backend_db_init (CalBackendDB *cbdb); -static void cal_backend_db_destroy (GtkObject *object); - -static GnomeVFSURI *cal_backend_db_get_uri (CalBackend *backend); -static void cal_backend_db_add_cal (CalBackend *backend, Cal *cal); -static CalBackendOpenStatus cal_backend_db_open (CalBackend *backend, - GnomeVFSURI *uri, - gboolean only_if_exists); -static gboolean cal_backend_db_is_loaded (CalBackend *backend); - -static int cal_backend_db_get_n_objects (CalBackend *backend, CalObjType type); -static char *cal_backend_db_get_object (CalBackend *backend, const char *uid); -static CalObjType cal_backend_db_get_type_by_uid (CalBackend *backend, const char *uid); -static GList* cal_backend_db_get_uids (CalBackend *backend, CalObjType type); -static GList* cal_backend_db_get_objects_in_range (CalBackend *backend, - CalObjType type, - time_t start, - time_t end); -static char *cal_backend_db_get_free_busy (CalBackend *backend, - time_t start, - time_t end); -static GNOME_Evolution_Calendar_CalObjChangeSeq *cal_backend_db_get_changes ( - CalBackend *backend, CalObjType type, const char *change_id); - -static GNOME_Evolution_Calendar_CalComponentAlarmsSeq *cal_backend_db_get_alarms_in_range ( - CalBackend *backend, time_t start, time_t end); - -static GNOME_Evolution_Calendar_CalComponentAlarms *cal_backend_db_get_alarms_for_object ( - CalBackend *backend, const char *uid, time_t start, time_t end, gboolean *object_found); - -static gboolean cal_backend_db_update_object (CalBackend *backend, - const char *uid, - const char *calobj); -static gboolean cal_backend_db_remove_object (CalBackend *backend, const char *uid); - -static void close_cursor (CalBackendDB *cbdb, CalBackendDBCursor *cursor); -static CalBackendDBCursor *open_cursor (CalBackendDB *cbdb, DB *db); -static CalBackendDBCursor *find_cursor_by_db (CalBackendDB *cbdb, DB *db); -static DBT *find_record_by_id (CalBackendDBCursor *cursor, const gchar *id); - -static DB_TXN *begin_transaction (CalBackendDB *cbdb); -static void commit_transaction (DB_TXN *tid); -static void rollback_transaction (DB_TXN *tid); - -static CalBackendClass *parent_class; - -/** - * cal_backend_db_get_type: - * @void: - * - * Registers the #CalBackendDB class if necessary and returns the type ID - * associated to it. - * - * Return value: The type ID of the #CalBackendDB class. - */ -GtkType -cal_backend_db_get_type (void) -{ - static GtkType cal_backend_db_type = 0; - - if (!cal_backend_db_type) { - static const GtkTypeInfo cal_backend_db_info = { - "CalBackendDB", - sizeof (CalBackendDB), - sizeof (CalBackendDBClass), - (GtkClassInitFunc) cal_backend_db_class_init, - (GtkObjectInitFunc) cal_backend_db_init, - NULL, - NULL, - (GtkClassInitFunc) NULL - }; - - cal_backend_db_type = gtk_type_unique(CAL_BACKEND_TYPE, &cal_backend_db_info); - } - - return cal_backend_db_type; -} - -/* class initialization function for the DB backend */ -static void -cal_backend_db_class_init (CalBackendDBClass *klass) -{ - GtkObjectClass *object_class; - CalBackendClass *backend_class; - - object_class = (GtkObjectClass *) klass; - backend_class = (CalBackendClass *) klass; - - parent_class = gtk_type_class(CAL_BACKEND_TYPE); - - object_class->destroy = cal_backend_db_destroy; - - backend_class->get_uri = cal_backend_db_get_uri; - backend_class->add_cal = cal_backend_db_add_cal; - backend_class->open = cal_backend_db_open; - backend_class->is_loaded = cal_backend_db_is_loaded; - backend_class->get_n_objects = cal_backend_db_get_n_objects; - backend_class->get_object = cal_backend_db_get_object; - backend_class->get_type_by_uid = cal_backend_db_get_type_by_uid; - backend_class->get_uids = cal_backend_db_get_uids; - backend_class->get_objects_in_range = cal_backend_db_get_objects_in_range; - backend_class->get_free_busy = cal_backend_db_get_free_busy; - backend_class->get_changes = cal_backend_db_get_changes; - backend_class->get_alarms_in_range = cal_backend_db_get_alarms_in_range; - backend_class->get_alarms_for_object = cal_backend_db_get_alarms_for_object; - backend_class->update_object = cal_backend_db_update_object; - backend_class->remove_object = cal_backend_db_remove_object; -} - -/* object initialization function for the DB backend */ -static void -cal_backend_db_init (CalBackendDB *cbdb) -{ - CalBackendDBPrivate *priv; - - priv = g_new0(CalBackendDBPrivate, 1); - cbdb->priv = priv; -} - -/* Destroy handler for the DB backend */ -static void -cal_backend_db_destroy (GtkObject *object) -{ - CalBackendDB *cbdb; - CalBackendDBPrivate *priv; - GList *node; - - g_return_if_fail(object != NULL); - g_return_if_fail(IS_CAL_BACKEND_DB(object)); - - cbdb = CAL_BACKEND_DB(object); - priv = cbdb->priv; - - g_assert(cbdb->priv->clients == NULL); - - /* clean up */ - if (priv->uri) { - gnome_vfs_uri_unref(priv->uri); - priv->uri = NULL; - } - - /* close open cursors */ - while ((node = g_list_first(cbdb->priv->cursors))) { - close_cursor(cbdb, (CalBackendDBCursor *) node->data); - } - - /* close open databases */ - if (cbdb->priv->objects_db) - cbdb->priv->objects_db->close(cbdb->priv->objects_db, 0); - if (cbdb->priv->history_db) - cbdb->priv->history_db->close(cbdb->priv->history_db, 0); - - /* close DB environment */ - if (cbdb->priv->environment) - cbdb->priv->environment->close(cbdb->priv->environment, 0); - - g_free((gpointer) priv); - cbdb->priv = NULL; - - if (GTK_OBJECT_CLASS(parent_class)->destroy) - (*GTK_OBJECT_CLASS(parent_class)->destroy)(object); -} - -/* - * Private functions - */ - -/* close an open cursor and frees all associated memory */ -static void -close_cursor (CalBackendDB *cbdb, CalBackendDBCursor *cursor) -{ - GList *node; - DBT *dbt; - - g_return_if_fail(cursor != NULL); - - cursor->ref--; - if (cursor->ref > 0) - return; - - /* free all keys and data */ - while ((node = g_list_first(cursor->keys))) { - dbt = (DBT *) node->data; - cursor->keys = g_list_remove(cursor->keys, (gpointer) dbt); - g_free((gpointer) dbt); - } - while ((node = g_list_first(cursor->data))) { - dbt = (DBT *) node->data; - cursor->data = g_list_remove(cursor->data, (gpointer) dbt); - g_free((gpointer) dbt); - } - - /* finally, close the cursor */ - cursor->dbc->c_close(cursor->dbc); - - cbdb->priv->cursors = g_list_remove(cbdb->priv->cursors, (gpointer) cursor); - g_free((gpointer) cursor); -} - -/* open a cursor for the given database */ -static CalBackendDBCursor * -open_cursor (CalBackendDB *cbdb, DB *db) -{ - CalBackendDBCursor *cursor; - gint ret; - - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(db != NULL, NULL); - - /* search for the cursor in our list of cursors */ - cursor = find_cursor_by_db(cbdb, db); - if (cursor) { - cursor->ref++; - return cursor; - } - - /* create the cursor */ - cursor = g_new0(CalBackendDBCursor, 1); - cursor->parent_db = db; - cursor->ref = 1; - - ret = db->cursor(db, NULL, &cursor->dbc, 0); - if (ret == 0) { - DBT key; - DBT data; - - /* read data */ - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - - while ((ret = cursor->dbc->c_get(cursor->dbc, &key, &data, DB_NEXT)) == 0) { - cursor->keys = g_list_append(cursor->keys, g_memdup(&key, sizeof(key))); - cursor->data = g_list_append(cursor->data, g_memdup(&data, sizeof(data))); - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - } - if (ret == DB_NOTFOUND) { - cbdb->priv->cursors = g_list_prepend(cbdb->priv->cursors, (gpointer) cursor); - return cursor; - } - - /* close cursor on error */ - close_cursor(cbdb, cursor); - } - - return NULL; -} - -/* search for a cursor in the given backend */ -static CalBackendDBCursor * -find_cursor_by_db (CalBackendDB *cbdb, DB *db) -{ - GList *node; - - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - g_return_val_if_fail(db != NULL, NULL); - - for (node = g_list_first(cbdb->priv->cursors); node != NULL; node = g_list_next(node)) { - CalBackendDBCursor* cursor = (CalBackendDBCursor *) node->data; - - if (cursor && cursor->parent_db == db) - return cursor; - } - - return NULL; /* not found */ -} - -/* finds a record in a cursor by its ID */ -static DBT * -find_record_by_id (CalBackendDBCursor *cursor, const gchar *id) -{ - GList *node; - - g_return_val_if_fail(cursor != NULL, NULL); - g_return_val_if_fail(id != NULL, NULL); - - - for (node = g_list_first(cursor->keys); node != NULL; node = g_list_next(node)) { - DBT *key; - - key = (DBT *) node->data; - if (key && !strcmp(key->data, id)) { - GList *tmp; - - tmp = g_list_nth(cursor->data, g_list_position(cursor->keys, node)); - if (tmp) - return (DBT *) tmp->data; - return NULL; /* no data associated with this key */ - } - } - - return NULL; /* not found */ -} - -/* start a new transaction */ -static DB_TXN * -begin_transaction (CalBackendDB *cbdb) -{ - DB_TXN *tid; - gint ret; - - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - - if ((ret = txn_begin(cbdb->priv->environment, NULL, &tid, 0)) != 0) { - /* TODO: error logging */ - return NULL; - } - - return tid; -} - -/* finish successfully a transaction */ -static void -commit_transaction (DB_TXN *tid) -{ - gint ret; - - g_return_if_fail(tid != NULL); - - if ((ret = txn_commit(tid, 0)) != 0) { - /* TODO: error logging? */ - } -} - -/* abort a transaction */ -static void -rollback_transaction (DB_TXN *tid) -{ - gint ret; - - g_return_if_fail(tid != NULL); - - if ((ret = txn_abort(tid)) != 0) { - /* TODO: error logging? */ - } -} - -/* - * Calendar backend methods - */ - -/* get_uri handler for the DB backend */ -static GnomeVFSURI * -cal_backend_db_get_uri (CalBackend *backend) -{ - CalBackendDB *cbdb; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - - return cbdb->priv->uri; -} - -/* callback used when a Cal is destroyed */ -static void -destroy_cal_cb (GtkObject *object, gpointer data) -{ - Cal *cal; - Cal *tmp_cal; - CalBackendDB *cbdb; - GList *node; - - cal = CAL(object); - cbdb = CAL_BACKEND_DB(data); - - g_return_if_fail(IS_CAL_BACKEND_DB(cbdb)); - g_return_if_fail(cbdb->priv != NULL); - - /* find the Cal in the list of clients */ - for (node = cbdb->priv->clients; node != NULL; node = g_list_next(node)) { - tmp_cal = CAL(node->data); - if (tmp_cal == cal) - break; - } - - if (node) { - /* disconnect this Cal */ - cbdb->priv->clients = g_list_remove_link(cbdb->priv->clients, node); - g_list_free_1(node); - - /* when all clients go away, notify the parent factory about it so that - * it may decide to kill the backend or not. - */ - if (!cbdb->priv->clients) - cal_backend_last_client_gone(CAL_BACKEND(cbdb)); - } -} - -/* add_cal_handler for the DB backend */ -static void -cal_backend_db_add_cal (CalBackend *backend, Cal *cal) -{ - CalBackendDB *cbdb; - - cbdb = CAL_BACKEND_DB(backend); - g_return_if_fail(IS_CAL_BACKEND_DB(cbdb)); - g_return_if_fail(cbdb->priv != NULL); - g_return_if_fail(IS_CAL(cal)); - - /* we do not keep a reference to the Cal since the calendar user agent - * owns it - */ - gtk_signal_connect(GTK_OBJECT(cal), - "destroy", - GTK_SIGNAL_FUNC(destroy_cal_cb), - backend); - - cbdb->priv->clients = g_list_prepend(cbdb->priv->clients, (gpointer) cal); -} - -/* database file initialization */ -static gboolean -open_database_file (CalBackendDB *cbdb, const gchar *str_uri, gboolean only_if_exists) -{ - gint ret; - struct stat sb; - gchar *dir; - - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), FALSE); - g_return_val_if_fail(cbdb->priv != NULL, FALSE); - g_return_val_if_fail(cbdb->priv->objects_db != NULL, FALSE); - g_return_val_if_fail(cbdb->priv->history_db != NULL, FALSE); - g_return_val_if_fail(str_uri != NULL, FALSE); - - /* initialize DB environment (for transactions) */ - dir = g_strdup_printf(ENVIRONMENT_DIRECTORY, g_get_home_dir()); - if (stat(dir, &sb) != 0) { - - /* if the directory exists, we're done, since DB will fail if it's the - * wrong one. If it does not exist, create the environment */ - if (mkdir(dir, S_IRWXU) != 0) { - g_free((gpointer) dir); - return FALSE; - } - - /* create the environment handle */ - if ((ret = db_env_create(&cbdb->priv->environment, 0)) != 0) { - g_free((gpointer) dir); - return FALSE; - } - - cbdb->priv->environment->set_errpfx(cbdb->priv->environment, "cal-backend-db"); - - /* open the transactional environment */ - if ((ret = cbdb->priv->environment->open(cbdb->priv->environment, - ENVIRONMENT_DIRECTORY, - DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | - DB_INIT_MPOOL | DB_INIT_TXN | - DB_RECOVER | DB_THREAD, - S_IRUSR | S_IWUSR)) != 0) { - g_free((gpointer) dir); - return FALSE; - } - } - - g_free((gpointer) dir); - - /* open/create objects database into given file */ - if ((ret = db_create(&cbdb->priv->objects_db, cbdb->priv->environment, 0)) != 0 - || (ret = db_create(&cbdb->priv->history_db, cbdb->priv->environment, 0)) != 0) { - return FALSE; - } - - if (only_if_exists) { - ret = cbdb->priv->objects_db->open(cbdb->priv->objects_db, - str_uri, - "calendar_objects", - DB_HASH, - DB_THREAD, - 0644); - } - else { - ret = cbdb->priv->objects_db->open(cbdb->priv->objects_db, - str_uri, - "calendar_objects", - DB_HASH, - DB_CREATE | DB_THREAD, - 0644); - } - if (ret == 0) { - /* now, open the history database */ - ret = cbdb->priv->history_db->open(cbdb->priv->history_db, - str_uri, - "calendar_history", - DB_BTREE, - DB_CREATE | DB_THREAD, - 0644); - if (ret == 0) return TRUE; - - /* close objects database on error */ - cbdb->priv->objects_db->close(cbdb->priv->objects_db, 0); - } - - return FALSE; -} - -/* open handler for the DB backend */ -static CalBackendOpenStatus -cal_backend_db_open (CalBackend *backend, GnomeVFSURI *uri, gboolean only_if_exists) -{ - CalBackendDB *cbdb; - gchar *str_uri; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), CAL_BACKEND_OPEN_ERROR); - g_return_val_if_fail(cbdb->priv != NULL, CAL_BACKEND_OPEN_ERROR); - g_return_val_if_fail(uri != NULL, CAL_BACKEND_OPEN_ERROR); - g_return_val_if_fail(cbdb->priv->objects_db == NULL, CAL_BACKEND_OPEN_ERROR); - - /* open the given URI */ - if (!gnome_vfs_uri_is_local(uri)) - return CAL_BACKEND_OPEN_ERROR; - str_uri = gnome_vfs_uri_to_string(uri, - (GNOME_VFS_URI_HIDE_USER_NAME - | GNOME_VFS_URI_HIDE_PASSWORD - | GNOME_VFS_URI_HIDE_HOST_NAME - | GNOME_VFS_URI_HIDE_HOST_PORT - | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); - - /* open database file */ - if (!open_database_file(cbdb, (const gchar *) str_uri, only_if_exists)) { - g_free((gpointer) str_uri); - return CAL_BACKEND_OPEN_ERROR; - } - - gnome_vfs_uri_ref(uri); - cbdb->priv->uri = uri; - g_free((gpointer) str_uri); - - return CAL_BACKEND_OPEN_SUCCESS; -} - -/* is_loaded handler for the DB backend */ -static gboolean -cal_backend_db_is_loaded (CalBackend *backend) -{ - CalBackendDB *cbdb; - - cbdb = CAL_BACKEND_DB (backend); - - return (cbdb->priv->uri != NULL); -} - -/* get_n_objects handler for the DB backend */ -static int -cal_backend_db_get_n_objects (CalBackend *backend, CalObjType type) -{ - CalBackendDB *cbdb; - CalBackendDBCursor *cursor; - int total_count = 0; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), -1); - g_return_val_if_fail(cbdb->priv != NULL, -1); - - /* open the cursor */ - cursor = open_cursor(cbdb, cbdb->priv->objects_db); - if (cursor) { - GList *node; - - /* we traverse all data, to check for each object's type */ - for (node = g_list_first(cursor->data); node != NULL; node = g_list_next(node)) { - icalcomponent *icalcomp; - DBT *data = (DBT *) node->data; - - icalcomp = icalparser_parse_string((char *) data->data); - if (icalcomp) { - switch (icalcomponent_isa(icalcomp)) { - case ICAL_VEVENT_COMPONENT : - if (type & CALOBJ_TYPE_EVENT) - total_count++; - break; - case ICAL_VTODO_COMPONENT : - if (type & CALOBJ_TYPE_TODO) - total_count++; - break; - case ICAL_VJOURNAL_COMPONENT : - if (type & CALOBJ_TYPE_JOURNAL) - total_count++; - break; - default : - break; - } - icalcomponent_free(icalcomp); - } - } - close_cursor(cbdb, cursor); - } - - return total_count; -} - -/* get_object handler for the DB backend */ -static char * -cal_backend_db_get_object (CalBackend *backend, const char *uid) -{ - CalBackendDB *cbdb; - gint ret; - DBT key; - DBT data; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - g_return_val_if_fail(cbdb->priv->objects_db != NULL, NULL); - g_return_val_if_fail(uid != NULL, NULL); - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - key.data = (void *) uid; - key.size = strlen(uid); // + 1 - - /* read record from database */ - if ((ret = cbdb->priv->objects_db->get(cbdb->priv->objects_db, - NULL, - &key, - &data, - 0)) == 0) { - gchar *str = g_strdup((gchar *) data.data); - return str; - } - - return NULL; -} - -/* get_type_by_uid handler for the DB backend */ -static CalObjType -cal_backend_db_get_type_by_uid (CalBackend *backend, const char *uid) -{ - CalBackendDB *cbdb; - DBT key; - DBT data; - gint ret; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), CAL_COMPONENT_NO_TYPE); - g_return_val_if_fail(cbdb->priv != NULL, CAL_COMPONENT_NO_TYPE); - g_return_val_if_fail(cbdb->priv->objects_db != NULL, CAL_COMPONENT_NO_TYPE); - g_return_val_if_fail(uid != NULL, CAL_COMPONENT_NO_TYPE); - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - key.data = (void *) uid; - key.size = strlen(uid); // + 1 - - /* read record from database */ - if ((ret = cbdb->priv->objects_db->get(cbdb->priv->objects_db, - NULL, - &key, - &data, - 0)) == 0) { - icalcomponent *icalcomp = icalparser_parse_string((char *) data.data); - if (icalcomp) { - CalObjType type; - - switch (icalcomponent_isa(icalcomp)) { - case ICAL_VEVENT_COMPONENT : - type = CALOBJ_TYPE_EVENT; - break; - case ICAL_VTODO_COMPONENT : - type = CALOBJ_TYPE_TODO; - break; - case ICAL_VJOURNAL_COMPONENT : - type = CALOBJ_TYPE_JOURNAL; - break; - default : - type = CAL_COMPONENT_NO_TYPE; - } - - icalcomponent_free(icalcomp); - return type; - } - } - - return CAL_COMPONENT_NO_TYPE; -} - -static GList * -add_uid_if_match (GList *list, CalBackendDBCursor *cursor, GList *data_node, CalObjType type) -{ - DBT *data; - - g_return_val_if_fail(cursor != NULL, list); - g_return_val_if_fail(data_node != NULL, list); - - data = (DBT *) data_node->data; - if (data) { - icalcomponent *icalcomp; - gchar *uid = NULL; - - icalcomp = icalparser_parse_string(data->data); - if (!icalcomp) return list; - - switch (icalcomponent_isa(icalcomp)) { - case ICAL_VEVENT_COMPONENT : - if (type & CALOBJ_TYPE_EVENT) - uid = (gchar *) icalcomponent_get_uid(icalcomp); - break; - case ICAL_VTODO_COMPONENT : - if (type & CALOBJ_TYPE_TODO) - uid = (gchar *) icalcomponent_get_uid(icalcomp); - break; - case ICAL_VJOURNAL_COMPONENT : - if (type & CALOBJ_TYPE_JOURNAL) - uid = (gchar *) icalcomponent_get_uid(icalcomp); - break; - default : - uid = NULL; - } - - if (uid) - list = g_list_prepend(list, g_strdup(uid)); - icalcomponent_free(icalcomp); - } - - return list; -} - -/* get_uids handler for the DB backend */ -static GList * -cal_backend_db_get_uids (CalBackend *backend, CalObjType type) -{ - CalBackendDB *cbdb; - GList *list = NULL; - CalBackendDBCursor *cursor; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - g_return_val_if_fail(cbdb->priv->objects_db != NULL, NULL); - - /* open cursor */ - cursor = open_cursor(cbdb, cbdb->priv->objects_db); - if (cursor) { - GList *node; - - /* we traverse all data, to check for each object's type */ - for (node = g_list_first(cursor->data); node != NULL; node = g_list_next(node)) { - list = add_uid_if_match(list, cursor, node, type); - } - close_cursor(cbdb, cursor); - } - - return list; -} - -/* callback used from cal_recur_generate_instances(): adds the component's UID to - * our hash table - */ -static gboolean -add_instance (CalComponent *comp, time_t start, time_t end, gpointer data) -{ - GHashTable *uid_hash; - const char *uid; - const char *old_uid; - - uid_hash = data; - - cal_component_get_uid(comp, &uid); - - old_uid = g_hash_table_lookup(uid_hash, uid); - if (old_uid) - return FALSE; - - g_hash_table_insert(uid_hash, (char *) uid, NULL); - return FALSE; -} - -/* creates the list of UIDs in the given range */ -static void -get_instances_in_range (GHashTable *uid_hash, - CalBackendDBCursor *cursor, - CalObjType type, - time_t start, - time_t end) -{ - GList *node; - - g_return_if_fail(uid_hash != NULL); - g_return_if_fail(cursor != NULL); - - for (node = g_list_first(cursor->data); node != NULL; node = g_list_next(node)) { - DBT *data; - icalcomponent *icalcomp; - - data = (DBT *) node->data; - if (data) { - icalcomp = icalparser_parse_string((char *) data->data); - if (icalcomp) { - CalComponent *comp = cal_component_new(); - cal_component_set_icalcomponent(comp, icalcomp); - - switch (icalcomponent_isa(icalcomp)) { - case ICAL_VEVENT_COMPONENT : - if (type & CALOBJ_TYPE_EVENT) - cal_recur_generate_instances(comp, - start, - end, - add_instance, - uid_hash); - break; - case ICAL_VTODO_COMPONENT : - if (type & CALOBJ_TYPE_TODO) - cal_recur_generate_instances(comp, - start, - end, - add_instance, - uid_hash); - break; - case ICAL_VJOURNAL_COMPONENT : - if (type & CALOBJ_TYPE_JOURNAL) - cal_recur_generate_instances(comp, - start, - end, - add_instance, - uid_hash); - break; - default : - break; - } - - gtk_object_unref(GTK_OBJECT(comp)); - icalcomponent_free(icalcomp); - } - } - } -} - -/* callback used from g_hash_table_foreach: adds a UID from the hash table to our list */ -static void -add_uid_to_list (gpointer key, gpointer value, gpointer data) -{ - GList **list; - const char *uid; - - list = (GList **) data; - - uid = (const char *) key; - *list = g_list_prepend(*list, (gpointer) g_strdup(uid)); -} - -/* get_objects_in_range handler for the DB backend */ -static GList * -cal_backend_db_get_objects_in_range (CalBackend *backend, - CalObjType type, - time_t start, - time_t end) -{ - CalBackendDB *cbdb; - GList *list = NULL; - CalBackendDBCursor *cursor; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - - /* open cursor */ - cursor = open_cursor(cbdb, cbdb->priv->objects_db); - if (cursor) { - GHashTable *uid_hash; - - /* build the hash table */ - uid_hash = g_hash_table_new(g_str_hash, g_str_equal); - get_instances_in_range(uid_hash, cursor, type, start, end); - - /* build the list to be returned from the hash table */ - g_hash_table_foreach(uid_hash, add_uid_to_list, &list); - g_hash_table_destroy(uid_hash); - - close_cursor(cbdb, cursor); - } - - return list; -} - -/* get_free_busy handler for the DB backend */ -static char * -cal_backend_db_get_free_busy (CalBackend *backend, time_t start, time_t end) -{ - return NULL; -} - -/* get_changes handler for the DB backend */ -static GNOME_Evolution_Calendar_CalObjChangeSeq * -cal_backend_db_get_changes (CalBackend *backend, CalObjType type, const char *change_id) -{ - CalBackendDB *cbdb; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - - return NULL; -} - -/* computes the range of time in which recurrences should be generated for a - * component in order to compute alarm trigger times. - */ -static void -compute_alarm_range (CalComponent *comp, - GList *alarm_uids, - time_t start, - time_t end, - time_t *alarm_start, - time_t *alarm_end) -{ - GList *l; - - *alarm_start = start; - *alarm_end = end; - - for (l = alarm_uids; l; l = l->next) { - const char *auid; - CalComponentAlarm *alarm; - CalAlarmTrigger trigger; - struct icaldurationtype *dur; - time_t dur_time; - - auid = l->data; - alarm = cal_component_get_alarm (comp, auid); - g_assert (alarm != NULL); - - cal_component_alarm_get_trigger (alarm, &trigger); - cal_component_alarm_free (alarm); - - switch (trigger.type) { - case CAL_ALARM_TRIGGER_NONE: - case CAL_ALARM_TRIGGER_ABSOLUTE: - continue; - case CAL_ALARM_TRIGGER_RELATIVE_START: - case CAL_ALARM_TRIGGER_RELATIVE_END: - dur = &trigger.u.rel_duration; - dur_time = icaldurationtype_as_int (*dur); - - if (dur->is_neg) - /* If the duration is negative then dur_time - * will be negative as well; that is why we - * subtract to expand the range. - */ - *alarm_end = MAX (*alarm_end, end - dur_time); - else - *alarm_start = MIN (*alarm_start, start - dur_time); - - break; - default: - g_assert_not_reached (); - } - } - - g_assert (*alarm_start <= *alarm_end); -} - -/* closure data to generate alarm occurrences */ -struct alarm_occurrence_data { - /* these are the info we have */ - GList *alarm_uids; - time_t start; - time_t end; - - /* this is what we compute */ - GSList *triggers; - int n_triggers; -}; - -/* callback used from cal_recur_generate_instances(); generates triggers for all - * of a component's RELATIVE alarms. - */ -static gboolean -add_alarm_occurrences_cb (CalComponent *comp, time_t start, time_t end, gpointer data) -{ - struct alarm_occurrence_data *aod; - GList *l; - - aod = data; - - for (l = aod->alarm_uids; l; l = l->next) { - const char *auid; - CalComponentAlarm *alarm; - CalAlarmTrigger trigger; - struct icaldurationtype *dur; - time_t dur_time; - time_t occur_time, trigger_time; - CalAlarmInstance *instance; - - auid = l->data; - alarm = cal_component_get_alarm (comp, auid); - g_assert (alarm != NULL); - - cal_component_alarm_get_trigger (alarm, &trigger); - cal_component_alarm_free (alarm); - - if (trigger.type != CAL_ALARM_TRIGGER_RELATIVE_START - && trigger.type != CAL_ALARM_TRIGGER_RELATIVE_END) - continue; - - dur = &trigger.u.rel_duration; - dur_time = icaldurationtype_as_int (*dur); - - if (trigger.type == CAL_ALARM_TRIGGER_RELATIVE_START) - occur_time = start; - else - occur_time = end; - - /* If dur->is_neg is true then dur_time will already be - * negative. So we do not need to test for dur->is_neg here; we - * can simply add the dur_time value to the occur_time and get - * the correct result. - */ - - trigger_time = occur_time + dur_time; - - if (trigger_time < aod->start || trigger_time >= aod->end) - continue; - - instance = g_new (CalAlarmInstance, 1); - instance->auid = auid; - instance->trigger = trigger_time; - instance->occur = occur_time; - - aod->triggers = g_slist_prepend (aod->triggers, instance); - aod->n_triggers++; - } - - return TRUE; -} - -/* generates the absolute triggers for a component */ -static void -generate_absolute_triggers (CalComponent *comp, struct alarm_occurrence_data *aod) -{ - GList *l; - - for (l = aod->alarm_uids; l; l = l->next) { - const char *auid; - CalComponentAlarm *alarm; - CalAlarmTrigger trigger; - time_t abs_time; - CalAlarmInstance *instance; - - auid = l->data; - alarm = cal_component_get_alarm (comp, auid); - g_assert (alarm != NULL); - - cal_component_alarm_get_trigger (alarm, &trigger); - cal_component_alarm_free (alarm); - - if (trigger.type != CAL_ALARM_TRIGGER_ABSOLUTE) - continue; - - abs_time = icaltime_as_timet (trigger.u.abs_time); - - if (abs_time < aod->start || abs_time >= aod->end) - continue; - - instance = g_new (CalAlarmInstance, 1); - instance->auid = auid; - instance->trigger = abs_time; - instance->occur = abs_time; /* No particular occurrence, so just use the same time */ - - aod->triggers = g_slist_prepend (aod->triggers, instance); - aod->n_triggers++; - } -} - -/* compares two alarm instances; called from g_slist_sort() */ -static gint -compare_alarm_instance (gconstpointer a, gconstpointer b) -{ - const CalAlarmInstance *aia, *aib; - - aia = a; - aib = b; - - if (aia->trigger < aib->trigger) - return -1; - else if (aia->trigger > aib->trigger) - return 1; - else - return 0; -} - -/* generates alarm instances for a calendar component. Returns the instances - * structure, or NULL if no alarm instances occurred in the specified time - * range. - */ -static CalComponentAlarms * -generate_alarms_for_comp (CalComponent *comp, time_t start, time_t end) -{ - CalComponentAlarms *alarms = NULL; - GList *alarm_uids; - time_t alarm_start, alarm_end; - struct alarm_occurrence_data aod; - - g_return_val_if_fail(IS_CAL_COMPONENT(comp), NULL); - - if (!cal_component_has_alarms(comp)) - return NULL; - - alarm_uids = cal_component_get_alarm_uids(comp); - compute_alarm_range(comp, alarm_uids, start, end, &alarm_start, &alarm_end); - - aod.alarm_uids = alarm_uids; - aod.start = start; - aod.end = end; - aod.triggers = NULL; - aod.n_triggers = 0; - cal_recur_generate_instances(comp, alarm_start, alarm_end, add_alarm_occurrences_cb, &aod); - - /* we add the ABSOLUTE triggers separately */ - generate_absolute_triggers(comp, &aod); - - if (aod.n_triggers == 0) - return NULL; - - /* create the component alarm instances structure */ - alarms = g_new (CalComponentAlarms, 1); - alarms->comp = comp; - gtk_object_ref (GTK_OBJECT (alarms->comp)); - alarms->alarms = g_slist_sort (aod.triggers, compare_alarm_instance); - - return alarms; -} - -/* retrieve list of alarms */ -static GSList * -get_list_of_alarms (CalBackendDBCursor *cursor, time_t start, time_t end) -{ - GSList *list = NULL; - GList *node; - - g_return_val_if_fail(cursor != NULL, NULL); - - for (node = g_list_first(cursor->data); node != NULL; node = g_list_next(node)) { - icalcomponent *icalcomp; - icalcomponent_kind kind; - DBT *data; - - data = (DBT *) node->data; - if (data) { - icalcomp = icalparser_parse_string((char *) data->data); - if (icalcomp) { - /* per RFC 2445, only VEVENTs and VTODOs can have alarms */ - kind = icalcomponent_isa(icalcomp); - if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) { - CalComponent *comp; - CalComponentAlarms *alarms; - - /* create the CalComponent to compute the alarms */ - comp = cal_component_new(); - cal_component_set_icalcomponent(comp, icalcomp); - - alarms = generate_alarms_for_comp(comp, start, end); - if (alarms) - list = g_slist_prepend(list, (gpointer) alarms); - - gtk_object_unref(GTK_OBJECT(comp)); - } - - icalcomponent_free(icalcomp); - } - } - } - - return list; -} - -/* fills a CORBA sequence of alarm instances */ -static void -fill_alarm_instances_seq (GNOME_Evolution_Calendar_CalAlarmInstanceSeq *seq, GSList *alarms) -{ - int n_alarms; - GSList *l; - int i; - - n_alarms = g_slist_length(alarms); - - CORBA_sequence_set_release(seq, TRUE); - seq->_length = n_alarms; - seq->_buffer = CORBA_sequence_GNOME_Evolution_Calendar_CalAlarmInstance_allocbuf(n_alarms); - - for (l = alarms, i = 0; l != NULL; l = l->next, i++) { - CalAlarmInstance *instance; - GNOME_Evolution_Calendar_CalAlarmInstance *corba_instance; - - instance = (CalAlarmInstance *) l->data; - corba_instance = seq->_buffer + i; - - corba_instance->auid = CORBA_string_dup(instance->auid); - corba_instance->trigger = (long) instance->trigger; - corba_instance->occur = (long) instance->occur; - } -} - -/* get_alarms_in_range handler for the DB backend */ -static GNOME_Evolution_Calendar_CalComponentAlarmsSeq * -cal_backend_db_get_alarms_in_range (CalBackend *backend, time_t start, time_t end) -{ - CalBackendDB *cbdb; - CalBackendDBCursor *cursor; - gint number_of_alarms; - GSList *alarm_list; - GSList *node; - gint i; - GNOME_Evolution_Calendar_CalComponentAlarmsSeq *seq = NULL; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - g_return_val_if_fail (start != -1 && end != -1, NULL); - g_return_val_if_fail (start <= end, NULL); - - /* open cursor */ - cursor = open_cursor(cbdb, cbdb->priv->objects_db); - if (cursor) { - alarm_list = get_list_of_alarms(cursor, start, end); - number_of_alarms = g_slist_length(alarm_list); - - /* create the CORBA sequence */ - seq = GNOME_Evolution_Calendar_CalComponentAlarmsSeq__alloc(); - CORBA_sequence_set_release(seq, TRUE); - seq->_length = number_of_alarms; - seq->_buffer = CORBA_sequence_GNOME_Evolution_Calendar_CalComponentAlarms_allocbuf( - number_of_alarms); - - /* populate CORBA sequence */ - for (node = alarm_list, i = 0; node != NULL; node = node->next, i++) { - CalComponentAlarms *alarms; - gchar *comp_str; - - alarms = (CalComponentAlarms *) node->data; - - comp_str = cal_component_get_as_string (alarms->comp); - seq->_buffer[i].calobj = CORBA_string_dup(comp_str); - g_free((gpointer) comp_str); - - fill_alarm_instances_seq(&seq->_buffer[i].alarms, alarms->alarms); - - cal_component_alarms_free(alarms); - } - - g_slist_free(alarm_list); - close_cursor(cbdb, cursor); - } - - return seq; -} - -/* get_alarms_for_object handler for the DB backend */ -static GNOME_Evolution_Calendar_CalComponentAlarms * -cal_backend_db_get_alarms_for_object (CalBackend *backend, - const char *uid, - time_t start, - time_t end, - gboolean *object_found) -{ - CalBackendDB *cbdb; - CalBackendDBCursor *cursor; - GNOME_Evolution_Calendar_CalComponentAlarms *corba_alarms = NULL; - DBT *data; - gchar *comp_str; - CalComponentAlarms *alarms; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), NULL); - g_return_val_if_fail(cbdb->priv != NULL, NULL); - g_return_val_if_fail(uid != NULL, NULL); - g_return_val_if_fail(start != -1 && end != -1, NULL); - g_return_val_if_fail(start <= end, NULL); - g_return_val_if_fail(object_found != NULL, NULL); - - *object_found = FALSE; - - /* open the cursor */ - cursor = open_cursor(cbdb, cbdb->priv->objects_db); - if (cursor) { - data = find_record_by_id(cursor, uid); - if (data) { - icalcomponent *icalcomp; - CalComponent *comp; - - *object_found = TRUE; - - comp_str = (gchar *) data->data; - - icalcomp = icalparser_parse_string(comp_str); - if (!icalcomp) { - close_cursor(cbdb, cursor); - return NULL; - } - - comp = cal_component_new(); - cal_component_set_icalcomponent(comp, icalcomp); - - /* create the CORBA alarms */ - corba_alarms = GNOME_Evolution_Calendar_CalComponentAlarms__alloc(); - corba_alarms->calobj = CORBA_string_dup (comp_str); - - /* populate the CORBA sequence */ - alarms = generate_alarms_for_comp(comp, start, end); - if (alarms) { - fill_alarm_instances_seq(&corba_alarms->alarms, alarms->alarms); - cal_component_alarms_free(alarms); - } - else - fill_alarm_instances_seq(&corba_alarms->alarms, NULL); - - gtk_object_unref(GTK_OBJECT(comp)); - icalcomponent_free(icalcomp); - } - - close_cursor(cbdb, cursor); - } - - return corba_alarms; -} - -/* do notifications to Cal clients */ -static void -do_notify (CalBackendDB *cbdb, void (*notify_fn)(Cal *, gchar *), const gchar *uid) -{ - GList *node; - - g_return_if_fail(IS_CAL_BACKEND_DB(cbdb)); - g_return_if_fail(cbdb->priv != NULL); - g_return_if_fail(notify_fn != NULL); - g_return_if_fail(uid != NULL); - - /* FIXME: do notification asynchronously */ - for (node = g_list_first(cbdb->priv->clients); node != NULL; node = g_list_next(node)) { - Cal *cal; - - cal = CAL(node->data); - (*notify_fn)(cal, (gpointer) uid); - } -} - -/* adds a record to the history database */ -static gboolean -add_history (CalBackendDB *cbdb, DB_TXN *tid, const gchar *uid, const gchar *calobj) -{ - DBT key; - DBT new_data; - gint ret; - - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), FALSE); - g_return_val_if_fail(uid != NULL, FALSE); - g_return_val_if_fail(calobj != NULL, FALSE); - - /* fill in DBT structures */ - memset(&key, 0, sizeof(key)); - key.data = (void *) uid; - key.size = strlen(uid); // + 1 - - memset(&new_data, 0, sizeof(new_data)); - new_data.data = (void *) calobj; - new_data.size = strlen(calobj); // + 1 - - /* add the new record to the database */ - if ((ret = cbdb->priv->history_db->put(cbdb->priv->objects_db, - tid, - &key, - &new_data, - 0)) != 0) { - return FALSE; - } - - return TRUE; -} - -/* update_object handler for the DB backend */ -static gboolean -cal_backend_db_update_object (CalBackend *backend, const char *uid, const char *calobj) -{ - CalBackendDB *cbdb; - DB_TXN *tid; - DBT key; - DBT new_data; - gint ret; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), FALSE); - g_return_val_if_fail(cbdb->priv != NULL, FALSE); - g_return_val_if_fail(cbdb->priv->objects_db != NULL, FALSE); - g_return_val_if_fail(uid != NULL, FALSE); - g_return_val_if_fail(calobj != NULL, FALSE); - - /* start transaction */ - tid = begin_transaction(cbdb); - if (!tid) - return FALSE; - - memset(&key, 0, sizeof(key)); - key.data = (void *) uid; - key.size = strlen(uid); // + 1 - - memset(&new_data, 0, sizeof(new_data)); - new_data.data = (void *) calobj; - new_data.size = strlen(calobj); // + 1 - - if ((ret = cbdb->priv->objects_db->put(cbdb->priv->objects_db, - tid, - &key, - &new_data, - 0)) != 0) { - rollback_transaction(tid); - return FALSE; - } - - if (!add_history(cbdb, tid, uid, calobj)) { - rollback_transaction(tid); - return FALSE; - } - commit_transaction(tid); - - cal_backend_obj_updated (CAL_BACKEND (cbdb), uid); - do_notify(cbdb, cal_notify_update, uid); - return TRUE; -} - -/* remove_object handler for the DB backend */ -static gboolean -cal_backend_db_remove_object (CalBackend *backend, const char *uid) -{ - CalBackendDB *cbdb; - DB_TXN *tid; - DBT key; - gint ret; - - cbdb = CAL_BACKEND_DB(backend); - g_return_val_if_fail(IS_CAL_BACKEND_DB(cbdb), FALSE); - g_return_val_if_fail(cbdb->priv != NULL, FALSE); - g_return_val_if_fail(uid != NULL, FALSE); - - memset(&key, 0, sizeof(key)); - key.data = (void *) uid; - key.size = strlen(uid); // + 1 - - /* start transaction */ - tid = begin_transaction(cbdb); - if (!tid) - return FALSE; - - /* remove record from database */ - if ((ret = cbdb->priv->objects_db->del(cbdb->priv->objects_db, tid, &key, 0)) != 0) { - rollback_transaction(tid); - return FALSE; - } - - /* TODO: update history database */ - commit_transaction(tid); - - cal_backend_obj_removed (CAL_BACKEND (cbdb), uid); - do_notify(cbdb, cal_notify_remove, uid); - - return TRUE; -} |