/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2004 Hiroyuki Ikezoe
 *
 *  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 /* HAVE_CONFIG_H */

#include "kz-app.h"
#include <stdlib.h>
#ifdef USE_SSL
#include <gcrypt.h>
#include <gnutls/gnutls.h> 
#include <errno.h>
#include <pthread.h>
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
#ifdef HAVE_LIBSM
#  include <X11/SM/SMlib.h>
#  include <X11/ICE/ICElib.h>
#  ifdef G_OS_UNIX
#     include <sys/types.h>
#  endif
#  ifdef HAVE_UNISTD_H
#    include <unistd.h>
#  endif
#  include <fcntl.h>
#endif /* HAVE_LIBSM */

#include <glib/gi18n.h>
#include "kazehakase.h"
#include "kz-actions-tab.h"
#include "kz-ext.h"
#include "kz-icons.h"
#include "kz-session.h"
#include "kz-tab-label.h"
#include "kz-window.h"
#include "glib-utils.h"
#include "utils.h"

enum {
	PROP_0,
	PROP_ARGC,
	PROP_ARGV
};

typedef struct _KzAppPrivate	KzAppPrivate;
struct _KzAppPrivate
{
	gint	        argc;
	gchar         **argv;
	GList          *window_list;
	KzFavicon      *favicon;
	KzSearch       *search;
	KzRootBookmark *bookmarks;
	KzProfile      *profile;
	KzProfile      *proxy;
#if USE_MIGEMO
	KzMigemo       *migemo;
#endif
#ifdef HAVE_LIBSM
	gpointer        smc_conn;
	gchar          *session_id;
#endif /* HAVE_LIBSM */
};

#define KZ_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_APP, KzAppPrivate))

static GObject *constructor  (GType type,
                              guint n_props,
                              GObjectConstructParam *props);
static void     dispose      (GObject *object);
static void     set_property (GObject *object,
                              guint prop_id,
                              const GValue *value,
                              GParamSpec *pspec);
static void     get_property (GObject *object,
                              guint prop_id,
                              GValue *value,
                              GParamSpec *pspec);

static void     cb_destroy_window (GtkObject *object, gpointer data);

#ifdef HAVE_LIBSM
static void     connect_sm   (KzApp *app);
#endif /* HAVE_LIBSM */

static KzApp    *the_kzapp = NULL;

G_DEFINE_TYPE(KzApp, kz_app, G_TYPE_OBJECT)

static void
kz_app_class_init (KzAppClass *klass)
{
	GObjectClass *object_class;

	kz_app_parent_class = g_type_class_peek_parent(klass);
	object_class = (GObjectClass *) klass;

	object_class->constructor  = constructor;
	object_class->dispose	   = dispose;
	object_class->set_property = set_property;
	object_class->get_property = get_property;
	
	g_object_class_install_property(
		object_class,
		PROP_ARGC,
		g_param_spec_int(
			"argc",
			_("Argument number"),
			_("The number of argument of program"),
			0, G_MAXINT,
			0,
			G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
	g_object_class_install_property(
		object_class,
		PROP_ARGV,
		g_param_spec_pointer ("argv",
				      _("Argument list"),
				      _("The argument list of program"),
				      G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); 
	g_type_class_add_private(object_class, sizeof(KzAppPrivate));
}

static GObject *
constructor (GType                  type,
             guint                  n_props,
             GObjectConstructParam *props)
{
	GObject *object;

	if (!the_kzapp) 
	{
		GObjectClass *klass = G_OBJECT_CLASS(kz_app_parent_class);
		object = klass->constructor(type, n_props, props);

#ifdef HAVE_LIBSM
		/* session management */
		connect_sm(KZ_APP(object));
#endif /* HAVE_LIBSM */

		the_kzapp = KZ_APP(object);
	}
	else
	{
		object = g_object_ref(G_OBJECT(the_kzapp));
	}
	return object;
}

static void
set_property (GObject *object,
              guint prop_id,
              const GValue *value,
              GParamSpec *pspec)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(object);

	switch (prop_id)
	{
	case PROP_ARGC:
		priv->argc = g_value_get_int(value);
		break;
	case PROP_ARGV:
		priv->argv = g_strdupv(g_value_get_pointer(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


static void
get_property (GObject *object,
              guint prop_id,
              GValue *value,
              GParamSpec *pspec)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(object);

	switch (prop_id)
	{
	case PROP_ARGC:
		g_value_set_int(value, priv->argc);
		break;
	case PROP_ARGV:
		g_value_set_pointer(value, priv->argv);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


static void
prepare_dir (void)
{
	/* for thumbnails */
	make_thumbnails_dir();
	
	/* for favicon */
	make_dir("favicon");

	/* for popup */
	make_dir("popup");

	/* for smart bookmark input history */
	make_dir("smartbookmark_history");
	
	/* for storing form data */
	make_dir("form_data");
}

static void
kz_app_init (KzApp *app)
{
	gchar *sysconf_file, *conf_file;
	gchar *bookmark_file, *sys_bookmark_file;
	gchar *clip_file, *current_session_file;
	gchar *bookmark_bar_file, *sys_bookmark_bar_file;
	gchar *smartbookmark_file, *sys_smartbookmark_file;
	gchar *accel_prefs_file;
	gchar *proxy_file, *sysproxy_file;
	gchar *search_engine;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	priv->window_list = NULL;
	priv->favicon = kz_favicon_get_instance();

	/* load prefs */
	sysconf_file = g_strdup(KZ_SYSCONFDIR G_DIR_SEPARATOR_S"kzrc");
	conf_file = g_build_filename(g_get_home_dir(),
				     "."PACKAGE,
				     "kzrc", NULL);
	
	priv->profile = kz_profile_open(conf_file, sysconf_file);

	/* prepare history search engine */
	search_engine = kz_profile_get_string(priv->profile, "History" , "search_engine");
	if (search_engine)
	{
		if (!strcmp(search_engine, "hyperestraier") ||
		    !strcmp(search_engine, "rast"))
			priv->search = kz_search_new(search_engine);

		if (priv->search)
		{
			if (!kz_search_exist_index_dir(priv->search))
				kz_search_make_index(priv->search);
		}
		g_free(search_engine);
	}

	/* load bookmarks */
	bookmark_file         = g_build_filename(g_get_home_dir(),
					    	 "."PACKAGE,
					    	 "bookmarks.xml",
					    	 NULL);
	sys_bookmark_file     = g_build_filename(KZ_SYSCONFDIR,
					    	 "bookmarks.xml",
					    	 NULL);

        bookmark_bar_file     = g_build_filename(g_get_home_dir(),
					    	 "."PACKAGE,
						 "bookmarkbar.xml",
					    	 NULL);
	sys_bookmark_bar_file = g_build_filename(KZ_SYSCONFDIR,
					    	 "bookmarkbar.xml",
					    	 NULL);
	clip_file             = g_build_filename(g_get_home_dir(),
					    	 "."PACKAGE,
						 "clip.xml",
					    	 NULL);
        smartbookmark_file    = g_build_filename(g_get_home_dir(),
					    	 "."PACKAGE,
					    	 "smartbookmarks.xml",
					    	 NULL);
	sys_smartbookmark_file= g_build_filename(KZ_SYSCONFDIR,
					    	 "smartbookmarks.xml",
					    	 NULL);
	
	current_session_file  = g_build_filename(g_get_home_dir(),
						 "."PACKAGE,
						 "current_session.xml",
						 NULL);

	priv->bookmarks = kz_root_bookmark_new(bookmark_file, sys_bookmark_file,
					       clip_file, NULL);

	kz_root_bookmark_add_smartbookmark_file(priv->bookmarks,
						smartbookmark_file,
						sys_smartbookmark_file);
	kz_root_bookmark_add_bookmark_bar_file(priv->bookmarks,
					       bookmark_bar_file,
					       sys_bookmark_bar_file);
	kz_root_bookmark_add_current_session_file(priv->bookmarks,
						  current_session_file);
	kz_session_set_profile(KZ_SESSION(priv->bookmarks->current_session),
			       priv->profile);

	/* Load other prefs... */
	accel_prefs_file = g_build_filename(g_get_home_dir(),
					    "."PACKAGE,
					    "keyaccelrc",
					    NULL);
	gtk_accel_map_load(accel_prefs_file);

	sysproxy_file = g_build_filename(KZ_SYSCONFDIR, "proxyrc", NULL);
	proxy_file = g_build_filename(g_get_home_dir(),
				      "."PACKAGE,
				      "proxyrc", NULL);
	priv->proxy = kz_profile_open(proxy_file, sysproxy_file);

	kz_icons_init();

	/* make dirs */
	prepare_dir();

#ifdef USE_SSL
        /* initialize gnutls. this function should be called once. */
	gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
	gnutls_global_init();
#endif

#if USE_MIGEMO
	priv->migemo = kz_migemo_new();
#endif

	g_free(sysconf_file);
	g_free(conf_file);
	g_free(bookmark_file);
	g_free(sys_bookmark_file);
	g_free(clip_file);
	g_free(bookmark_bar_file);
	g_free(sys_bookmark_bar_file);
	g_free(smartbookmark_file);
	g_free(sys_smartbookmark_file);
	g_free(current_session_file);
	g_free(accel_prefs_file);
	g_free(sysproxy_file);
	g_free(proxy_file);
}


static void
clean_history_cache (void)
{
	guint limit_days = 30;
	gboolean limit = FALSE;
	time_t limit_seconds;

	KZ_CONF_GET("History", "limit_days", limit_days, INT);
	KZ_CONF_GET("History", "limit_cache", limit, BOOL);
	if (limit)
	{
		gchar *history_timestamp, *image_dir;
		limit_seconds = limit_days * 86400;
		history_timestamp = g_build_filename(g_get_home_dir(),
					  HISTORY_DIR,
					  "timestamp",
					  NULL);
		purge_history_file_by_timestamp(history_timestamp, limit_seconds);
		g_free(history_timestamp);

		/* parge popup files */
		image_dir = g_build_filename(g_get_home_dir(),
					IMAGE_DIR,
					NULL);
		purge_history_file(image_dir, limit_seconds);
		g_free(image_dir);
	}
}

static void
dispose (GObject *object)
{
	gchar *accel_prefs_file;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(object);

	accel_prefs_file = g_build_filename(g_get_home_dir(),
					    "."PACKAGE,
					    "keyaccelrc",
					    NULL);

	gtk_accel_map_save(accel_prefs_file);
	g_free(accel_prefs_file);

	/* clean history cache */
	clean_history_cache();

	kz_root_bookmark_save_all(priv->bookmarks);
	/* freeze session for storing last saving session */
	kz_session_freeze(KZ_SESSION(priv->bookmarks->current_session));
	g_object_unref(G_OBJECT(priv->bookmarks));

	kz_profile_close(priv->profile);
	kz_profile_close(priv->proxy);

	if (priv->search)
		g_object_unref(priv->search);
	if (priv->favicon)
		g_object_unref(priv->favicon);

	if (priv->argv)
		g_strfreev(priv->argv);

#ifdef USE_SSL
	gnutls_global_deinit();
#endif

#ifdef USE_MIGEMO
	if (priv->migemo)
	{
		kz_migemo_free(priv->migemo);
		priv->migemo = NULL;
	}
#endif	

#ifdef HAVE_LIBSM
	if (priv->smc_conn)
		SmcCloseConnection ((SmcConn) priv->smc_conn, 0, NULL);
#endif /* HAVE_LIBSM */

	priv->profile   = NULL;
	priv->proxy     = NULL;
	priv->bookmarks = NULL;
	priv->search    = NULL;
	priv->favicon   = NULL;
	priv->argv	= NULL;
	
	if (G_OBJECT_CLASS (kz_app_parent_class)->dispose)
		G_OBJECT_CLASS (kz_app_parent_class)->dispose(object);
}


KzApp *
kz_app_new (gint argc, gchar **argv)
{
	KzApp *kzapp = g_object_new(KZ_TYPE_APP,
				    "argc", argc,
				    "argv", argv,
				    NULL);

	return kzapp;
}

static void
cb_window_map (KzWindow *kz, GtkAction *action)
{
	gtk_action_activate(action);
	g_signal_handlers_disconnect_by_func
		(kz, G_CALLBACK(cb_window_map), action);
}

void
kz_app_save_session (KzApp *app)
{
	KzAppPrivate *priv;

	g_return_if_fail(KZ_IS_APP(app));

	priv = KZ_APP_GET_PRIVATE(app);
	kz_bookmark_file_save(KZ_BOOKMARK_FILE(priv->bookmarks->current_session));
}

GtkWidget *
kz_app_restore_session (KzApp *app)
{
	GList *list;
	const GList *node, *window_node;
	const gchar *location;
	KzAppPrivate *priv;
	KzBookmark *session;

	g_return_val_if_fail(KZ_IS_APP(app), NULL);

	priv = KZ_APP_GET_PRIVATE(app);

	/* close all tabs before loading session file */
	kz_session_freeze(KZ_SESSION(priv->bookmarks->current_session));
	for (node = priv->window_list; node; node = g_list_next(node))
		kz_window_close_all_tab(KZ_WINDOW(node->data));
	kz_session_thaw(KZ_SESSION(priv->bookmarks->current_session));

	session = priv->bookmarks->current_session;
	location = kz_bookmark_file_get_location(KZ_BOOKMARK_FILE(session));
	if (g_file_test(location, G_FILE_TEST_EXISTS))
		kz_bookmark_file_load(KZ_BOOKMARK_FILE(session));

	list = kz_bookmark_get_children(session);

	window_node = priv->window_list;
	/* windows */
	for (node = list; node; node = g_list_next(node))
	{
		KzWindow *window;
		KzBookmark *window_bookmark = KZ_BOOKMARK(node->data);

		if (!window_node)
		{
			window = KZ_WINDOW(kz_app_create_new_window(app, NULL));
			gtk_widget_show(GTK_WIDGET(window));
		}
		else
		{
			window = KZ_WINDOW(window_node->data);
			window_node = g_list_next(window_node);
		}

		kz_window_restore_tabs(window, window_bookmark);
	}
	g_list_free(list);

	/* close extra windows */
	for (;window_node; window_node = g_list_next(window_node))
	{
		KzWindow *window = KZ_WINDOW(window_node->data);
		gtk_widget_destroy(GTK_WIDGET(window));
	}

	/* if there is no window, create a window */
	if (!priv->window_list)
		kz_app_create_new_window(app, NULL);

	return GTK_WIDGET(priv->window_list->data);
}

GtkWidget *
kz_app_create_new_window (KzApp *app, const gchar *uri)
{
	GtkWidget *window;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	window = kz_window_new(uri);

	g_signal_connect(window, "destroy",
			 G_CALLBACK(cb_destroy_window), app);
	priv->window_list = g_list_append(priv->window_list, window);

	return window;
}

static KzUILevel
kz_ui_level_from_str (const gchar *level)
{
	if (!level || !*level)
		return KZ_UI_LEVEL_INVALID;

	if (!strcmp(level, "custom"))
		return KZ_UI_LEVEL_CUSTOM;
	else if (!strcmp(level, "expert"))
		return KZ_UI_LEVEL_EXPERT;
	else if (!strcmp(level, "medium"))
		return KZ_UI_LEVEL_MEDIUM;
	else if (!strcmp(level, "beginner"))
		return KZ_UI_LEVEL_BEGINNER;

	return KZ_UI_LEVEL_INVALID;
}

#if 0
static const gchar *
kz_ui_level_to_str (KzUILevel level)
{
	switch (level)
	{
	case KZ_UI_LEVEL_CUSTOM:
		return "custom";
	case KZ_UI_LEVEL_EXPERT:
		return "expert";
	case KZ_UI_LEVEL_MEDIUM:
		return "medium";
	case KZ_UI_LEVEL_BEGINNER:
		return "beginner";
	default:
		break;
	}

	return NULL;
}
#endif

KzUILevel
kz_app_get_ui_level (KzApp *app)
{
	KzUILevel val;
	gchar level[16];
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	g_return_val_if_fail(priv->profile, KZ_UI_LEVEL_BEGINNER);

	level[0] = '\0';
	kz_profile_get_value(priv->profile,
			     "Global", "ui_level",
			     level, sizeof(level),
			     KZ_PROFILE_VALUE_TYPE_STRING);

	val = kz_ui_level_from_str(level);
	if (!val)
		return KZ_UI_LEVEL_BEGINNER;

	return val;
}

KzFavicon *
kz_app_get_favicon (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->favicon;
}

KzSearch *
kz_app_get_search (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->search;
}

KzRootBookmark *
kz_app_get_root_bookmark (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->bookmarks;
}

KzProfile *
kz_app_get_profile (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->profile;
}

KzProfile *
kz_app_get_proxy (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->proxy;
}

const GList *
kz_app_get_window_list (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->window_list;
}

GtkWidget *
kz_app_get_window_from_tab (KzApp *app, GtkWidget *tab_widget)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);
	GList *node;

	for (node = (GList*)priv->window_list; node; node = g_list_next (node))
	{
		KzWindow *kz = node->data;
		GtkWidget *label;

		if (!KZ_IS_WINDOW(kz)) continue;

		label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
						   tab_widget);
		if (label)
			return GTK_WIDGET(kz);
	}

	return NULL;
}

static void
cb_destroy_window (GtkObject *object, gpointer data)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(data);

	g_signal_handlers_disconnect_by_func(object,
					     G_CALLBACK(cb_destroy_window), data);
	priv->window_list = g_list_remove(priv->window_list, object);

	if (!priv->window_list)
	{
		static gboolean main_quited = FALSE;
		if (!main_quited)
		{
			gtk_main_quit();
			main_quited = TRUE;
		}
	}
}

#if USE_MIGEMO
KzMigemo *
kz_app_get_migemo (KzApp *app)
{
	return KZ_APP_GET_PRIVATE(app)->migemo;
}
#endif

#ifdef HAVE_LIBSM

/* these codes are taken from libgnomeui.*/

static void ice_io_error_handler (IceConn connection);

static void new_ice_connection (IceConn connection, IcePointer client_data,
				Bool opening, IcePointer *watch_data);

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

	status = IceProcessMessages (connection, NULL, NULL);

	if (status == IceProcessMessagesIOError)
	{
		IcePointer context = IceGetConnectionContext (connection);

		if (context && GTK_IS_OBJECT (context))
		{
			guint disconnect_id = g_signal_lookup ("disconnect", G_OBJECT_TYPE (context));

			if (disconnect_id > 0)
				g_signal_emit (context, disconnect_id, 0);
		}
		else
		{
			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)
	{
		GIOChannel *channel;
		/* Make sure we don't pass on these file descriptors to any
		   exec'ed children */
		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 IceIOErrorHandler ice_installed_handler;

/* We call any handler installed before (or after) gnome_ice_init but
   avoid calling the default libICE handler which does an exit() */
static void
ice_io_error_handler (IceConn connection)
{
	if (ice_installed_handler)
		(*ice_installed_handler) (connection);
}

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

	if (! ice_init)
	{
		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_init = TRUE;
	}
}

static void
save_yourself_callback (SmcConn   smc_conn,
			SmPointer client_data,
			int       save_style,
			gboolean  shutdown,
			int       interact_style,
			gboolean  fast)
{
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(client_data);

	SmcSaveYourselfDone((SmcConn) priv->smc_conn, TRUE);
}

static void
die_callback (SmcConn smc_conn, SmPointer client_data)
{
	gtk_main_quit();
}

static void
save_complete_callback (SmcConn smc_conn, SmPointer client_data)
{
}

static void
shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data)
{
}

static void
session_set_value (SmcConn      smc_conn,
		   gchar       *name,
		   char        *type,
		   int          num_vals,
		   SmPropValue *vals)
{
	SmProp *proplist[1];
	SmProp prop;

	prop.name = name;
	prop.type = type;
	prop.num_vals = num_vals;
	prop.vals = vals;

	proplist[0]= &prop;
	SmcSetProperties ((SmcConn) smc_conn, 1, proplist);
}

static void
session_set_string (SmcConn smc_conn, gchar *name, gchar *value)
{
	SmPropValue val;

	g_return_if_fail (name);

	val.length = strlen (value)+1;
	val.value  = value;

	session_set_value (smc_conn, name, SmARRAY8, 1, &val);
}

static void
session_set_gchar (SmcConn smc_conn, gchar *name, gchar value)
{
	SmPropValue val;

	g_return_if_fail (name);

	val.length = 1;
	val.value  = &value;

	session_set_value (smc_conn, name, SmCARD8, 1, &val);
}

static void
session_set_clone_command (KzApp *app)
{
	SmPropValue *vals;
	gint i;
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	vals = g_new(SmPropValue, priv->argc);

	for (i = 0; i < priv->argc; i++)
	{
		vals[i].length = strlen(priv->argv[i]);
		vals[i].value  = priv->argv[i];
	}
	session_set_value(priv->smc_conn, SmCloneCommand, SmLISTofARRAY8, i, vals);
	session_set_value(priv->smc_conn, SmRestartCommand, SmLISTofARRAY8, i, vals);

	g_free(vals);
}

static void
connect_sm (KzApp *app)
{
	SmcCallbacks      callbacks;
	gchar            *session_id;
	gchar error_string[256] = "";
	KzAppPrivate *priv = KZ_APP_GET_PRIVATE(app);

	g_return_if_fail(app);

	if (!g_getenv("SESSION_MANAGER"))
		return;

	ice_init();

	callbacks.save_yourself.callback      = save_yourself_callback;
	callbacks.die.callback                = die_callback;
	callbacks.save_complete.callback      = save_complete_callback;
	callbacks.shutdown_cancelled.callback = shutdown_cancelled_callback;

	callbacks.save_yourself.client_data =
		callbacks.die.client_data =
		callbacks.save_complete.client_data =
		callbacks.shutdown_cancelled.client_data = (SmPointer) app;

	priv->session_id = NULL;
	priv->smc_conn= (gpointer)
		SmcOpenConnection(NULL, app,
				SmProtoMajor, SmProtoMinor,
				SmcSaveYourselfProcMask | SmcDieProcMask |
				SmcSaveCompleteProcMask |
				SmcShutdownCancelledProcMask,
				&callbacks,
				priv->session_id, &session_id,
				sizeof(error_string), error_string);

	if (error_string[0])
	{
		g_warning("While connecting to session manager:\n%s.",
				error_string);
	}
	if (priv->smc_conn)
	{
		gchar *tmp;

		gdk_set_sm_client_id(session_id);

		tmp = g_get_current_dir();
		session_set_string(priv->smc_conn, SmCurrentDirectory, tmp);
		g_free(tmp);

		tmp = g_strdup_printf("%d", (int) getpid());
		session_set_string(priv->smc_conn, SmProcessID, tmp);
		g_free(tmp);

		session_set_string(priv->smc_conn, SmUserID, (gchar *)g_get_user_name());

		session_set_gchar(priv->smc_conn, SmRestartStyleHint, (gchar) SmRestartIfRunning);
		session_set_string(priv->smc_conn, SmProgram, g_get_prgname());

		session_set_clone_command(app);
		g_free(session_id);
	}
}
#endif /* HAVE_LIBSM */
