From 71a1d2e707a2b5dc68c00ae4d69747c3a2f6385e Mon Sep 17 00:00:00 2001 From: Rodrigo Moya Date: Mon, 12 Feb 2001 18:35:27 +0000 Subject: added the new DB3-based backend. This is just the beginning, there are 2001-02-11 Rodrigo Moya * pcs/cal-backend-db.[ch]: added the new DB3-based backend. This is just the beginning, there are some missing things still. svn path=/trunk/; revision=8182 --- calendar/AUTHORS | 1 + calendar/ChangeLog | 5 + calendar/pcs/cal-backend-db.c | 995 ++++++++++++++++++++++++++++++++++++++++++ calendar/pcs/cal-backend-db.h | 57 +++ 4 files changed, 1058 insertions(+) create mode 100644 calendar/pcs/cal-backend-db.c create mode 100644 calendar/pcs/cal-backend-db.h (limited to 'calendar') diff --git a/calendar/AUTHORS b/calendar/AUTHORS index 7e482aef97..0049fa248e 100644 --- a/calendar/AUTHORS +++ b/calendar/AUTHORS @@ -2,3 +2,4 @@ Miguel de Icaza Federico Mena Arturo Esponosa Russell Steinthal +Rodrigo Moya diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 57a653d0a3..aca8f9f9bd 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,8 @@ +2001-02-11 Rodrigo Moya + + * pcs/cal-backend-db.[ch]: added the new DB3-based backend. This is just + the beginning, there are some missing things still. + 2001-02-11 Gediminas Paulauskas Really use xml-i18n-tools. diff --git a/calendar/pcs/cal-backend-db.c b/calendar/pcs/cal-backend-db.c new file mode 100644 index 0000000000..0165164e5e --- /dev/null +++ b/calendar/pcs/cal-backend-db.c @@ -0,0 +1,995 @@ +/* Evolution calendar - iCalendar DB backend + * + * Copyright (C) 2001 Ximian, Inc. + * + * Author: Rodrigo Moya + * + * 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 +#include +#include "cal-util/cal-recur.h" +#include "cal-backend-db.h" +#include +#if DB_MAJOR_VERSION < 3 +# error "You need libdb3 to compile the DB backend" +#endif + +/* structure to identify an open cursor */ +typedef struct { + DBC* dbc; + DB* parent_db; + /* TODO: convert into a hash table */ + 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 *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 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 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 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->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_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); + + 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); + + /* 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) + return cursor; + + /* create the cursor */ + cursor = g_new0(CalBackendDBCursor, 1); + cursor->parent_db = db; + + ret = db->cursor(db, NULL, &cursor->dbc, 0); + if (ret == 0) { + DBT key; + DBT data; + + /* read 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))); + } + 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; + gint pos = 0; + + 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 = (DBT *) node->data; + if (key) { + if (!strcmp(key->data, id)) { + GList* tmp = g_list_nth(cursor->data, pos); + if (tmp) + return (DBT *) node->data; + } + } + pos++; + } + + return NULL; /* not found */ +} + +/* + * 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; + + 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); + + /* open/create objects database into given file */ + if (only_if_exists) { + ret = cbdb->priv->objects_db->open(cbdb->priv->objects_db, + str_uri, + "calendar_objects", + DB_HASH, + 0, + 0644); + } + else { + ret = cbdb->priv->objects_db->open(cbdb->priv->objects_db, + str_uri, + "calendar_objects", + DB_HASH, + DB_CREATE, + 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, + 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; + gint ret; + + 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 ((ret = db_create(&cbdb->priv->objects_db, NULL, 0)) != 0 + || (ret = db_create(&cbdb->priv->history_db, NULL, 0)) != 0) { + g_free((gpointer) str_uri); + return CAL_BACKEND_OPEN_ERROR; + } + 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; +} + +/* 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_COMPONENTS : + if (type & CALOBJ_TYPE_TODO) + total_count++; + break; + case ICAL_VJOURNAL_COMPONENT : + if (type & CALOBJ_TYPE_JOURNAL) + total_count++; + break; + } + icalcomponent_free(icalcomp); + } + } + } + + return total_count; +} + +/* get_object handler for the DB backend */ +static char * +cal_backend_db_get_object (CalBackend *backend, const char *uid) +{ + CalBackendDB *cbdb; + 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); + g_return_val_if_fail(uid != NULL, NULL); + + /* open cursor */ + cursor = open_cursor(cbdb, cbdb->priv->objects_db); + if (cursor) { + gint ret; + DBT *data; + + data = find_record_by_id(cursor, uid); + if (data) + return (char *) data->data; + } + + 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; + CalBackendDBCursor *cursor; + + 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); + + /* open the cursor */ + cursor = open_cursor(cbdb, cbdb->priv->objects_db); + if (cursor) { + DBT *data = find_record_by_id(cursor, uid); + if (data) { + 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; + + icalcomp = icalparser_parse_string(data->data); + if (!icalcomp) return list; + switch (icalcomponent_isa(icalcomp)) { + case ICAL_VEVENT_COMPONENT : + if (type & CALOBJ_TYPE_EVENT) + uid = icalcomponent_get_uid(icalcomp); + break; + case ICAL_VTODO_COMPONENT : + if (type & CALOBJ_TYPE_TODO) + uid = icalcomponent_get_uid(icalcomp); + break; + case ICAL_VJOURNAL_COMPONENT : + if (type & CALOBJ_TYPE_JOURNAL) + uid = 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); + } + } + + 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; + } + + gtk_object_unref(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); + } + + return list; +} + +/* 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; +} + +/* 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; + GList *alarm_list; + GList *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) { + /* TODO: get list of alarms */ + + /* 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); + + /* TODO: populate CORBA sequence */ + } + + g_list_free(alarm_list); + + 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; + + 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); + + /* open the cursor */ + cursor = open_cursor(cbdb, cbdb->priv->object_db); + if (cursor) { + /* TODO: retrieve list of alarms for this object */ + + /* create the CORBA alarms */ + corba_alarms = GNOME_Evolution_Calendar_CalComponentAlarms__alloc(); + + /* TODO: populate the CORBA alarms */ + } + + return corba_alarms; +} + +/* update_object handler for the DB backend */ +static gboolean +cal_backend_db_update_object (CalBackend *backend, const char *uid, const char *calobj) +{ + CalBackendDB *cbdb; + CalBackendDBCursor *cursor; + + 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); + g_return_val_if_fail(calobj != NULL, FALSE); + + /* open the cursor */ + cursor = open_cursor(cbdb, cbdb->priv->objects_db); + if (cursor) { + DBT *data; + + data = find_record_by_id(cursor, uid); + if (data) { + DBT key; + DBT new_data; + int ret; + + /* try to change the value in the cursor */ + 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 + + /* move the cursor to the given key */ + if ((ret = cursor->dbc->c_get(cursor->dbc, &key, NULL, DB_FIRST)) != 0) { + return FALSE; + } + + /* TODO: start transaction */ + + if ((ret = cursor->dbc->c_put(cursor->dbc, &key, &new_data, DB_CURRENT)) != 0) { + /* TODO: rollback transaction */ + return FALSE; + } + + /* TODO: update history database */ + /* TODO: commit transaction */ + + memcpy(data, &new_data, sizeof(new_data)); + + return TRUE; + } + } + + return FALSE; +} + +/* remove_object handler for the DB backend */ +static gboolean +cal_backend_db_remove_object (CalBackend *backend, const char *uid) +{ + CalBackendDB *cbdb; + CalBackendDBCursor *cursor; + + 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); + + /* open cursor */ + cursor = open_cursor(cbdb, cbdb->priv->objects_db); + if (cursor) { + DBT *data = find_record_by_id(cursor, uid); + if (data) { + GList *l; + int ret; + DBT key; + + memset(&key, 0, sizeof(key); + key.data = (void *) uid; + key.size = strlen(uid); // + 1 + + if ((ret = cursor->dbc->c_get(cursor->dbc, &key, NULL, DB_FIRST)) != 0) { + return FALSE; + } + + /* TODO: begin transaction */ + + /* remove record from cursor */ + if ((ret = cursor->dbc->c_del(cursor->dbc, 0)) != 0) { + /* TODO: rollback transaction */ + return FALSE; + } + + /* TODO: update history database */ + /* TODO: commit transaction */ + + /* remove record from in-memory lists */ + l = g_list_nth(cursor->keys, + g_list_index(cursor->data, (gpointer) data)); + if (l) { + DBT *key_to_free = (DBT *) l->data; + + cursor->keys = g_list_remove(cursor->keys, (gpointer) key_to_free); + g_free((gpointer) key_to_free); + + cursor->data = g_list_remove(cursor->data, (gpointer) data); + g_free((gpointer) data); + } + + return TRUE; + } + } + + return FALSE; +} diff --git a/calendar/pcs/cal-backend-db.h b/calendar/pcs/cal-backend-db.h new file mode 100644 index 0000000000..c73dbb5a83 --- /dev/null +++ b/calendar/pcs/cal-backend-db.h @@ -0,0 +1,57 @@ +/* Evolution calendar - iCalendar DB backend + * + * Copyright (C) 2001 Ximian, Inc. + * + * Author: Rodrigo Moya + * + * 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. + */ + +#ifndef CAL_BACKEND_DB_H +#define CAL_BACKEND_DB_H + +#include "cal-backend.h" + +BEGIN_GNOME_DECLS + +#define CAL_BACKEND_DB_TYPE (cal_backend_db_get_type ()) +#define CAL_BACKEND_DB(obj) (GTK_CHECK_CAST ((obj), CAL_BACKEND_DB_TYPE, \ + CalBackendDB)) +#define CAL_BACKEND_DB_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), CAL_BACKEND_DB_TYPE, \ + CalBackendDBClass)) +#define IS_CAL_BACKEND_DB(obj) (GTK_CHECK_TYPE ((obj), CAL_BACKEND_DB_TYPE)) +#define IS_CAL_BACKEND_DB_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), CAL_BACKEND_DB_TYPE)) + +typedef struct _CalBackendDB CalBackendDB; +typedef struct _CalBackendDBClass CalBackendDBClass; + +typedef struct _CalBackendDBPrivate CalBackendDBPrivate; + +struct _CalBackendDB { + CalBackend backend; + + /* Private data */ + CalBackendDBPrivate *priv; +}; + +struct _CalBackendDBClass { + CalBackendClass parent_class; +}; + +GtkType cal_backend_db_get_type (void); + +END_GNOME_DECLS + +#endif -- cgit