/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* oafd: OAF CORBA dameon.
*
* Copyright (C) 1999, 2000 Red Hat, Inc.
* Copyright (C) 1999, 2000 Eazel, Inc.
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Authors: Elliot Lee <sopwith@redhat.com>
* Maciej Stachowiak <mjs@eazel.com>
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <glib/gi18n.h>
#include <glib/gmarkup.h>
#include "bonobo-activation/bonobo-activation-private.h"
#include "server.h"
/* SAX Parser */
typedef enum {
STATE_START,
STATE_OAF_INFO,
STATE_OAF_SERVER,
STATE_OAF_ATTRIBUTE,
STATE_ITEM,
STATE_UNKNOWN,
STATE_ERROR
} ParseState;
typedef struct {
char *filename; /* Filename of .server file, or NULL
* if parsing from memory.
*/
ParseState state;
ParseState prev_state;
int unknown_depth;
const char *host;
GSList **entries;
Bonobo_ServerInfo *cur_server;
Bonobo_ActivationProperty *cur_prop;
GList *cur_props;
GList *cur_items;
} ParseInfo;
#define IS_ELEMENT(x) (!strcmp (name, x))
static ParseInfo *
parse_info_new (const char *filename,
const char *host,
GSList **entries)
{
ParseInfo *info = g_new0 (ParseInfo, 1);
info->prev_state = STATE_UNKNOWN;
info->state = STATE_START;
info->host = host;
info->entries = entries;
info->filename = g_strdup (filename);
return info;
}
static void
parse_info_free (ParseInfo *info)
{
g_free (info->filename);
g_free (info);
}
static char *
od_validate (const char *iid, const char *type, const char *location)
{
int i;
if (iid == NULL) {
return g_strdup (_("a NULL iid is not valid"));
}
if (type == NULL) {
return g_strdup_printf (_("iid %s has a NULL type"), iid);
}
if (location == NULL) {
return g_strdup_printf (_("iid %s has a NULL location"), iid);
}
for (i = 0; iid && iid [i]; i++) {
char c = iid [i];
if (c == ',' || c == '[' || c == ']' ||
/* Reserved for future expansion */
c == '!' || c == '#' || c == '|') {
return g_strdup_printf (_("invalid character '%c' in iid '%s'"),
c, iid);
}
}
return NULL;
}
static void
parse_oaf_server_attrs (ParseInfo *info,
const gchar **attr_names,
const gchar **attr_values)
{
const char *iid = NULL;
const char *type = NULL;
const char *location = NULL;
const char *att, *val;
char *error;
int i = 0;
info->state = STATE_OAF_SERVER;
if (!attr_names || !attr_values)
return;
do {
att = attr_names[i];
val = attr_values[i++];
if (att && val) {
if (!iid && !strcmp (att, "iid"))
iid = val;
else if (!type && !strcmp (att, "type"))
type = val;
else if (!location && !strcmp (att, "location"))
location = val;
}
} while (att && val);
error = od_validate (iid, type, location);
if (error != NULL) {
g_warning ("%s", error);
g_free (error);
return;
}
#ifdef G_OS_WIN32
/* If this data has been read from a .server file, and the
* path to the exe or dll starts with ../, make it relative to
* the location of the .server file. Very convenient, means
* yuo can install some software package that includes a
* Bonobo component in a freeestanding location, and just need
* to update your BONOBO_ACTIVATION_PATH so the .server file
* is found.
*
* In other cases, possibly replace configure-time shlib or
* exe location with the actual installed one. This is for
* components that have been built with the same
* configure-time prefix as libbonobo.
*/
if (!strcmp (type, "exe") || !strcmp (type, "shlib")) {
if (info->filename != NULL &&
!strncmp (location, "..", 2) &&
G_IS_DIR_SEPARATOR (location[2])) {
gchar *dirname = g_path_get_dirname (info->filename);
location = g_build_filename (dirname, location, NULL);
g_free (dirname);
} else {
location = _bonobo_activation_win32_replace_prefix (_bonobo_activation_win32_get_prefix (), location);
}
}
#endif
/* Now create the ServerInfo object */
info->cur_server = g_new0 (Bonobo_ServerInfo, 1);
info->cur_server->iid = CORBA_string_dup (iid);
info->cur_server->server_type = CORBA_string_dup (type);
info->cur_server->location_info = CORBA_string_dup (location);
info->cur_server->hostname = CORBA_string_dup (info->host);
info->cur_server->username = CORBA_string_dup (g_get_user_name ());
info->cur_server->domain = CORBA_string_dup ("unused");
#ifdef G_OS_WIN32
if (!strcmp (type, "exe") || !strcmp (type, "shlib"))
g_free ((char *) location);
#endif
}
static GHashTable *interesting_locales = NULL;
void
add_initial_locales (void)
{
const char * const * langs;
int i;
if (!interesting_locales)
interesting_locales = g_hash_table_new (
g_str_hash, g_str_equal);
langs = g_get_language_names ();
for (i = 0; langs[i] != NULL; i++)
g_hash_table_insert (interesting_locales,
g_strdup (langs[i]),
GUINT_TO_POINTER (1));
}
gboolean
register_interest_in_locales (const char *locales)
{
int i;
char **localev;
gboolean new_locale = FALSE;
localev = g_strsplit (locales, ",", 0);
for (i = 0; localev[i]; i++) {
if (!g_hash_table_lookup (interesting_locales, localev[i])) {
#ifdef LOCALE_DEBUG
g_warning ("New locale '%s' (%d)!",
localev[i], g_list_length (locale_list));
#endif
g_hash_table_insert (interesting_locales,
g_strdup (localev[i]),
GUINT_TO_POINTER (1));
new_locale = TRUE;
}
}
g_strfreev (localev);
return new_locale;
}
static gboolean
is_locale_interesting (const char *name_with_locale)
{
const char *locale;
if (!name_with_locale)
return FALSE;
if (!(locale = strchr (name_with_locale, '-')))
return TRUE;
locale++;
return g_hash_table_lookup (interesting_locales, locale) != NULL;
}
static gboolean
od_string_to_boolean (const char *str)
{
if (!g_ascii_strcasecmp (str, "true") ||
!g_ascii_strcasecmp (str, "yes") ||
!strcmp (str, "1"))
return TRUE;
else
return FALSE;
}
static void
parse_oaf_attribute (ParseInfo *info,
const gchar **attr_names,
const gchar **attr_values)
{
int i = 0;
const char *type = NULL;
const char *name = NULL;
const char *value = NULL;
const char *att, *val;
g_assert (info->cur_server);
info->state = STATE_OAF_ATTRIBUTE;
if (!attr_names || !attr_values)
return;
do {
att = attr_names[i];
val = attr_values[i++];
if (att && val) {
if (!strcmp (att, "type"))
type = val;
else if (!strcmp (att, "name")) {
name = val;
if (!is_locale_interesting (name))
return;
} else if (!strcmp (att, "value"))
value = val;
}
} while (att && val);
if (!type || !name)
return;
if (name[0] == '_')
g_critical ("%s is an invalid property name "
"- property names beginning with '_' are reserved",
name);
info->cur_prop = ORBit_small_alloc (TC_Bonobo_ActivationProperty);
info->cur_prop->name = CORBA_string_dup (name);
if (g_ascii_strcasecmp (type, "stringv") == 0) {
info->cur_prop->v._d = Bonobo_ACTIVATION_P_STRINGV;
} else if (g_ascii_strcasecmp (type, "number") == 0) {
info->cur_prop->v._d = Bonobo_ACTIVATION_P_NUMBER;
info->cur_prop->v._u.value_number = atof (value);
} else if (g_ascii_strcasecmp (type, "boolean") == 0) {
info->cur_prop->v._d = Bonobo_ACTIVATION_P_BOOLEAN;
info->cur_prop->v._u.value_boolean = od_string_to_boolean (value);
} else {
/* Assume string */
info->cur_prop->v._d = Bonobo_ACTIVATION_P_STRING;
if (value != NULL) {
info->cur_prop->v._u.value_string = CORBA_string_dup (value);
} else {
g_warning (_("Property '%s' has no value"),
info->cur_prop->name);
info->cur_prop->v._u.value_string =
CORBA_string_dup ("");
}
}
}
static void
parse_stringv_item (ParseInfo *info,
const gchar **attr_names,
const gchar **attr_values)
{
const char *value = NULL;
const char *att, *val;
int i = 0;
if (!attr_names || !attr_values)
return;
do {
att = attr_names[i];
val = attr_values[i++];
if (att && val) {
if (!value && !strcmp (att, "value")) {
value = val;
break;
}
}
} while (att && val);
if (value)
info->cur_items = g_list_prepend (info->cur_items, CORBA_string_dup (value));
info->state = STATE_ITEM;
}
static void
od_start_element (GMarkupParseContext *context,
const gchar *name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
ParseInfo *info = user_data;
switch (info->state) {
case STATE_START:
if (IS_ELEMENT ("oaf_info"))
info->state = STATE_OAF_INFO;
else {
info->prev_state = info->state;
info->state = STATE_UNKNOWN;
info->unknown_depth++;
}
break;
case STATE_OAF_INFO:
if (IS_ELEMENT ("oaf_server"))
parse_oaf_server_attrs (info, attribute_names, attribute_values);
else {
info->prev_state = info->state;
info->state = STATE_UNKNOWN;
info->unknown_depth++;
}
break;
case STATE_OAF_SERVER:
if (IS_ELEMENT ("oaf_attribute"))
parse_oaf_attribute (info, attribute_names, attribute_values);
else {
info->prev_state = info->state;
info->state = STATE_UNKNOWN;
info->unknown_depth++;
}
break;
case STATE_OAF_ATTRIBUTE:
if (IS_ELEMENT ("item"))
parse_stringv_item (info, attribute_names, attribute_values);
else {
info->prev_state = info->state;
info->state = STATE_UNKNOWN;
info->unknown_depth++;
}
break;
case STATE_UNKNOWN:
info->unknown_depth++;
break;
case STATE_ERROR:
break;
break;
default:
g_error ("start element, unknown state: %d", info->state);
}
}
static void
add_entry (ParseInfo *info)
{
GSList *l;
for (l = *(info->entries); l; l = l->next) {
Bonobo_ServerInfo *si = l->data;
if (!strcmp (si->iid, info->cur_server->iid))
return;
}
*(info->entries) = g_slist_prepend (*(info->entries), info->cur_server);
}
static void
od_end_element (GMarkupParseContext *context,
const gchar *name,
gpointer user_data,
GError **error)
{
ParseInfo *info = user_data;
switch (info->state) {
case STATE_ITEM:
info->state = STATE_OAF_ATTRIBUTE;
break;
case STATE_OAF_ATTRIBUTE: {
if (info->cur_prop && info->cur_prop->v._d == Bonobo_ACTIVATION_P_STRINGV) {
gint i, len;
GList *p;
len = g_list_length (info->cur_items);
info->cur_prop->v._u.value_stringv._length = len;
info->cur_prop->v._u.value_stringv._buffer =
CORBA_sequence_CORBA_string_allocbuf (len);
info->cur_items = g_list_reverse (info->cur_items);
for (i = 0, p = info->cur_items; p; p = p->next, i++)
info->cur_prop->v._u.
value_stringv._buffer[i] = p->data;
g_list_free (info->cur_items);
info->cur_items = NULL;
}
if (info->cur_prop) {
info->cur_props = g_list_prepend (info->cur_props, info->cur_prop);
info->cur_prop = NULL;
}
info->state = STATE_OAF_SERVER;
break;
}
case STATE_OAF_SERVER: {
if (info->cur_server) {
GList *p;
gint len, i;
len = g_list_length (info->cur_props);
info->cur_server->props._length = len;
info->cur_server->props._buffer =
CORBA_sequence_Bonobo_ActivationProperty_allocbuf (len);
info->cur_props = g_list_reverse (info->cur_props);
for (i = 0, p = info->cur_props; p; p = p->next, i++) {
Bonobo_ActivationProperty_copy (&info->cur_server->props._buffer[i],
(Bonobo_ActivationProperty *) p->data);
CORBA_free (p->data);
}
g_list_free (info->cur_props);
info->cur_props = NULL;
add_entry (info);
info->cur_server = NULL;
}
info->state = STATE_OAF_INFO;
break;
}
case STATE_OAF_INFO: {
info->state = STATE_START;
break;
}
case STATE_UNKNOWN:
info->unknown_depth--;
if (info->unknown_depth == 0)
info->state = info->prev_state;
break;
case STATE_START:
break;
default:
g_error ("end element, unknown state: %d", info->state);
}
}
static void
od_error (GMarkupParseContext *context,
GError *error,
gpointer user_data)
{
ParseInfo *info = user_data;
g_error ("Failed to parse: '%s' in file '%s'",
error ? error->message : "<nomsg>",
info->filename ? info->filename : "<memory>");
}
static GMarkupParser od_gmarkup_parser = {
od_start_element,
od_end_element,
NULL,
NULL,
od_error
};
static void
od_load_file (const char *file,
GSList **entries,
const char *host)
{
gsize length;
gchar *contents = NULL;
ParseInfo *info;
GMarkupParseContext *ctxt = NULL;
if (!g_file_get_contents (file, &contents, &length, NULL))
goto err;
info = parse_info_new (file, host, entries);
ctxt = g_markup_parse_context_new (&od_gmarkup_parser, 0, info,
(GDestroyNotify) parse_info_free);
if (!g_markup_parse_context_parse (ctxt, contents, length, NULL)) {
err:
g_warning (_("Could not parse badly formed XML document %s"), file);
}
if (ctxt)
g_markup_parse_context_free (ctxt);
g_free (contents);
}
void
bonobo_parse_server_info_memory (const char *server_info,
GSList **entries,
const char *host)
{
ParseInfo *info;
GMarkupParseContext *ctxt;
info = parse_info_new (NULL, host, entries);
ctxt = g_markup_parse_context_new (&od_gmarkup_parser, 0, info,
(GDestroyNotify) parse_info_free);
if (!g_markup_parse_context_parse (ctxt, server_info, strlen (server_info), NULL))
g_warning ("Failed to parse serverinfo from memory");
g_markup_parse_context_free (ctxt);
}
static gboolean
od_filename_has_extension (const char *filename,
const char *extension)
{
char *last_dot;
last_dot = strrchr (filename, '.');
return last_dot != NULL && strcmp (last_dot, extension) == 0;
}
static void
od_load_directory (const char *directory,
GSList **entries,
const char *host)
{
GDir *directory_handle;
const char *directory_entry;
char *pathname;
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, _("Trying dir %s"), directory);
directory_handle = g_dir_open (directory, 0, NULL);
if (directory_handle == NULL) {
/* FIXME */
return;
}
for (directory_entry = g_dir_read_name (directory_handle);
directory_entry != NULL;
directory_entry = g_dir_read_name (directory_handle)) {
pathname = g_build_filename (directory, directory_entry, NULL);
if (od_filename_has_extension (pathname, ".server")) {
od_load_file (pathname, entries, host);
}
g_free (pathname);
}
g_dir_close (directory_handle);
}
void
bonobo_server_info_load (char **directories,
Bonobo_ServerInfoList *servers,
GPtrArray const *runtime_servers,
GHashTable **iid_to_server_info_map,
const char *host)
{
GSList *entries;
int length;
GSList *p;
int i, j;
g_return_if_fail (directories);
g_return_if_fail (iid_to_server_info_map);
entries = NULL;
if (*iid_to_server_info_map != NULL) {
g_hash_table_destroy (*iid_to_server_info_map);
}
*iid_to_server_info_map = g_hash_table_new (g_str_hash, g_str_equal);
/* Load each directory */
for (i = 0; directories[i] != NULL; i++)
od_load_directory (directories[i], &entries, host);
/* Now convert 'entries' into something that the server can store and pass back */
length = g_slist_length (entries);
servers->_buffer = CORBA_sequence_Bonobo_ServerInfo_allocbuf
(length + runtime_servers->len);
servers->_length = length + runtime_servers->len;
servers->_maximum = servers->_length;
for (j = 0, p = entries; j < length; j++, p = p->next) {
memcpy (&servers->_buffer[j], p->data, sizeof (Bonobo_ServerInfo));
g_hash_table_insert (*iid_to_server_info_map,
servers->_buffer[j].iid,
&servers->_buffer[j]);
}
/* append information of runtime-defined servers */
for (j = 0; j < runtime_servers->len; j++) {
servers->_buffer[length + j] = *(Bonobo_ServerInfo *)
g_ptr_array_index (runtime_servers, j);
g_hash_table_insert (*iid_to_server_info_map,
servers->_buffer[length + j].iid,
&servers->_buffer[length + j]);
}
g_slist_foreach (entries, (GFunc) g_free, NULL);
g_slist_free (entries);
}
syntax highlighted by Code2HTML, v. 0.9.1