static void
interact_request_callback (SmsConn   conn,
                           SmPointer manager_data,
                           int       dialog_type)
{
        CsmXSMPClient *client = manager_data;
#if 0
        gboolean       res;
        GError        *error;
#endif

        g_debug ("CsmXSMPClient: Client '%s' received InteractRequest(%s)",
                 client->priv->description,
                 dialog_type == SmDialogNormal ? "Dialog" : "Errors");

        csm_client_end_session_response (CSM_CLIENT (client),
                                         FALSE, FALSE, FALSE,
                                         _("This program is blocking logout."));

#if 0
        /* Can't just call back with Interact because session client
           grabs the keyboard!  So, we try to get it to release
           grabs by telling it we've cancelled the shutdown.
           This grabbing is clearly bullshit and is not supported by
           the client spec or protocol spec.
        */
        res = xsmp_cancel_end_session (CSM_CLIENT (client), &error);
        if (! res) {
                g_warning ("Unable to cancel end session: %s", error->message);
                g_error_free (error);
        }
#endif
        xsmp_interact (CSM_CLIENT (client));
}
static DBusHandlerResult
client_dbus_filter_function (DBusConnection *connection,
                             DBusMessage    *message,
                             void           *user_data)
{
        CsmDBusClient *client = CSM_DBUS_CLIENT (user_data);
        const char    *path;

        g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
        g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);

        path = dbus_message_get_path (message);

        g_debug ("CsmDBusClient: obj_path=%s interface=%s method=%s",
                 dbus_message_get_path (message),
                 dbus_message_get_interface (message),
                 dbus_message_get_member (message));

        if (dbus_message_is_method_call (message, SM_DBUS_CLIENT_PRIVATE_INTERFACE, "EndSessionResponse")) {
                g_assert (csm_client_peek_id (CSM_CLIENT (client)) != NULL);

                if (path != NULL && strcmp (path, csm_client_peek_id (CSM_CLIENT (client))) != 0) {
                        /* Different object path */
                        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
                }
                handle_end_session_response (client, message);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static void
delete_property (CsmXSMPClient *client,
                 const char    *name)
{
        int     index;
        SmProp *prop;

        prop = find_property (client, name, &index);
        if (!prop) {
                return;
        }

#if 0
        /* This is wrong anyway; we can't unconditionally run the current
         * discard command; if this client corresponds to a CsmAppResumed,
         * and the current discard command is identical to the app's
         * discard_command, then we don't run the discard command now,
         * because that would delete a saved state we may want to resume
         * again later.
         */
        if (!strcmp (name, SmDiscardCommand)) {
                csm_client_run_discard (CSM_CLIENT (client));
        }
#endif

        g_ptr_array_remove_index_fast (client->priv->props, index);
        SmFreeProperty (prop);
}
static void
save_yourself_done_callback (SmsConn   conn,
                             SmPointer manager_data,
                             Bool      success)
{
        CsmXSMPClient *client = manager_data;

        g_debug ("CsmXSMPClient: Client '%s' received SaveYourselfDone(success = %s)",
                 client->priv->description,
                 success ? "True" : "False");

	if (client->priv->current_save_yourself != -1) {
		SmsSaveComplete (client->priv->conn);
		client->priv->current_save_yourself = -1;
	}

        /* If success is false then the application couldn't save data. Nothing
         * the session manager can do about, though. FIXME: we could display a
         * dialog about this, I guess. */
        csm_client_end_session_response (CSM_CLIENT (client),
                                         TRUE, FALSE, FALSE,
                                         NULL);

        if (client->priv->next_save_yourself) {
                int      save_type = client->priv->next_save_yourself;
                gboolean allow_interact = client->priv->next_save_yourself_allow_interact;

                client->priv->next_save_yourself = -1;
                client->priv->next_save_yourself_allow_interact = -1;
                do_save_yourself (client, save_type, allow_interact);
        }
}
static Status
register_client_callback (SmsConn    conn,
                          SmPointer  manager_data,
                          char      *previous_id)
{
        CsmXSMPClient *client = manager_data;
        gboolean       handled;
        char          *id;

        g_debug ("CsmXSMPClient: Client '%s' received RegisterClient(%s)",
                 client->priv->description,
                 previous_id ? previous_id : "NULL");


        /* There are three cases:
         * 1. id is NULL - we'll use a new one
         * 2. id is known - we'll use known one
         * 3. id is unknown - this is an error
         */
        id = g_strdup (previous_id);

        handled = FALSE;
        g_signal_emit (client, signals[REGISTER_REQUEST], 0, &id, &handled);
        if (! handled) {
                g_debug ("CsmXSMPClient:  RegisterClient not handled!");
                g_free (id);
                free (previous_id);
                g_assert_not_reached ();
                return FALSE;
        }

        if (IS_STRING_EMPTY (id)) {
                g_debug ("CsmXSMPClient:   rejected: invalid previous_id");
                free (previous_id);
                return FALSE;
        }

        g_object_set (client, "startup-id", id, NULL);

        set_description (client);

        g_debug ("CsmXSMPClient: Sending RegisterClientReply to '%s'", client->priv->description);

        SmsRegisterClientReply (conn, id);

        if (IS_STRING_EMPTY (previous_id)) {
                /* Send the initial SaveYourself. */
                g_debug ("CsmXSMPClient: Sending initial SaveYourself");
                SmsSaveYourself (conn, SmSaveLocal, False, SmInteractStyleNone, False);
                client->priv->current_save_yourself = SmSaveLocal;
        }

        csm_client_set_status (CSM_CLIENT (client), CSM_CLIENT_REGISTERED);

        g_free (id);
        free (previous_id);

        return TRUE;
}
static void
close_connection_callback (SmsConn     conn,
                           SmPointer   manager_data,
                           int         count,
                           char      **reason_msgs)
{
        CsmXSMPClient *client = manager_data;
        int            i;

        g_debug ("CsmXSMPClient: Client '%s' received CloseConnection", client->priv->description);
        for (i = 0; i < count; i++) {
                g_debug ("CsmXSMPClient:  close reason: '%s'", reason_msgs[i]);
        }
        SmFreeReasons (count, reason_msgs);

        csm_client_set_status (CSM_CLIENT (client), CSM_CLIENT_FINISHED);
        csm_client_disconnected (CSM_CLIENT (client));
}
static void
handle_end_session_response (CsmDBusClient *client,
                             DBusMessage   *message)
{
        const char     *sender;
        DBusMessage    *reply;
        DBusError       error;
        dbus_bool_t     is_ok;
        const char     *reason;

        dbus_error_init (&error);
        if (! dbus_message_get_args (message, &error,
                                     DBUS_TYPE_BOOLEAN, &is_ok,
                                     DBUS_TYPE_STRING, &reason,
                                     DBUS_TYPE_INVALID)) {
                if (dbus_error_is_set (&error)) {
                        g_warning ("Invalid method call: %s", error.message);
                        dbus_error_free (&error);
                }
                raise_error (client->priv->connection,
                             message,
                             DBUS_ERROR_FAILED,
                             "There is a syntax error in the invocation of the method EndSessionResponse");
                return;
        }

        g_debug ("CsmDBusClient: got EndSessionResponse is-ok:%d reason=%s", is_ok, reason);

        /* make sure it is from our client */
        sender = dbus_message_get_sender (message);
        if (sender == NULL
            || IS_STRING_EMPTY (client->priv->bus_name)
            || strcmp (sender, client->priv->bus_name) != 0) {

                raise_error (client->priv->connection,
                             message,
                             DBUS_ERROR_FAILED,
                             "Caller not recognized as the client");
                return;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL) {
                g_error ("No memory");
        }

        csm_client_end_session_response (CSM_CLIENT (client),
                                         is_ok, FALSE, FALSE, reason);


        if (! dbus_connection_send (client->priv->connection, reply, NULL)) {
                g_error ("No memory");
        }

        dbus_message_unref (reply);
}
CsmClient *
csm_xsmp_client_new (IceConn ice_conn)
{
        CsmXSMPClient *xsmp;

        xsmp = g_object_new (CSM_TYPE_XSMP_CLIENT,
                             "ice-connection", ice_conn,
                             NULL);

        return CSM_CLIENT (xsmp);
}
CsmClient *
csm_dbus_client_new (const char *startup_id,
                     const char *bus_name)
{
        CsmDBusClient *client;

        client = g_object_new (CSM_TYPE_DBUS_CLIENT,
                               "startup-id", startup_id,
                               "bus-name", bus_name,
                               NULL);

        return CSM_CLIENT (client);
}
static gboolean
client_iochannel_watch (GIOChannel    *channel,
                        GIOCondition   condition,
                        CsmXSMPClient *client)
{
        gboolean keep_going;

        g_object_ref (client);
        switch (IceProcessMessages (client->priv->ice_connection, NULL, NULL)) {
        case IceProcessMessagesSuccess:
                keep_going = TRUE;
                break;

        case IceProcessMessagesIOError:
                g_debug ("CsmXSMPClient: IceProcessMessagesIOError on '%s'", client->priv->description);
                csm_client_set_status (CSM_CLIENT (client), CSM_CLIENT_FAILED);
                /* Emitting "disconnected" will eventually cause
                 * IceCloseConnection() to be called.
                 */
                csm_client_disconnected (CSM_CLIENT (client));
                keep_going = FALSE;
                break;

        case IceProcessMessagesConnectionClosed:
                g_debug ("CsmXSMPClient: IceProcessMessagesConnectionClosed on '%s'",
                         client->priv->description);
                client->priv->ice_connection = NULL;
                keep_going = FALSE;
                break;

        default:
                g_assert_not_reached ();
        }
        g_object_unref (client);

        return keep_going;
}
static void
interact_done_callback (SmsConn   conn,
                        SmPointer manager_data,
                        Bool      cancel_shutdown)
{
        CsmXSMPClient *client = manager_data;

        g_debug ("CsmXSMPClient: Client '%s' received InteractDone(cancel_shutdown = %s)",
                 client->priv->description,
                 cancel_shutdown ? "True" : "False");

        csm_client_end_session_response (CSM_CLIENT (client),
                                         TRUE, FALSE, cancel_shutdown,
                                         NULL);
}
static void
save_yourself_phase2_request_callback (SmsConn   conn,
                                       SmPointer manager_data)
{
        CsmXSMPClient *client = manager_data;

        g_debug ("CsmXSMPClient: Client '%s' received SaveYourselfPhase2Request",
                 client->priv->description);

        client->priv->current_save_yourself = -1;

        /* this is a valid response to SaveYourself and therefore
           may be a response to a QES or ES */
        csm_client_end_session_response (CSM_CLIENT (client),
                                         TRUE, TRUE, FALSE,
                                         NULL);
}
static void
set_description (CsmXSMPClient *client)
{
        SmProp     *prop;
        const char *id;

        prop = find_property (client, SmProgram, NULL);
        id = csm_client_peek_startup_id (CSM_CLIENT (client));

        g_free (client->priv->description);
        if (prop) {
                client->priv->description = g_strdup_printf ("%p [%.*s %s]",
                                                             client,
                                                             prop->vals[0].length,
                                                             (char *)prop->vals[0].value,
                                                             id);
        } else if (id != NULL) {
                client->priv->description = g_strdup_printf ("%p [%s]", client, id);
        } else {
                client->priv->description = g_strdup_printf ("%p", client);
        }
}
static char *
get_desktop_file_path (CsmXSMPClient *client)
{
        SmProp     *prop;
        char       *desktop_file_path = NULL;
        const char *program_name;

        /* XSMP clients using egcsmclient defines a special property
         * pointing to their respective desktop entry file */
        prop = find_property (client, CsmDesktopFile, NULL);

        if (prop) {
                GFile *file = g_file_new_for_uri (prop->vals[0].value);
                desktop_file_path = g_file_get_path (file);
                g_object_unref (file);
                goto out;
        }

        /* If we can't get desktop file from CsmDesktopFile then we
         * try to find the desktop file from its program name */
        prop = find_property (client, SmProgram, NULL);

        if (!prop) {
                goto out;
        }

        program_name = prop->vals[0].value;
        desktop_file_path =
                csm_util_find_desktop_file_for_app_name (program_name,
                                                         TRUE, FALSE);

out:
        g_debug ("CsmXSMPClient: desktop file for client %s is %s",
                 csm_client_peek_id (CSM_CLIENT (client)),
                 desktop_file_path ? desktop_file_path : "(null)");

        return desktop_file_path;
}