int
stonithd_signon(const char * client_name)
{
	int     rc = ST_FAIL;
	char	path[] = IPC_PATH_ATTR;
	char	sock[] = STONITHD_SOCK;
	char	cbsock[] = STONITHD_CALLBACK_SOCK;
	struct  ha_msg * request;
	struct  ha_msg * reply;
	GHashTable *	 wchanattrs;
	uid_t	my_euid;
	gid_t	my_egid;
	const char * tmpstr;
	int 	rc_tmp;
	gboolean connected = TRUE;
 	cl_uuid_t cookie, *cptr = NULL;

	if (chan == NULL || chan->ch_status != IPC_CONNECT) {
	    connected = FALSE;
	} else if (cbchan == NULL || cbchan->ch_status != IPC_CONNECT) {
	    connected = FALSE;
	}

	if(!connected) {
		/* cleanup */
		if (NULL != chan) {
		    chan->ops->destroy(chan);
		    chan = NULL;
		}
		if (NULL != cbchan) {
		    cbchan->ops->destroy(cbchan);
		    cbchan = NULL;
		}
		stdlib_log(LOG_DEBUG, "stonithd_signon: creating connection");
		wchanattrs = g_hash_table_new(g_str_hash, g_str_equal);
        	g_hash_table_insert(wchanattrs, path, sock);
		/* Connect to the stonith deamon */
		chan = ipc_channel_constructor(IPC_ANYTYPE, wchanattrs);
		g_hash_table_destroy(wchanattrs);
	
		if (chan == NULL) {
			stdlib_log(LOG_ERR, "stonithd_signon: Can't connect "
				   " to stonithd");
			rc = ST_FAIL;
			goto end;
		}

	        if (chan->ops->initiate_connection(chan) != IPC_OK) {
			stdlib_log(LOG_ERR, "stonithd_signon: Can't initiate "
				   "connection to stonithd");
			rc = ST_FAIL;
			goto end;
       		}
	}

	CLIENT_PID = getpid();
	snprintf(CLIENT_PID_STR, sizeof(CLIENT_PID_STR), "%d", CLIENT_PID);
	if ( client_name != NULL ) {
		CLIENT_NAME = client_name;
	} else {
		CLIENT_NAME = CLIENT_PID_STR;
	}

	if ( (request = create_basic_reqmsg_fields(ST_SIGNON)) == NULL) {
		rc = ST_FAIL;
		goto end;
	}

	/* important error check client name length */
	my_euid = geteuid();
	my_egid = getegid();
	if (  (	ha_msg_add_int(request, F_STONITHD_CEUID, my_euid) != HA_OK )
	    ||(	ha_msg_add_int(request, F_STONITHD_CEGID, my_egid) != HA_OK )
	    ||( ha_msg_add(request, F_STONITHD_COOKIE, "") != HA_OK )
	   ) {
		stdlib_log(LOG_ERR, "stonithd_signon: "
			   "cannot add field to ha_msg.");
		ZAPMSG(request);
		rc = ST_FAIL;
		goto end;
	}

	stdlib_log(LOG_DEBUG, "sending out the signon msg.");
	/* Send the registration request message */
	if (msg2ipcchan(request, chan) != HA_OK) {
		ZAPMSG(request);
		stdlib_log(LOG_ERR, "can't send signon message to IPC");
		rc = ST_FAIL;
		goto end;
	}

	/* waiting for the output to finish */
	do { 
		rc_tmp= chan_waitout_timeout(chan, DEFAULT_TIMEOUT);
	} while (rc_tmp == IPC_INTR);

	ZAPMSG(request);
	if (IPC_OK != rc_tmp) {
		stdlib_log(LOG_ERR, "%s:%d: waitout failed."
			   , __FUNCTION__, __LINE__);
		rc = ST_FAIL;
		goto end;
	}

	/* Read the reply... */
        if ( IPC_OK != chan_waitin_timeout(chan, DEFAULT_TIMEOUT) ) {
		stdlib_log(LOG_ERR, "%s:%d: waitin failed."
			   , __FUNCTION__, __LINE__);
		rc = ST_FAIL;
		goto end;
	}

	if ( (reply = msgfromIPC_noauth(chan)) == NULL ) {
		stdlib_log(LOG_ERR, "stonithd_signon: failed to fetch reply.");
		rc = ST_FAIL;
		goto end;
	}
	
	if ( TRUE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL, 
			     F_STONITHD_APIRPL, ST_RSIGNON, TRUE) ) {
		if ( ((tmpstr=cl_get_string(reply, F_STONITHD_APIRET)) != NULL)
	   	    && (STRNCMP_CONST(tmpstr, ST_APIOK) == 0) ) {
			rc = ST_OK;
			stdlib_log(LOG_DEBUG, "signed on to stonithd.");
			/* get cookie if any */
			if( cl_get_uuid(reply, F_STONITHD_COOKIE, &cookie) == HA_OK ) {
				cptr = &cookie;
			}
		} else {
			stdlib_log(LOG_WARNING, "failed to signon to the "
				   "stonithd.");
		}
	} else {
		stdlib_log(LOG_ERR, "stonithd_signon: "
			   "Got an unexpected message.");
	}
	ZAPMSG(reply);

	if (ST_OK != rc) { /* Something wrong when try to sign on to stonithd */
		goto end;
	}

	/* Connect to the stonith deamon via callback channel */
	wchanattrs = g_hash_table_new(g_str_hash, g_str_equal);
        g_hash_table_insert(wchanattrs, path, cbsock);
	cbchan = ipc_channel_constructor(IPC_ANYTYPE, wchanattrs);
	g_hash_table_destroy(wchanattrs);

	if (cbchan == NULL) {
		stdlib_log(LOG_ERR, "stonithd_signon: Can't construct "
			   "callback channel to stonithd.");
		rc = ST_FAIL;
		goto end;
	}

        if (cbchan->ops->initiate_connection(cbchan) != IPC_OK) {
		stdlib_log(LOG_ERR, "stonithd_signon: Can't initiate "
			   "connection with the callback channel");
		rc = ST_FAIL;
		goto end;
 	}

	if ( (reply = msgfromIPC_noauth(cbchan)) == NULL ) {
		stdlib_log(LOG_ERR, "%s:%d: failed to fetch reply via the "
			   " callback channel"
			   , __FUNCTION__, __LINE__);
		rc = ST_FAIL;
		goto end;
	}
	
	if ( TRUE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL, 
			     F_STONITHD_APIRPL, ST_RSIGNON, TRUE) ) {
		tmpstr=cl_get_string(reply, F_STONITHD_APIRET);
		if ( !STRNCMP_CONST(tmpstr, ST_APIOK) ) {
			/* 
			 * If the server directly authenticates us (probably 
			 * via pid-auth), go ahead.
			 */
			stdlib_log(LOG_DEBUG, "%s:%d: Got a good signon reply "
				  "via the callback channel."
				   , __FUNCTION__, __LINE__);
		} else if ( !STRNCMP_CONST(tmpstr, ST_COOKIE) ) {
			/*
			 * If the server asks for a cookie to identify myself,
			 * initiate cookie authentication.
			 */
			if (cptr == NULL) {
				stdlib_log(LOG_ERR, "server requested cookie auth on "
					"the callback channel, but it didn't "
					"provide the cookie on the main channel.");
				rc = ST_FAIL;
			} else {
				rc = authenticate_with_cookie(cbchan, cptr);
			}
		} else {
			/* Unknown response. */
			rc = ST_FAIL;
			stdlib_log(LOG_ERR, "%s:%d: Got a bad signon reply "
				  "via the callback channel."
				   , __FUNCTION__, __LINE__);
		}
	} else {
		rc = ST_FAIL;
		stdlib_log(LOG_ERR, "stonithd_signon: "
			   "Got an unexpected message via the callback chan.");
	}
	ZAPMSG(reply);

end:
	if (ST_OK != rc) {
		/* Something wrong when confirm via callback channel */
		stonithd_signoff();
	}

	return rc;
}
Exemple #2
0
gboolean upload_results(AppData *app_data) {
    GError *error = NULL;
    SoupURI *result_uri = NULL;

    guint ret = 0;

    SoupSession *session;
    SoupMessage *server_msg;
    SoupRequest *request;

    gchar *form_data;
    GHashTable *data_table = g_hash_table_new (NULL, NULL);

    result_uri = soup_uri_new (app_data->server);
    if (result_uri == NULL) {
        g_set_error (&error, RESTRAINT_ERROR,
                     RESTRAINT_PARSE_ERROR_BAD_SYNTAX,
                     "Malformed server url: %s", app_data->server);
        goto cleanup;
    }
    session = soup_session_new_with_options("timeout", 3600, NULL);

    g_hash_table_insert (data_table, "path", app_data->test_name);
    g_hash_table_insert (data_table, "result", app_data->test_result);

    // if AVC_ERROR=+no_avc_check then disable the selinux check plugin
    // This is for legacy rhts tests.. please use --disable-plugin
    gchar *avc_error = getenv("AVC_ERROR");
    if (g_strcmp0 (avc_error, "+no_avc_check") == 0) {
        g_ptr_array_add (app_data->disable_plugin, g_strdup ("10_avc_check"));
    }

    if (app_data->disable_plugin->pdata) {
        g_ptr_array_add (app_data->disable_plugin, NULL);
        g_hash_table_insert (data_table, "disable_plugin",
                             g_strjoinv (" ", (gchar **)app_data->disable_plugin->pdata));
    }
    if (app_data->no_plugins)
      g_hash_table_insert (data_table, "no_plugins", &app_data->no_plugins);
    if (app_data->score)
      g_hash_table_insert (data_table, "score", app_data->score);
    if (app_data->result_msg)
      g_hash_table_insert (data_table, "message", app_data->result_msg);

    request = (SoupRequest *)soup_session_request_http_uri (session, "POST", result_uri, &error);
    server_msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
    g_object_unref(request);
    form_data = soup_form_encode_hash (data_table);
    soup_message_set_request (server_msg, "application/x-www-form-urlencoded",
                              SOUP_MEMORY_TAKE, form_data, strlen (form_data));
    g_print ("** %s %s Score:%s\n", app_data->test_name, app_data->test_result,
        app_data->score != NULL ? app_data->score : "N/A");

    ret = soup_session_send_message (session, server_msg);
    if (SOUP_STATUS_IS_SUCCESSFUL (ret)) {
        gchar *location = g_strdup_printf ("%s/logs/",
                                           soup_message_headers_get_one (server_msg->response_headers, "Location"));
        soup_uri_free (result_uri);
        result_uri = soup_uri_new (location);
        g_free (location);
        if (app_data->outputfile != NULL &&
            g_file_test (app_data->outputfile, G_FILE_TEST_EXISTS))
        {
            g_print ("Uploading %s ", app_data->filename);
            if (upload_file (session, app_data->outputfile, app_data->filename, result_uri, &error)) {
                g_print ("done\n");
            } else {
                g_print ("failed\n");
            }
        }
    } else {
       g_warning ("Failed to submit result, status: %d Message: %s\n", ret, server_msg->reason_phrase);
    }
    g_object_unref(server_msg);
    soup_session_abort(session);
    g_object_unref(session);

cleanup:
    g_hash_table_destroy(data_table);
    if (result_uri != NULL) {
        soup_uri_free (result_uri);
    }

    if (error) {
        g_printerr("%s [%s, %d]\n", error->message,
                g_quark_to_string(error->domain), error->code);
        g_clear_error(&error);
        return FALSE;
    } else {
        return TRUE;
    }
}
Exemple #3
0
/*
 * query object, about the contact list for each account
 * and fill all available contacts in the contacts table
 */
static gboolean
_get_contacts (void)
{
	GError *error;
	GSList *contacts_list;
	GHashTable *prefs_map;
	gchar **accounts;
	gchar **account_iter;
	gchar *account;
	
	error = NULL;

	if (proxy == NULL) {
		g_warning("[Gajim] unable to connect to session bus");
		return FALSE;
	}
	/* get gajim prefs and lookup for iconset */
	if (!dbus_g_proxy_call(proxy, "prefs_list", &error, G_TYPE_INVALID,
			dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING),
			&prefs_map, G_TYPE_INVALID))
	{
		_handle_dbus_exception(error, TRUE);
		return FALSE;
	}
	gpointer iconset_ptr = g_hash_table_lookup(prefs_map, "iconset");
	if (iconset_ptr != NULL) {
		iconset = g_strdup((gchar *)iconset_ptr);
	} else {
		g_warning("[Gajim] unable to get prefs value for iconset");
		return FALSE;
	}
	g_hash_table_destroy(prefs_map);
	/* END get gajim prefs */
	error= NULL;
	if (!dbus_g_proxy_call (proxy, "list_accounts", &error, G_TYPE_INVALID,
			G_TYPE_STRV,
			&accounts, G_TYPE_INVALID))
	{
		_handle_dbus_exception(error, TRUE);
		return FALSE;
	}
	for(account_iter = accounts; *account_iter ; account_iter++) {
		account = g_strdup(*account_iter);
		error = NULL;	
		/* query gajim remote object and put results in 'contacts_list' */
		if (!dbus_g_proxy_call (proxy, "list_contacts", &error,
				G_TYPE_STRING, account, /* call arguments */
				G_TYPE_INVALID, /* delimiter */
				/* return value is collection of maps */
				dbus_g_type_get_collection ("GSList",
					dbus_g_type_get_map ("GHashTable",
						G_TYPE_STRING, G_TYPE_VALUE)),
				&contacts_list, G_TYPE_INVALID))
		{
			_handle_dbus_exception(error, FALSE);
			error = NULL;
			continue;
		}
		g_slist_foreach (contacts_list, _foreach_contact, account);
		g_slist_free(contacts_list);
	}
	g_strfreev (accounts);
	return TRUE;
}
Exemple #4
0
/*
 * contact cb, gets property from contact dict
 * and put online contacts to jid_table
 */
static void
_foreach_contact(gpointer contact, gpointer user_data)
{
	const gchar *show;
	
	GValue *value;
	GHashTable *contact_table;
	
	/* holds contact props of already exisiting jid/nick */
	GHashTable *existing_contact;
	
	/* name of the contact in completion list
	   it may be jid, nick, jid (account), or nick(account) */
	GString *contact_str;
	
	gchar *jid;
	gchar *account;
	gint i;
	
	if (contact == NULL) {
		g_warning("Null contact in the list");
		return;
	}
	contact_table = (GHashTable *) contact;
	account = (gchar *) user_data;
	
	value = g_hash_table_lookup(contact_table, "show");
	if (value == NULL || !G_VALUE_HOLDS_STRING(value)) {
		g_warning("String expected (contact - show)");
		g_hash_table_destroy(contact_table);
		return;
	}
	show = g_value_get_string ((GValue *)value);
	if(g_str_equal(show, "offline") || g_str_equal(show, "error")) {
		g_hash_table_destroy(contact_table);
		return;
	}
	/* remove unneeded item with key resource and add account
	   to contact properties */
	g_hash_table_insert(contact_table, "account", account);
	g_hash_table_remove(contact_table, "resource");
	
	/* add nick the same way as jid */
	for(i=0;i<2;i++) {
		value = g_hash_table_lookup(contact_table, COMPLETION_PROPS[i]);	
		if(value == NULL || !G_VALUE_HOLDS_STRING(value)) {
			g_warning("String expected (contact - name)");
			return;
		}
		jid = g_value_dup_string((GValue *)value);
		existing_contact = g_hash_table_lookup(jid_table, jid);
		if(existing_contact) {
			/* add existing contact as nick (account) */
			contact_str = g_string_new(jid);
			g_string_append(contact_str, " (");
			g_string_append(contact_str,
				g_hash_table_lookup(existing_contact, "account"));
			g_string_append(contact_str, ")");
			g_hash_table_insert(jid_table, contact_str->str,
													existing_contact);
			g_string_free(contact_str, FALSE);
			
			/* add current contact as nick (account) */
			contact_str = g_string_new(jid);
			g_string_append(contact_str, " (");
			g_string_append(contact_str,
				g_hash_table_lookup(contact_table, "account"));
			g_string_append(contact_str, ")");
			g_hash_table_insert(jid_table, contact_str->str,
													contact_table);
			g_string_free(contact_str, FALSE);
		}
		else {
			g_hash_table_insert(jid_table, jid, contact_table);
		}
	}
	
}
Exemple #5
0
static void
builder_options_set_property (GObject      *object,
                              guint         prop_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  BuilderOptions *self = BUILDER_OPTIONS (object);
  gchar **tmp;

  switch (prop_id)
    {
    case PROP_CFLAGS:
      g_clear_pointer (&self->cflags, g_free);
      self->cflags = g_value_dup_string (value);
      break;

    case PROP_CXXFLAGS:
      g_clear_pointer (&self->cxxflags, g_free);
      self->cxxflags = g_value_dup_string (value);
      break;

    case PROP_PREFIX:
      g_clear_pointer (&self->prefix, g_free);
      self->prefix = g_value_dup_string (value);
      break;

    case PROP_ENV:
      tmp = self->env;
      self->env = g_strdupv (g_value_get_boxed (value));
      g_strfreev (tmp);
      break;

    case PROP_ARCH:
      g_hash_table_destroy (self->arch);
      /* NOTE: This takes ownership of the hash table! */
      self->arch = g_value_dup_boxed (value);
      break;

    case PROP_BUILD_ARGS:
      tmp = self->build_args;
      self->build_args = g_strdupv (g_value_get_boxed (value));
      g_strfreev (tmp);
      break;

    case PROP_CONFIG_OPTS:
      tmp = self->config_opts;
      self->config_opts = g_strdupv (g_value_get_boxed (value));
      g_strfreev (tmp);
      break;

    case PROP_STRIP:
      self->strip = g_value_get_boolean (value);
      break;

    case PROP_NO_DEBUGINFO:
      self->no_debuginfo = g_value_get_boolean (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}
Exemple #6
0
// destroy Symbol Table
void s_table_destroy (SymbolTable* table) {
    debugInfo("Try to destroy Symbol Table\n");
    g_hash_table_foreach(table, &s_destroy_entry, NULL);
    g_hash_table_destroy ( table );
}
Exemple #7
0
static void jabber_logout(struct im_connection *ic)
{
	struct jabber_data *jd = ic->proto_data;

	while (jd->filetransfers) {
		imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out");
	}

	while (jd->streamhosts) {
		jabber_streamhost_t *sh = jd->streamhosts->data;
		jd->streamhosts = g_slist_remove(jd->streamhosts, sh);
		g_free(sh->jid);
		g_free(sh->host);
		g_free(sh);
	}

	if (jd->fd >= 0) {
		jabber_end_stream(ic);
	}

	while (ic->groupchats) {
		jabber_chat_free(ic->groupchats->data);
	}

	if (jd->r_inpa >= 0) {
		b_event_remove(jd->r_inpa);
	}
	if (jd->w_inpa >= 0) {
		b_event_remove(jd->w_inpa);
	}

	if (jd->ssl) {
		ssl_disconnect(jd->ssl);
	}
	if (jd->fd >= 0) {
		proxy_disconnect(jd->fd);
	}

	if (jd->tx_len) {
		g_free(jd->txq);
	}

	if (jd->node_cache) {
		g_hash_table_destroy(jd->node_cache);
	}

	if (jd->buddies) {
		jabber_buddy_remove_all(ic);
	}

	xt_free(jd->xt);

	md5_free(&jd->cached_id_prefix);

	g_free(jd->oauth2_access_token);
	g_free(jd->away_message);
	g_free(jd->internal_jid);
	g_free(jd->gmail_tid);
	g_free(jd->username);
	g_free(jd->me);
	g_free(jd);

	jabber_connections = g_slist_remove(jabber_connections, ic);
}
Exemple #8
0
int main(int argc, char **argv)
{
    int verbose = 0;
    char *pattern = NULL;
    char *source_fname = NULL;
    char *dest_fname = NULL;
    int64_t start_utime = 0;
    int64_t end_utime = -1;
    int have_end_utime = 0;
    int invert_regex = 0;

    char *optstring = "hc:vs:e:i";
    char c;

    while ((c = getopt(argc, argv, optstring)) >= 0)
    {
        switch (c) {
            case 'h':
                usage();
                break;
            case 's':
                {
                    char *eptr = NULL;
                    double start_time = strtod(optarg, &eptr);
                    if(*eptr != 0)
                        usage();
                    start_utime = (int64_t) (start_time * 1000000);
                }
                break;
            case 'e':
                {
                    char *eptr = NULL;
                    double end_time = strtod(optarg, &eptr);
                    if(*eptr != 0)
                        usage();
                    end_utime = (int64_t) (end_time * 1000000);
                    have_end_utime = 1;
                }
                break;
            case 'i':
                invert_regex = 1;
                break;
            case 'c':
                pattern = g_strdup(optarg);
                break;
            case 'v':
                verbose = 1;
                break;
            default:
                usage();
                break;
        };
    }

    if(start_utime < 0 || (have_end_utime && end_utime < start_utime))
        usage();

    if (optind != argc - 2)
        usage();

    if (!pattern)
        usage();

#ifdef USE_GREGEX
    GRegex * regex;
    GError *rerr = NULL;
    regex = g_regex_new(pattern, (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, &rerr);
    if(rerr) {
        fprintf(stderr, "bad regex\n");
        exit(1);
    }
#else
    regex_t preg;

    if (0 != regcomp(&preg, pattern, REG_NOSUB | REG_EXTENDED)) {
        fprintf(stderr, "bad regex\n");
        exit(1);
    }
#endif

    source_fname = argv[argc - 2];
    dest_fname = argv[argc - 1];

    lcm_eventlog_t *src_log = lcm_eventlog_create(source_fname, "r");
    if (!src_log) {
        perror("Unable to open source logfile");
#ifdef USE_GREGEX
		g_regex_unref(regex);
#else
        regfree(&preg);
#endif
        return 1;
    }
    lcm_eventlog_t *dst_log = lcm_eventlog_create(dest_fname, "w");
    if (!dst_log) {
        perror("Unable to open destination logfile");
        lcm_eventlog_destroy(src_log);
#ifdef USE_GREGEX
		g_regex_unref(regex);
#else
        regfree(&preg);
#endif
        return 1;
    }

    GHashTable *counts = g_hash_table_new_full(g_str_hash, g_str_equal,
            g_free, free);
    int nwritten = 0;
    int have_first_event_timestamp = 0;
    int64_t first_event_timestamp;

    for (lcm_eventlog_event_t *event = lcm_eventlog_read_next_event(src_log);
            event != NULL;
            event = lcm_eventlog_read_next_event(src_log)) {
        if(!have_first_event_timestamp) {
            first_event_timestamp = event->timestamp;
            have_first_event_timestamp = 1;
        }

        int64_t elapsed = event->timestamp - first_event_timestamp;
        if(elapsed < start_utime) {
            lcm_eventlog_free_event(event);
            continue;
        }
        if(have_end_utime && elapsed > end_utime) {
            lcm_eventlog_free_event(event);
            break;
        }

#ifdef USE_GREGEX
		int regmatch =  g_regex_match(regex, event->channel, (GRegexMatchFlags) 0, NULL);
#else
        int regmatch = regexec(&preg, event->channel, 0, NULL, 0);
#endif
        int copy_to_dest = (regmatch == 0 && !invert_regex) ||
                           (regmatch != 0 && invert_regex);
        if (copy_to_dest) {
            lcm_eventlog_write_event(dst_log, event);
            nwritten++;

            if (verbose)  {
                int *count = (int *) g_hash_table_lookup(counts, event->channel);
                if (!count) {
                    count = (int*) malloc(sizeof(int));
                    *count = 1;
                    g_hash_table_insert(counts, g_strdup(event->channel), count);
                    printf("matched channel %s\n", event->channel);
                } else {
                    *count += 1;
                }
            }
        }
        lcm_eventlog_free_event(event);
    }

    if (verbose) {
        g_hash_table_foreach(counts, _verbose_entry_summary, NULL);
        printf("=====\n");
        printf("Events written: %d\n", nwritten);
    }
    
#ifdef USE_GREGEX
	g_regex_unref(regex);
#else
	regfree(&preg);
#endif
    lcm_eventlog_destroy(src_log);
    lcm_eventlog_destroy(dst_log);
    g_hash_table_destroy(counts);
    return 0;
}
static DBusHandlerResult
rb_metadata_dbus_save (DBusConnection *connection,
		       DBusMessage *message,
		       ServiceData *svc)
{
	char *uri;
	DBusMessageIter iter;
	DBusMessage *reply;
	GHashTable *data;
	GError *error = NULL;

	/* get URI and metadata from message */
	if (!dbus_message_iter_init (message, &iter)) {
		return DBUS_HANDLER_RESULT_NEED_MEMORY;
	}
	if (!rb_metadata_dbus_get_string (&iter, &uri)) {
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	}

	data = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rb_value_free);
	if (!rb_metadata_dbus_read_from_message (svc->metadata,
						 data,
						 &iter)) {
		/* make translatable? */
		g_free (uri);
		return send_error (connection, message,
				   RB_METADATA_ERROR_INTERNAL,
				   "Unable to read metadata from message");
	}

	/* pass to real metadata instance, and save it */
	rb_metadata_reset (svc->metadata);
	g_hash_table_foreach_remove (data, (GHRFunc) _set_metadata, svc->metadata);
	g_hash_table_destroy (data);

	rb_metadata_save (svc->metadata, uri, &error);
	g_free (uri);

	if (error) {
		DBusHandlerResult r;
		rb_debug ("metadata error: %s", error->message);

		r = send_error (connection, message, error->code, error->message);
		g_clear_error (&error);
		return r;
	}

	reply = dbus_message_new_method_return (message);
	if (!reply) {
		rb_debug ("out of memory creating return message");
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	}

	if (!dbus_connection_send (connection, reply, NULL)) {
		rb_debug ("failed to send return message");
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	}

	dbus_message_unref (reply);

	return DBUS_HANDLER_RESULT_HANDLED;
}