gnome-control-center/archiver/archive.c
Bradford Hovinen 1d03bbd1c5 1.5.7
2001-08-29  Bradford Hovinen  <hovinen@ximian.com>

        * RELEASE : 1.5.7

        * archive.c (foreach_build_list_cb): Don't unref the location if
        it is deleted
        (load_all_locations): Check if the location is marked deleted and
        throw it away if so

        * location.c (location_new): Don't unref parent location
2001-09-04 13:43:48 +00:00

781 lines
19 KiB
C

/* -*- mode: c; style: linux -*- */
/* archive.c
* Copyright (C) 2000-2001 Ximian, Inc.
*
* Written by Bradford Hovinen (hovinen@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, 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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include "archive.h"
#include "util.h"
static GtkObjectClass *parent_class;
enum {
ARG_0,
ARG_PREFIX,
ARG_IS_GLOBAL
};
#define ARCHIVE_FROM_SERVANT(servant) (ARCHIVE (bonobo_object_from_servant (servant)))
static void archive_init (Archive *archive);
static void archive_class_init (ArchiveClass *klass);
static void archive_destroy (GtkObject *object);
static void archive_set_arg (GtkObject *object,
GtkArg *arg,
guint arg_id);
static void archive_get_arg (GtkObject *object,
GtkArg *arg,
guint arg_id);
static void load_all_locations (Archive *archive);
/* CORBA interface methods */
static ConfigArchiver_Location
impl_ConfigArchiver_Archive_getLocation (PortableServer_Servant servant,
const CORBA_char *locid,
CORBA_Environment *ev)
{
Location *loc;
loc = archive_get_location (ARCHIVE_FROM_SERVANT (servant), locid);
if (loc == NULL) {
bonobo_exception_set (ev, ex_ConfigArchiver_Archive_LocationNotFound);
return CORBA_OBJECT_NIL;
} else {
return CORBA_Object_duplicate (BONOBO_OBJREF (loc), ev);
}
}
static ConfigArchiver_Location
impl_ConfigArchiver_Archive_createLocation (PortableServer_Servant servant,
const CORBA_char *locid,
const CORBA_char *label,
const ConfigArchiver_Location parent_ref,
CORBA_Environment *ev)
{
Location *loc;
loc = archive_create_location (ARCHIVE_FROM_SERVANT (servant), locid, label,
LOCATION (bonobo_object_from_servant (parent_ref->servant)));
return CORBA_Object_duplicate (BONOBO_OBJREF (loc), ev);
}
static ConfigArchiver_LocationSeq *
impl_ConfigArchiver_Archive_getChildLocations (PortableServer_Servant servant,
ConfigArchiver_Location location_ref,
CORBA_Environment *ev)
{
ConfigArchiver_LocationSeq *ret;
Archive *archive;
Location *location;
GList *locs, *tmp;
guint i = 0;
archive = ARCHIVE_FROM_SERVANT (servant);
if (location_ref == CORBA_OBJECT_NIL)
location = NULL;
else
location = LOCATION (bonobo_object_from_servant (location_ref->servant));
locs = archive_get_child_locations (archive, location);
ret = ConfigArchiver_LocationSeq__alloc ();
ret->_length = g_list_length (locs);
ret->_buffer = CORBA_sequence_ConfigArchiver_Location_allocbuf (ret->_length);
for (tmp = locs; tmp != NULL; tmp = tmp->next)
ret->_buffer[i++] = CORBA_Object_duplicate (BONOBO_OBJREF (tmp->data), ev);
g_list_free (locs);
return ret;
}
static CORBA_char *
impl_ConfigArchiver_Archive__get_prefix (PortableServer_Servant servant,
CORBA_Environment *ev)
{
return CORBA_string_dup (ARCHIVE_FROM_SERVANT (servant)->prefix);
}
static CORBA_boolean
impl_ConfigArchiver_Archive__get_isGlobal (PortableServer_Servant servant,
CORBA_Environment *ev)
{
return ARCHIVE_FROM_SERVANT (servant)->is_global;
}
static ConfigArchiver_BackendList
impl_ConfigArchiver_Archive__get_backendList (PortableServer_Servant servant,
CORBA_Environment *ev)
{
return bonobo_object_dup_ref (BONOBO_OBJREF (ARCHIVE_FROM_SERVANT (servant)->backend_list), ev);
}
static ConfigArchiver_Location
impl_ConfigArchiver_Archive__get_currentLocation (PortableServer_Servant servant,
CORBA_Environment *ev)
{
return CORBA_Object_duplicate (BONOBO_OBJREF (archive_get_current_location (ARCHIVE_FROM_SERVANT (servant))), ev);
}
static void
impl_ConfigArchiver_Archive__set_currentLocation (PortableServer_Servant servant,
ConfigArchiver_Location location_ref,
CORBA_Environment *ev)
{
Archive *archive = ARCHIVE_FROM_SERVANT (servant);
Location *location;
location = LOCATION (bonobo_object_from_servant (location_ref->servant));
if (location == NULL)
bonobo_exception_set (ev, ex_ConfigArchiver_Archive_LocationNotFound);
else
archive_set_current_location (archive, location);
}
static void
impl_ConfigArchiver_Archive__set_currentLocationId (PortableServer_Servant servant,
const CORBA_char *locid,
CORBA_Environment *ev)
{
archive_set_current_location_id (ARCHIVE_FROM_SERVANT (servant), locid);
}
CORBA_char *
impl_ConfigArchiver_Archive__get_currentLocationId (PortableServer_Servant servant,
CORBA_Environment *ev)
{
return CORBA_string_dup (archive_get_current_location_id (ARCHIVE_FROM_SERVANT (servant)));
}
BONOBO_X_TYPE_FUNC_FULL (Archive, ConfigArchiver_Archive, BONOBO_X_OBJECT_TYPE, archive);
static void
archive_init (Archive *archive)
{
archive->prefix = NULL;
archive->locations = g_tree_new ((GCompareFunc) strcmp);
archive->current_location_id = NULL;
}
static void
archive_class_init (ArchiveClass *klass)
{
GtkObjectClass *object_class;
object_class = GTK_OBJECT_CLASS (klass);
object_class->destroy = archive_destroy;
object_class->set_arg = archive_set_arg;
object_class->get_arg = archive_get_arg;
gtk_object_add_arg_type ("Archive::prefix",
GTK_TYPE_POINTER,
GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT_ONLY,
ARG_PREFIX);
gtk_object_add_arg_type ("Archive::is-global",
GTK_TYPE_INT,
GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT_ONLY,
ARG_IS_GLOBAL);
klass->epv.getLocation = impl_ConfigArchiver_Archive_getLocation;
klass->epv.createLocation = impl_ConfigArchiver_Archive_createLocation;
klass->epv.getChildLocations = impl_ConfigArchiver_Archive_getChildLocations;
klass->epv._get_prefix = impl_ConfigArchiver_Archive__get_prefix;
klass->epv._get_isGlobal = impl_ConfigArchiver_Archive__get_isGlobal;
klass->epv._get_backendList = impl_ConfigArchiver_Archive__get_backendList;
klass->epv._get_currentLocation = impl_ConfigArchiver_Archive__get_currentLocation;
klass->epv._get_currentLocationId = impl_ConfigArchiver_Archive__get_currentLocationId;
klass->epv._set_currentLocation = impl_ConfigArchiver_Archive__set_currentLocation;
klass->epv._set_currentLocationId = impl_ConfigArchiver_Archive__set_currentLocationId;
parent_class = gtk_type_class (BONOBO_X_OBJECT_TYPE);
}
static void
archive_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
Archive *archive;
g_return_if_fail (object != NULL);
g_return_if_fail (IS_ARCHIVE (object));
g_return_if_fail (arg != NULL);
archive = ARCHIVE (object);
switch (arg_id) {
case ARG_PREFIX:
if (GTK_VALUE_POINTER (*arg) != NULL)
archive->prefix = g_strdup (GTK_VALUE_POINTER (*arg));
break;
case ARG_IS_GLOBAL:
archive->is_global = GTK_VALUE_INT (*arg);
archive->backend_list =
BACKEND_LIST (backend_list_new (archive->is_global));
break;
default:
break;
}
}
static void
archive_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
Archive *archive;
g_return_if_fail (object != NULL);
g_return_if_fail (IS_ARCHIVE (object));
g_return_if_fail (arg != NULL);
archive = ARCHIVE (object);
switch (arg_id) {
case ARG_PREFIX:
GTK_VALUE_POINTER (*arg) = archive->prefix;
break;
case ARG_IS_GLOBAL:
GTK_VALUE_INT (*arg) = archive->is_global;
break;
default:
arg->type = GTK_TYPE_INVALID;
break;
}
}
/**
* archive_construct:
* @archive:
* @is_new: TRUE iff this is a new archive
*
* Load the archive information from disk
*
* Returns: TRUE on success and FALSE on failure
*/
gboolean
archive_construct (Archive *archive, gboolean is_new)
{
gint ret = 0;
g_return_val_if_fail (archive != NULL, FALSE);
g_return_val_if_fail (IS_ARCHIVE (archive), FALSE);
g_return_val_if_fail (archive->prefix != NULL, FALSE);
if (is_new) {
if (g_file_exists (archive->prefix))
return FALSE;
ret = mkdir (archive->prefix, S_IREAD | S_IWRITE | S_IEXEC);
if (ret == -1) return FALSE;
} else {
if (!g_file_test (archive->prefix, G_FILE_TEST_ISDIR))
return FALSE;
}
return TRUE;
}
/**
* archive_load:
* @is_global: TRUE iff we should load the global archive
*
* Load either the global or per-user configuration archive
*
* Return value: Reference to archive
**/
BonoboObject *
archive_load (gboolean is_global)
{
BonoboObject *object;
gchar *prefix;
if (is_global)
prefix = "/var/ximian-setup-tools";
else
prefix = g_concat_dir_and_file (g_get_home_dir (),
".gnome/capplet-archive");
object = BONOBO_OBJECT (gtk_object_new (archive_get_type (),
"prefix", prefix,
"is-global", is_global,
NULL));
if (!is_global)
g_free (prefix);
if (archive_construct (ARCHIVE (object), FALSE) == FALSE &&
archive_construct (ARCHIVE (object), TRUE) == FALSE)
{
bonobo_object_unref (object);
return NULL;
}
return object;
}
static gint
free_location_cb (gchar *locid, BonoboObject *location)
{
bonobo_object_unref (location);
g_free (locid);
return FALSE;
}
static void
archive_destroy (GtkObject *object)
{
Archive *archive;
g_return_if_fail (object != NULL);
g_return_if_fail (IS_ARCHIVE (object));
DEBUG_MSG ("Enter");
archive = ARCHIVE (object);
g_tree_traverse (archive->locations,
(GTraverseFunc) free_location_cb,
G_IN_ORDER,
NULL);
g_tree_destroy (archive->locations);
if (archive->current_location_id != NULL)
g_free (archive->current_location_id);
if (archive->backend_list != NULL)
bonobo_object_unref (BONOBO_OBJECT (archive->backend_list));
GTK_OBJECT_CLASS (parent_class)->destroy (GTK_OBJECT (archive));
}
/**
* archive_get_location:
* @locid:
*
* Get a reference to the location with the given name.
*
* Return value: Reference to location, NULL if no such location exists
**/
Location *
archive_get_location (Archive *archive,
const gchar *locid)
{
BonoboObject *loc_obj;
gchar *tmp;
g_return_val_if_fail (archive != NULL, NULL);
g_return_val_if_fail (IS_ARCHIVE (archive), NULL);
g_return_val_if_fail (locid != NULL, NULL);
/* Stupid borken glib... */
tmp = g_strdup (locid);
loc_obj = g_tree_lookup (archive->locations, tmp);
g_free (tmp);
if (loc_obj == NULL) {
loc_obj = location_open (archive, locid);
if (loc_obj == NULL)
return NULL;
else
g_tree_insert (archive->locations,
g_strdup (locid), loc_obj);
} else {
bonobo_object_ref (loc_obj);
}
return LOCATION (loc_obj);
}
/**
* archive_create_location:
* @archive:
* @location:
*
* Creates a new location
*/
Location *
archive_create_location (Archive *archive,
const gchar *locid,
const gchar *label,
Location *parent)
{
Location *location;
g_return_val_if_fail (archive != NULL, NULL);
g_return_val_if_fail (IS_ARCHIVE (archive), NULL);
g_return_val_if_fail (locid != NULL, NULL);
g_return_val_if_fail (label != NULL, NULL);
g_return_val_if_fail (parent == NULL || IS_LOCATION (parent), NULL);
location = LOCATION (location_new (archive, locid, label, parent));
if (location == NULL)
return NULL;
g_tree_insert (archive->locations, g_strdup (locid), location);
return location;
}
/**
* archive_unregister_location:
* @archive:
* @location:
*
* Unregisters a location from the archive
**/
void
archive_unregister_location (Archive *archive, Location *location)
{
gchar *tmp;
g_return_if_fail (archive != NULL);
g_return_if_fail (IS_ARCHIVE (archive));
g_return_if_fail (location != NULL);
g_return_if_fail (IS_LOCATION (location));
if (GTK_OBJECT_DESTROYED (archive)) return;
tmp = g_strdup (location_get_id (location));
g_tree_remove (archive->locations, tmp);
g_free (tmp);
}
/**
* archive_get_current_location:
*
* Convenience function to get a pointer to the current location
*
* Return value: Pointer to current location, or NULL if the current location
* does not exist and a default location could not be created
**/
Location *
archive_get_current_location (Archive *archive)
{
const gchar *locid;
g_return_val_if_fail (archive != NULL, NULL);
g_return_val_if_fail (IS_ARCHIVE (archive), NULL);
locid = archive_get_current_location_id (archive);
if (locid == NULL)
return NULL;
else
return archive_get_location (archive, locid);
}
/**
* archive_set_current_location:
* @location: Location to which to set archive
*
* Set the current location in an archive to the location given; apply
* configuration for the new location to all backends necessary
**/
void
archive_set_current_location (Archive *archive,
Location *location)
{
Location *old_location;
GList *backends;
g_return_if_fail (archive != NULL);
g_return_if_fail (IS_ARCHIVE (archive));
g_return_if_fail (location != NULL);
g_return_if_fail (IS_LOCATION (location));
old_location = archive_get_current_location (archive);
archive_set_current_location_id (archive, location_get_id (location));
backends = location_get_changed_backends (location, old_location);
location_rollback_backends_to (location, NULL, 0, backends, TRUE);
}
/**
* archive_set_current_location_id:
* @name:
*
* Sets the current location's name, but does not invoke any rollback
**/
void
archive_set_current_location_id (Archive *archive,
const gchar *locid)
{
g_return_if_fail (archive != NULL);
g_return_if_fail (IS_ARCHIVE (archive));
g_return_if_fail (locid != NULL);
if (archive->current_location_id != NULL)
g_free (archive->current_location_id);
archive->current_location_id = g_strdup (locid);
if (archive->is_global)
gnome_config_set_string
("/ximian-setup-tools/config/current/global-location",
archive->current_location_id);
else
gnome_config_set_string
("/capplet-archive/config/current/location",
archive->current_location_id);
gnome_config_sync ();
}
/**
* archive_get_current_location_id:
*
* Get the name of the current location
*
* Return value: String containing current location, should not be freed, or
* NULL if no current location exists and the default location could not be
* created
**/
const gchar *
archive_get_current_location_id (Archive *archive)
{
gboolean def;
Location *loc;
Location *current_location;
g_return_val_if_fail (archive != NULL, NULL);
g_return_val_if_fail (IS_ARCHIVE (archive), NULL);
if (archive->current_location_id == NULL) {
if (archive->is_global)
archive->current_location_id =
gnome_config_get_string_with_default
("/ximian-setup-tools/config/current/global-location=default", &def);
else
archive->current_location_id =
gnome_config_get_string_with_default
("/capplet-archive/config/current/location=default", &def);
/* Create default location if it does not exist */
if (def) {
current_location =
archive_get_location (archive, archive->current_location_id);
if (current_location == NULL) {
loc = archive_create_location (archive, archive->current_location_id,
_("Default location"), NULL);
if (archive->is_global &&
location_store_full_snapshot (loc) < 0)
{
location_delete (loc);
return NULL;
}
bonobo_object_unref (BONOBO_OBJECT (loc));
} else {
bonobo_object_unref (BONOBO_OBJECT (current_location));
}
}
}
return archive->current_location_id;
}
/**
* archive_get_prefix:
* @archive:
*
* Get the prefix for locations in this archive
*
* Return value: String containing prefix; should not be freed
**/
const gchar *
archive_get_prefix (Archive *archive)
{
g_return_val_if_fail (archive != NULL, FALSE);
g_return_val_if_fail (IS_ARCHIVE (archive), FALSE);
return archive->prefix;
}
/**
* archive_is_global:
* @archive:
*
* Tell whether the archive is global or per-user
*
* Return value: TRUE if global, FALSE if per-user
**/
gboolean
archive_is_global (Archive *archive)
{
g_return_val_if_fail (archive != NULL, FALSE);
g_return_val_if_fail (IS_ARCHIVE (archive), FALSE);
return archive->is_global;
}
/**
* archive_get_backend_list:
* @archive:
*
* Get the master backend list for this archive
*
* Return value: Reference to the master backend list
**/
BackendList *
archive_get_backend_list (Archive *archive)
{
g_return_val_if_fail (archive != NULL, FALSE);
g_return_val_if_fail (IS_ARCHIVE (archive), FALSE);
return archive->backend_list;
}
/**
* archive_foreach_child_location:
* @archive:
* @callback: Callback to invoke
* @parent: Iterate through the children of this location; iterate through
* toplevel locations if this is NULL
* @data: Arbitrary data to pass to the callback
*
* Invoke the given callback for each location that inherits the given
* location, or for each toplevel location if the parent given is
* NULL. Terminate the iteration if any child returns a nonzero value
**/
static gint
foreach_build_list_cb (gchar *key, Location *value, GList **node)
{
if (!location_is_deleted (value))
*node = g_list_prepend (*node, value);
return 0;
}
GList *
archive_get_child_locations (Archive *archive,
Location *parent)
{
GList *list = NULL, *node, *tmp;
Location *loc;
g_return_val_if_fail (archive != NULL, NULL);
g_return_val_if_fail (IS_ARCHIVE (archive), NULL);
load_all_locations (archive);
g_tree_traverse (archive->locations,
(GTraverseFunc) foreach_build_list_cb,
G_IN_ORDER,
&list);
node = list;
while (node != NULL) {
loc = node->data;
tmp = node->next;
if (location_get_parent (loc) != parent) {
list = g_list_remove_link (list, node);
g_list_free_1 (node);
bonobo_object_unref (BONOBO_OBJECT (loc));
}
node = tmp;
}
return list;
}
/* Load and register all the locations for this archive */
static void
load_all_locations (Archive *archive)
{
DIR *archive_dir;
struct dirent entry, *entryp;
gchar *filename;
Location *location;
archive_dir = opendir (archive->prefix);
if (archive_dir == NULL) {
g_warning ("load_all_locations: %s", g_strerror (errno));
return;
}
while (1) {
if (readdir_r (archive_dir, &entry, &entryp)) {
g_warning ("load_all_locations: %s",
g_strerror (errno));
break;
}
if (entryp == NULL) break;
if (strcmp (entry.d_name, ".") &&
strcmp (entry.d_name, ".."))
{
filename = g_concat_dir_and_file (archive->prefix,
entry.d_name);
if (g_file_test (filename, G_FILE_TEST_ISDIR)) {
location = archive_get_location (archive, entry.d_name);
if (location_is_deleted (location))
bonobo_object_unref (BONOBO_OBJECT (location));
}
}
}
}