/*  xfce4
 *  Copyright (C) 1999 Olivier Fourdan (fourdan@xfce.org)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; 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

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <gdk/gdkwindow.h>
#include <gtk/gtk.h>

#include <libxfce4util/libxfce4util.h>

#include "session-client.h"

#define SM_ID_ARG "--sm-client-id"
#define DPY_ARG "--display"

#ifdef HAVE_LIBSM

static void save_phase_2 (SmcConn smc_conn, SmPointer client_data);
static void interact (SmcConn smc_conn, SmPointer client_data);
static void shutdown_cancelled (SmcConn smc_conn, SmPointer client_data);
static void save_complete (SmcConn smc_conn, SmPointer client_data);
static void die (SmcConn smc_conn, SmPointer client_data);
static void save_yourself (SmcConn smc_conn, SmPointer client_data,
                           int save_style, Bool shutdown, int interact_style,
                           Bool fast);
static void set_clone_restart_commands (SessionClient * session_client);


static IceIOErrorHandler ice_installed_handler = NULL;

/* This is called when data is available on an ICE connection.  */
static gboolean
process_ice_messages (GIOChannel * channel, GIOCondition condition,
                      gpointer client_data)
{
    IceConn connection = (IceConn) client_data;
    IceProcessMessagesStatus status;

    status = IceProcessMessages (connection, NULL, NULL);
    if (status == IceProcessMessagesIOError)
    {
        g_warning ("Disconnected from session manager.");
        /* We were disconnected */
        IceSetShutdownNegotiation (connection, False);
        IceCloseConnection (connection);
    }

    return TRUE;
}

/* This is called when a new ICE connection is made.  It arranges for
   the ICE connection to be handled via the event loop.  */
static void
new_ice_connection (IceConn connection, IcePointer client_data, Bool opening,
                    IcePointer * watch_data)
{
    guint input_id;

    if (opening)
    {
        /* Make sure we don't pass on these file descriptors to any
         * exec'ed children
         */
        GIOChannel *channel;

        fcntl (IceConnectionNumber (connection), F_SETFD,
               fcntl (IceConnectionNumber (connection), F_GETFD,
                      0) | FD_CLOEXEC);

        channel = g_io_channel_unix_new (IceConnectionNumber (connection));

        input_id =
            g_io_add_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
                            process_ice_messages, connection);

        g_io_channel_unref (channel);

        *watch_data = (IcePointer) GUINT_TO_POINTER (input_id);
    }
    else
    {
        input_id = GPOINTER_TO_UINT ((gpointer) * watch_data);

        g_source_remove (input_id);
    }
}

static void
ice_io_error_handler (IceConn connection)
{
    g_warning ("ICE I/O Error");
    if (ice_installed_handler)
        (*ice_installed_handler) (connection);
}

static void
ice_init (void)
{
    static gboolean ice_initted = FALSE;

    if (!ice_initted)
    {
        IceIOErrorHandler default_handler;

        ice_installed_handler = IceSetIOErrorHandler (NULL);
        default_handler = IceSetIOErrorHandler (ice_io_error_handler);

        if (ice_installed_handler == default_handler)
            ice_installed_handler = NULL;

        IceAddConnectionWatch (new_ice_connection, NULL);

        ice_initted = TRUE;
    }
}

#endif


gboolean
session_init (SessionClient * session_client)
{
#ifdef HAVE_LIBSM

    char buf[256];
    unsigned long mask;
    SmcCallbacks callbacks;
    SmProp prop1, prop2, prop3, prop4, prop5, prop6, *props[6];
    SmPropValue prop1val, prop2val, prop3val, prop4val, prop5val, prop6val;
    char pid[32];
    char hint = SmRestartIfRunning;
    char priority = session_client->priority;

    ice_init ();

    mask =
        SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask |
        SmcShutdownCancelledProcMask;

    callbacks.save_yourself.callback = save_yourself;
    callbacks.save_yourself.client_data = (SmPointer) session_client;

    callbacks.die.callback = die;
    callbacks.die.client_data = (SmPointer) session_client;

    callbacks.save_complete.callback = save_complete;
    callbacks.save_complete.client_data = (SmPointer) session_client;

    callbacks.shutdown_cancelled.callback = shutdown_cancelled;
    callbacks.shutdown_cancelled.client_data = (SmPointer) session_client;

    session_client->session_connection =
        SmcOpenConnection (NULL, NULL, SmProtoMajor, SmProtoMinor, mask,
                           &callbacks, (char *) session_client->client_id,
                           &(session_client->given_client_id), 255, buf);

    if (session_client->session_connection == NULL)
    {
        return False;
    }
    else if (session_client->given_client_id == NULL)
    {
        return False;
    }

    if (session_client->client_id
        && strcmp (session_client->client_id,
                   session_client->given_client_id) == 0)
    {
        TRACE ("session_init - SESSION_CLIENT_IDLE");
        session_client->current_state = SESSION_CLIENT_IDLE;
    }
    else
    {
        TRACE ("session_init - SESSION_CLIENT_REGISTERING");
        session_client->current_state = SESSION_CLIENT_REGISTERING;
    }

    gdk_set_sm_client_id (session_client->given_client_id);

    switch (session_client->restart_style)
    {
        case SESSION_RESTART_IF_RUNNING:
            hint = SmRestartIfRunning;
            break;
        case SESSION_RESTART_ANYWAY:
            hint = SmRestartAnyway;
            break;
        case SESSION_RESTART_IMMEDIATELY:
            hint = SmRestartImmediately;
            break;
        case SESSION_RESTART_NEVER:
        default:
            hint = SmRestartNever;
            break;
    }

    prop1.name = SmProgram;
    prop1.type = SmARRAY8;
    prop1.num_vals = 1;
    prop1.vals = &prop1val;
    prop1val.value = session_client->program;
    prop1val.length = strlen (session_client->program);

    prop2.name = SmUserID;
    prop2.type = SmARRAY8;
    prop2.num_vals = 1;
    prop2.vals = &prop2val;
    prop2val.value = (char *) g_get_user_name ();
    prop2val.length = strlen (prop2val.value);

    prop3.name = SmRestartStyleHint;
    prop3.type = SmCARD8;
    prop3.num_vals = 1;
    prop3.vals = &prop3val;
    prop3val.value = &hint;
    prop3val.length = 1;

    sprintf (pid, "%d", getpid ());
    prop4.name = SmProcessID;
    prop4.type = SmARRAY8;
    prop4.num_vals = 1;
    prop4.vals = &prop4val;
    prop4val.value = pid;
    prop4val.length = strlen (prop4val.value);

    prop5.name = SmCurrentDirectory;
    prop5.type = SmARRAY8;
    prop5.num_vals = 1;
    prop5.vals = &prop5val;
    if (session_client->current_directory)
    {
        prop5val.value = (char *) session_client->current_directory;
    }
    else
    {
        prop5val.value = (char *) g_get_home_dir ();
    }
    prop5val.length = strlen (prop5val.value);

    prop6.name = "_GSM_Priority";
    prop6.type = SmCARD8;
    prop6.num_vals = 1;
    prop6.vals = &prop6val;
    prop6val.value = &priority;
    prop6val.length = 1;

    props[0] = &prop1;
    props[1] = &prop2;
    props[2] = &prop3;
    props[3] = &prop4;
    props[4] = &prop5;
    props[5] = &prop6;

    SmcSetProperties ((SmcConn) session_client->session_connection, 6, props);

#endif
    return TRUE;
}

void
session_shutdown (SessionClient * session_client)
{
#ifdef HAVE_LIBSM
    SmProp prop1;
    SmPropValue prop1val;
    SmProp *props[1];
    char hint = SmRestartIfRunning;

    if ((session_client->restart_style == SESSION_RESTART_IMMEDIATELY)
        && (session_client->session_connection != NULL))
    {
        prop1.name = SmRestartStyleHint;
        prop1.type = SmCARD8;
        prop1.num_vals = 1;
        prop1.vals = &prop1val;
        prop1val.value = &hint;
        prop1val.length = 1;

        props[0] = &prop1;

        SmcSetProperties ((SmcConn) session_client->session_connection, 1,
                          props);
    }
#endif
}

void
logout_session (SessionClient * session_client)
{
#ifdef HAVE_LIBSM
    if (!session_client->session_connection)
        return;
    SmcRequestSaveYourself ((SmcConn) session_client->session_connection,
                            SmSaveBoth, 1, SmInteractStyleAny, 0, 1);
#endif
}

#ifdef HAVE_LIBSM
static void
disconnect (SessionClient * session_client)
{
    if (session_client->session_connection != NULL) {
            SmcCloseConnection ((SmcConn) session_client->session_connection,
                                0, NULL);
            session_client->session_connection = NULL;
    }
    session_client->current_state = SESSION_CLIENT_DISCONNECTED;
    gdk_set_sm_client_id (NULL);
}

static void
save_yourself_possibly_done (SessionClient * session_client)
{
    /* We go to phase_2 only if the client needs it, ie it has set a hook for save_phase_2 */
    if (session_client->current_state == SESSION_CLIENT_SAVING_PHASE_1)
    {
        if (session_client->save_phase_2)
        {
            Status status;
            status =
                SmcRequestSaveYourselfPhase2 ((SmcConn) session_client->
                                              session_connection,
                                              save_phase_2,
                                              (SmPointer) session_client);

            if (status)
            {
                TRACE
                    ("save_yourself_possibly_done - SESSION_CLIENT_WAITING_FOR_PHASE_2");
                session_client->current_state =
                    SESSION_CLIENT_WAITING_FOR_PHASE_2;
            }
        }
        else if ((session_client->interact_style != SESSION_INTERACT_NONE)
                 && (session_client->interact))
        {
            Status status;
            status =
                SmcInteractRequest ((SmcConn) session_client->
                                    session_connection, SmDialogError,
                                    interact, (SmPointer) session_client);

            if (status)
            {
                TRACE
                    ("save_yourself_possibly_done - SESSION_CLIENT_WAITING_FOR_INTERACT");
                session_client->current_state =
                    SESSION_CLIENT_WAITING_FOR_INTERACT;
            }
        }
    }
    else if ((session_client->current_state == SESSION_CLIENT_SAVING_PHASE_2)
             && (session_client->interact_style != SESSION_INTERACT_NONE)
             && (session_client->interact))
        /* We go to interact only if 1) we're allowed to do so and 2) the client needs it, ie it has set a hook for interact */
    {
        Status status;
        status =
            SmcInteractRequest ((SmcConn) session_client->session_connection,
                                SmDialogError, interact,
                                (SmPointer) session_client);

        if (status)
        {
            TRACE
                ("save_yourself_possibly_done - SESSION_CLIENT_WAITING_FOR_INTERACT");
            session_client->current_state =
                SESSION_CLIENT_WAITING_FOR_INTERACT;
        }
    }

    if ((session_client->current_state == SESSION_CLIENT_SAVING_PHASE_1)
        || (session_client->current_state == SESSION_CLIENT_SAVING_PHASE_2)
        || (session_client->current_state ==
            SESSION_CLIENT_DONE_WITH_INTERACT))
    {
        set_clone_restart_commands (session_client);
        SmcSaveYourselfDone ((SmcConn) session_client->session_connection,
                             True);

        if (session_client->shutdown)
        {
            TRACE ("save_yourself_possibly_done - SESSION_CLIENT_FROZEN");
            session_client->current_state = SESSION_CLIENT_FROZEN;
        }
        else
        {
            TRACE ("save_yourself_possibly_done - SESSION_CLIENT_IDLE");
            session_client->current_state = SESSION_CLIENT_IDLE;
        }
    }
}

static void
save_phase_2 (SmcConn smc_conn, SmPointer client_data)
{
    SessionClient *session_client = (SessionClient *) client_data;

    TRACE ("entering save_phase_2");
    TRACE ("save_phase_2 - SESSION_CLIENT_SAVING_PHASE_2");
    session_client->current_state = SESSION_CLIENT_SAVING_PHASE_2;

    if (session_client->save_phase_2)
    {
        (*session_client->save_phase_2) (session_client->data);
    }

    save_yourself_possibly_done (session_client);
}

static void
save_yourself (SmcConn smc_conn, SmPointer client_data, int save_style,
               Bool shutdown, int interact_style, Bool fast)
{
    SessionClient *session_client = (SessionClient *) client_data;

    session_client->shutdown = shutdown;

    TRACE ("entering save_yourself");
    /* The first SaveYourself after registering for the first time
     * is a special case (SM specs 7.2).
     */
    if (session_client->current_state == SESSION_CLIENT_REGISTERING)
    {
        TRACE ("save_yourself - SESSION_CLIENT_IDLE");
        session_client->current_state = SESSION_CLIENT_IDLE;

        if ((save_style == SmSaveLocal)
            && (interact_style == SmInteractStyleNone) && !shutdown && !fast)
        {
            set_clone_restart_commands (session_client);
            SmcSaveYourselfDone ((SmcConn) session_client->session_connection,
                                 True);
            return;
        }
    }

    TRACE ("save_yourself - SESSION_CLIENT_SAVING_PHASE_1");
    session_client->current_state = SESSION_CLIENT_SAVING_PHASE_1;
    switch (interact_style)
    {
        case SmInteractStyleAny:
            session_client->interact_style = SESSION_INTERACT_ANY;
            break;
        case SmInteractStyleErrors:
            session_client->interact_style = SESSION_INTERACT_ERRORS;
            break;
        default:
            session_client->interact_style = SESSION_INTERACT_NONE;
            break;
    }
    if (session_client->save_yourself)
    {
        (*session_client->save_yourself) (session_client->data, save_style,
                                          (gboolean) shutdown, interact_style,
                                          (gboolean) fast);
    }
    save_yourself_possibly_done (session_client);
}


static void
die (SmcConn smc_conn, SmPointer client_data)
{
    SessionClient *session_client = (SessionClient *) client_data;

    TRACE ("entering die");
    disconnect (session_client);
    if (session_client->die)
    {
        (*session_client->die) (session_client->data);
    }
    else
    {
        exit (0);
    }
}

static void
save_complete (SmcConn smc_conn, SmPointer client_data)
{
    SessionClient *session_client = (SessionClient *) client_data;

    TRACE ("entering save_complete");
    if (session_client->save_complete)
    {
        (*session_client->save_complete) (session_client->data);
    }
}

static void
shutdown_cancelled (SmcConn smc_conn, SmPointer client_data)
{
    SessionClient *session_client = (SessionClient *) client_data;

    TRACE ("entering shutdown_cancelled");
    if ((session_client->session_connection != NULL)
        && (session_client->current_state != SESSION_CLIENT_IDLE)
        && (session_client->current_state != SESSION_CLIENT_FROZEN))
    {
        SmcSaveYourselfDone ((SmcConn) session_client->session_connection,
                             TRUE);
        TRACE ("shutdown_cancelled - SESSION_CLIENT_IDLE");
        session_client->current_state = SESSION_CLIENT_IDLE;
    }
    if (session_client->shutdown_cancelled)
    {
        (*session_client->shutdown_cancelled) (session_client->data);
    }
}

static void
interact (SmcConn smc_conn, SmPointer client_data)
{
    SessionClient *session_client = (SessionClient *) client_data;

    TRACE ("entering interact");
    if (session_client->interact)
    {
        (*session_client->interact) (session_client->data,
                                     session_client->interact_style);
    }

    TRACE ("interact - SESSION_CLIENT_DONE_WITH_INTERACT");
    session_client->current_state = SESSION_CLIENT_DONE_WITH_INTERACT;

    SmcInteractDone ((SmcConn) session_client->session_connection, False);

    save_yourself_possibly_done (session_client);
}

#define APPEND 1
#define REMOVE 2

static void
set_property_from_command (SessionClient * session_client, char *command,
                           gchar **ptr, gint alter_sm_id)
{
    SmProp prop, *props[1];
    gchar **args;
    gint i;
    gint argc;

    SmPropValue *vals;

    if (!ptr)
        return;

    args = ptr;
    for (argc = 0; *args; args++)
    {
        if (alter_sm_id == APPEND &&
            !g_ascii_strncasecmp (*args, SM_ID_ARG, strlen (SM_ID_ARG)))
        {
            alter_sm_id = FALSE;
        }
        argc++;
    }
    if (alter_sm_id == APPEND)
    {
        argc += 2;
    }
    vals = g_new (SmPropValue, argc);

    i = 0;
    while (*ptr)
    {
        if (alter_sm_id == REMOVE &&
            !g_ascii_strncasecmp (*ptr, SM_ID_ARG, strlen (SM_ID_ARG)))
        {
            /* skip the argument after SM_ID_ARG */
            if (ptr[1] != NULL)
                ptr++;
        }
        else
        {
            vals[i].length = strlen (*ptr);
            vals[i++].value = *ptr;
        }
        ptr++;
    }

    if (alter_sm_id == APPEND)
    {
        vals[i].length = strlen (SM_ID_ARG);
        vals[i++].value = SM_ID_ARG;
        vals[i].length = strlen (session_client->given_client_id);
        vals[i++].value = session_client->given_client_id;
    }

    prop.name = command;
    prop.type = SmLISTofARRAY8;
    prop.vals = vals;
    prop.num_vals = i;

    props[0] = &prop;
    SmcSetProperties ((SmcConn) session_client->session_connection, 1, props);
    g_free (vals);
}

static void
set_clone_restart_commands (SessionClient * session_client)
{
    /* Restart */
    set_property_from_command (session_client, SmRestartCommand,
                               session_client->restart_command, APPEND);

    /* Clone */
    set_property_from_command (session_client, SmCloneCommand,
                               session_client->clone_command ?
                               session_client->clone_command :
                               session_client->restart_command, REMOVE);

    /* Resign */
    set_property_from_command (session_client, SmResignCommand,
                               session_client->resign_command, FALSE);

    /* Discard */
    set_property_from_command (session_client, SmDiscardCommand,
                               session_client->discard_command, FALSE);

    /* Shutdown */
    set_property_from_command (session_client, SmShutdownCommand,
                               session_client->shutdown_command, FALSE);
}

#undef APPEND
#undef REMOVE

#endif

static gchar** safe_strvdup(gchar ** const source)
{
    int     count;
    int     i;
    gchar** result;
    
    if (source == NULL) {
        return NULL;
    }

    count = g_strv_length((gchar **)source);
    result = g_new0(gchar*, count + 1);

    for (i = 0; i < count; ++i) {
        result[i] = g_strdup(source[i]);
    }

    result[count] = NULL;
    return result;
}

SessionClient *
client_session_new_full (gpointer data, SessionRestartStyle restart_style,
                         gchar priority, gchar * client_id, gchar * program,
                         gchar * current_directory, gchar ** restart_command,
                         gchar ** clone_command, gchar ** discard_command,
                         gchar ** resign_command, gchar ** shutdown_command)
{
    SessionClient *session_client = g_new (SessionClient, 1);

    session_client->data = data;
    session_client->restart_style = restart_style;
    session_client->current_state = SESSION_CLIENT_IDLE;
    session_client->interact_style = SESSION_INTERACT_NONE;
    session_client->session_connection = NULL;
    session_client->priority = priority;
    session_client->client_id = g_strdup (client_id);
    session_client->given_client_id = NULL;
    if (program)
    {
        session_client->program = g_strdup (program);
    }
    else
    {
        session_client->program = g_strdup (g_get_prgname ());
    }
    if (current_directory)
    {
        session_client->current_directory = g_strdup (current_directory);
    }
    else
    {
        session_client->current_directory = g_strdup (g_get_home_dir ());
    }
    session_client->clone_command = safe_strvdup (clone_command);
    session_client->resign_command = safe_strvdup (resign_command);
    session_client->restart_command = safe_strvdup (restart_command);
    session_client->discard_command = safe_strvdup (discard_command);
    session_client->shutdown_command = safe_strvdup (shutdown_command);
    session_client->shutdown = FALSE;

    session_client->save_phase_2 = NULL;
    session_client->interact = NULL;
    session_client->shutdown_cancelled = NULL;
    session_client->save_complete = NULL;
    session_client->die = NULL;
    session_client->save_yourself = NULL;

    return session_client;
}

SessionClient *
client_session_new (gint argc, gchar * argv[], gpointer data,
                    SessionRestartStyle restart_style, gchar priority)
{
    SessionClient *session_client;
    gchar **array, **args;
    gchar *client_id = NULL;
    gboolean next_is_client_id = FALSE, had_display = FALSE;
    GdkDisplay *dpy = gdk_display_get_default();
    int i;

    if (argv == NULL)
    {
        g_return_val_if_fail (argc == 0, NULL);

        return NULL;
    }
    else
    {
        array = args = g_new (gchar *, argc + 3);

        for (i = 0; i < argc; i++)
        {
            if (next_is_client_id)
            {
                client_id = argv[i];
                next_is_client_id = FALSE;
            }
            else if (!g_ascii_strncasecmp (argv[i], SM_ID_ARG, strlen (SM_ID_ARG)))
            {
                next_is_client_id = TRUE;
            }
            else
            {
                if (!g_ascii_strncasecmp (argv[i], DPY_ARG, strlen (DPY_ARG)))
                {
                    had_display = TRUE;
                }
                *args++ = argv[i];
            }
        }

        if ((!had_display) && (dpy != NULL))
        {
            *args++ = DPY_ARG;
            *args++ = (gchar *)gdk_display_get_name(dpy);
        }

        *args = NULL;
    }
    session_client =
        client_session_new_full (data, restart_style, priority, client_id,
                                 NULL, NULL, array, NULL, NULL, NULL, NULL);
    g_free (array);

    return session_client;
}

void client_session_free(SessionClient * session_client)
{
    /* session_client->data; */
    /* session_client->session_connection; */
    
    if (session_client->session_connection != NULL &&
        session_client->current_state != SESSION_CLIENT_DISCONNECTED)
    {
        disconnect(session_client);
    }

    g_free(session_client->client_id);
    g_free(session_client->given_client_id);

    g_free(session_client->current_directory);
    g_free(session_client->program);
    g_strfreev(session_client->clone_command);
    g_strfreev(session_client->resign_command);
    g_strfreev(session_client->restart_command);
    g_strfreev(session_client->discard_command);
    g_strfreev(session_client->shutdown_command);
    g_free(session_client);
}

void client_session_set_save_phase_2_callback(SessionClient * session_client, save_phase_2_callback value)
{
    session_client->save_phase_2 = value;
}

void client_session_set_interact_callback(SessionClient * session_client, interact_callback value)
{
    session_client->interact = value;
}

void client_session_set_shutdown_cancelled_callback(SessionClient * session_client, shutdown_cancelled_callback value)
{
    session_client->shutdown_cancelled = value;
}

void client_session_set_save_complete_callback(SessionClient * session_client, save_complete_callback value)
{
    session_client->save_complete = value;
}

void client_session_set_die_callback(SessionClient * session_client, die_callback value)
{
    session_client->die = value;
}

void client_session_set_save_yourself_callback(SessionClient * session_client, save_yourself_callback value)
{
    session_client->save_yourself = value;
}

void client_session_set_data(SessionClient * session_client, gpointer value)
{
    session_client->data = value;
}

/*void client_session_set_session_connection(SessionClient * session_client, gpointer value)*/

/*void client_session_set_current_state(SessionClient * session_client, SessionClientState value)*/

void client_session_set_restart_style(SessionClient * session_client, SessionRestartStyle value)
{
#ifdef HAVE_LIBSM
    SmProp prop, *props[1];
    SmPropValue propval;
    char hint = SmRestartIfRunning;
#endif /* HAVE_LIBSM */    
    session_client->restart_style = value;

#ifdef HAVE_LIBSM
    if (session_client->session_connection != NULL) {
        switch (session_client->restart_style) {
            case SESSION_RESTART_IF_RUNNING:
                hint = SmRestartIfRunning;
                break;
            case SESSION_RESTART_ANYWAY:
                hint = SmRestartAnyway;
                break;
            case SESSION_RESTART_IMMEDIATELY:
                hint = SmRestartImmediately;
                break;
            case SESSION_RESTART_NEVER:
            default:
                hint = SmRestartNever;
                break;
        }

        prop.name = SmRestartStyleHint;
        prop.type = SmCARD8;
        prop.num_vals = 1;
        prop.vals = &propval;
        propval.value = &hint;
        propval.length = 1;
        props[0] = &prop;

        SmcSetProperties ((SmcConn) session_client->session_connection, 1, props);
    }
#endif /* HAVE_LIBSM */    
}

/* void client_session_set_interact_style(SessionClient * session_client, SessionInteractStyle value) */

void client_session_set_priority(SessionClient * session_client, gchar value)
{
#ifdef HAVE_LIBSM
    SmProp prop, *props[1];
    SmPropValue propval;
#endif /* HAVE_LIBSM */    

    session_client->priority = value;
#ifdef HAVE_LIBSM
    if (session_client->session_connection != NULL) {
        prop.name = "_GSM_Priority";
        prop.type = SmCARD8;
        prop.num_vals = 1;
        prop.vals = &propval;
        propval.value = &session_client->priority;
        propval.length = 1;
        props[0] = &prop;

        SmcSetProperties ((SmcConn) session_client->session_connection, 1, props);
    }
#endif /* HAVE_LIBSM */    
}

void client_session_set_client_id(SessionClient * session_client, gchar const* value)
{
    if (session_client->session_connection != NULL) {
        g_error("SessionClient: changing \"client_id\" while connected to session manager is not possible.");
        return;
    }
    
    if (session_client->client_id != value) {
        g_free(session_client->client_id);
        session_client->client_id = g_strdup(value);
    }
}

/*void client_session_set_given_client_id(SessionClient * session_client, gchar const* value) */

void client_session_set_current_directory(SessionClient * session_client, gchar const* value)
{
#ifdef HAVE_LIBSM
    SmProp prop, *props[1];
    SmPropValue propval;
#endif /* HAVE_LIBSM */    

    if (session_client->current_directory != value) {
        g_free(session_client->current_directory);
        session_client->current_directory = NULL;
        if (value) {
            session_client->current_directory = g_strdup(value);
        }
    }
#ifdef HAVE_LIBSM
    if (session_client->session_connection != NULL) {
        prop.name = SmCurrentDirectory;
        prop.type = SmARRAY8;
        prop.num_vals = 1;
        prop.vals = &propval;
        if (session_client->current_directory) {
            propval.value = (char *) session_client->current_directory;
        }
        else {
            propval.value = (char *) g_get_home_dir ();
        }
        propval.length = strlen (propval.value);
        props[0] = &prop;

        SmcSetProperties ((SmcConn) session_client->session_connection, 1, props);
    }
#endif /* HAVE_LIBSM */    
}

void client_session_set_program(SessionClient * session_client, gchar const* value)
{
#ifdef HAVE_LIBSM
    SmProp prop, *props[1];
    SmPropValue propval;
#endif /* HAVE_LIBSM */    
    
    if (session_client->program != value) {
        g_free(session_client->program);
        session_client->program = g_strdup(value);
    }
#ifdef HAVE_LIBSM
    if (session_client->session_connection != NULL) {
        prop.name = SmProgram;
        prop.type = SmARRAY8;
        prop.num_vals = 1;
        prop.vals = &propval;
        propval.value = session_client->program;
        propval.length = strlen (session_client->program);

        SmcSetProperties ((SmcConn) session_client->session_connection, 1, props);
    }
#endif /* HAVE_LIBSM */    
}

static gchar **copy_command(gchar **command, gchar** const value)
{
    if (command != value)
    {
        if (command != NULL) {
            g_strfreev(command);
            command = NULL;
        }

        if (value != NULL) {
            command = safe_strvdup(value);
        }
    }

    return command;
}
    
void client_session_set_clone_command(SessionClient * session_client, gchar** const value)
{
    session_client->clone_command = copy_command(session_client->clone_command, value);
}

void client_session_set_resign_command(SessionClient * session_client, gchar** const value)
{
    session_client->resign_command = copy_command(session_client->resign_command, value);
}

void client_session_set_restart_command(SessionClient * session_client, gchar** const value)
{
    session_client->restart_command = copy_command(session_client->restart_command, value);
}

void client_session_set_discard_command(SessionClient * session_client, gchar** const value)
{
    session_client->discard_command = copy_command(session_client->discard_command, value);
}

void client_session_set_shutdown_command(SessionClient * session_client, gchar** const value)
{
    session_client->shutdown_command = copy_command(session_client->shutdown_command, value);
}

save_phase_2_callback client_session_get_save_phase_2_callback(SessionClient * session_client)
{
    return session_client->save_phase_2;
}

interact_callback client_session_get_interact_callback(SessionClient * session_client)
{
    return session_client->interact;
}

shutdown_cancelled_callback client_session_get_shutdown_cancelled_callback(SessionClient * session_client)
{
    return session_client->shutdown_cancelled;
}

save_complete_callback client_session_get_save_complete_callback(SessionClient * session_client)
{
    return session_client->save_complete;
}

die_callback client_session_get_die_callback(SessionClient * session_client)
{
    return session_client->die;
}

save_yourself_callback client_session_get_save_yourself_callback(SessionClient * session_client)
{
    return session_client->save_yourself;
}

gpointer client_session_get_data(SessionClient * session_client)
{
    return session_client->data;
}

gpointer client_session_get_session_connection(SessionClient * session_client)
{
    return session_client->session_connection;
}

SessionClientState client_session_get_current_state(SessionClient * session_client)
{
    return session_client->current_state;
}

SessionRestartStyle client_session_get_restart_style(SessionClient * session_client)
{
    return session_client->restart_style;
}

SessionInteractStyle client_session_get_interact_style(SessionClient * session_client)
{
    return session_client->interact_style;
}

gchar client_session_get_priority(SessionClient * session_client)
{
    return session_client->priority;
}

gchar const* client_session_get_client_id(SessionClient * session_client)
{
    return session_client->client_id;
}

gchar const* client_session_get_given_client_id(SessionClient * session_client)
{
    return session_client->given_client_id;
}

gchar const* client_session_get_current_directory(SessionClient * session_client)
{
    return session_client->current_directory;
}

gchar const* client_session_get_program(SessionClient * session_client)
{
    return session_client->program;
}

G_CONST_RETURN gchar* G_CONST_RETURN * client_session_get_clone_command(SessionClient * session_client)
{
    return (const gchar * const *) session_client->clone_command;
}

G_CONST_RETURN gchar* G_CONST_RETURN * client_session_get_resign_command(SessionClient * session_client)
{
    return (const gchar * const *) session_client->resign_command;
}

G_CONST_RETURN gchar* G_CONST_RETURN * client_session_get_restart_command(SessionClient * session_client)
{
    return (const gchar * const *) session_client->restart_command;
}

G_CONST_RETURN gchar* G_CONST_RETURN * client_session_get_discard_command(SessionClient * session_client)
{
    return (const gchar * const *) session_client->discard_command;
}

G_CONST_RETURN gchar* G_CONST_RETURN * client_session_get_shutdown_command(SessionClient * session_client)
{
    return (const gchar * const *) session_client->shutdown_command;
}

