1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 * gsm-session-save.c
3 * Copyright (C) 2008 Lucas Rocha.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <glib.h>
22 #include <glib/gstdio.h>
23 #include <gio/gio.h>
24
25 #include "gsm-app.h"
26 #include "gsm-util.h"
27 #include "gsm-autostart-app.h"
28 #include "gsm-client.h"
29
30 #include "gsm-session-save.h"
31
32 #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager"
33 #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot"
34
35
36 static gboolean gsm_session_clear_saved_session (const char *directory,
37 GHashTable *discard_hash);
38
39 typedef struct {
40 const char *dir;
41 GHashTable *discard_hash;
42 GsmStore *app_store;
43 GError **error;
44 } SessionSaveData;
45
46 static gboolean
_app_has_app_id(const char * id,GsmApp * app,const char * app_id_a)47 _app_has_app_id (const char *id,
48 GsmApp *app,
49 const char *app_id_a)
50 {
51 const char *app_id_b;
52
53 app_id_b = gsm_app_peek_app_id (app);
54 return g_strcmp0 (app_id_a, app_id_b) == 0;
55 }
56
57 static gboolean
save_one_client(char * id,GObject * object,SessionSaveData * data)58 save_one_client (char *id,
59 GObject *object,
60 SessionSaveData *data)
61 {
62 GsmClient *client;
63 GKeyFile *keyfile;
64 GsmApp *app = NULL;
65 const char *app_id;
66 char *path = NULL;
67 char *filename = NULL;
68 char *contents = NULL;
69 gsize length = 0;
70 char *discard_exec;
71 GError *local_error;
72
73 client = GSM_CLIENT (object);
74
75 local_error = NULL;
76
77 app_id = gsm_client_peek_app_id (client);
78 if (!IS_STRING_EMPTY (app_id)) {
79 if (g_str_has_suffix (app_id, ".desktop"))
80 filename = g_strdup (app_id);
81 else
82 filename = g_strdup_printf ("%s.desktop", app_id);
83
84 path = g_build_filename (data->dir, filename, NULL);
85
86 app = (GsmApp *)gsm_store_find (data->app_store,
87 (GsmStoreFunc)_app_has_app_id,
88 (char *)app_id);
89 }
90 keyfile = gsm_client_save (client, app, &local_error);
91
92 if (keyfile == NULL || local_error) {
93 goto out;
94 }
95
96 contents = g_key_file_to_data (keyfile, &length, &local_error);
97
98 if (local_error) {
99 goto out;
100 }
101
102 if (!path || g_file_test (path, G_FILE_TEST_EXISTS)) {
103 if (filename)
104 g_free (filename);
105 if (path)
106 g_free (path);
107
108 filename = g_strdup_printf ("%s.desktop",
109 gsm_client_peek_startup_id (client));
110 path = g_build_filename (data->dir, filename, NULL);
111 }
112
113 g_file_set_contents (path,
114 contents,
115 length,
116 &local_error);
117
118 if (local_error) {
119 goto out;
120 }
121
122 discard_exec = g_key_file_get_string (keyfile,
123 G_KEY_FILE_DESKTOP_GROUP,
124 GSM_AUTOSTART_APP_DISCARD_KEY,
125 NULL);
126 if (discard_exec) {
127 g_hash_table_insert (data->discard_hash,
128 discard_exec, discard_exec);
129 }
130
131 g_debug ("GsmSessionSave: saved client %s to %s", id, filename);
132
133 out:
134 if (keyfile != NULL) {
135 g_key_file_free (keyfile);
136 }
137
138 g_free (contents);
139 g_free (filename);
140 g_free (path);
141
142 /* in case of any error, stop saving session */
143 if (local_error) {
144 g_propagate_error (data->error, local_error);
145 g_error_free (local_error);
146
147 return TRUE;
148 }
149
150 return FALSE;
151 }
152
153 void
gsm_session_save(GsmStore * client_store,GsmStore * app_store,GError ** error)154 gsm_session_save (GsmStore *client_store,
155 GsmStore *app_store,
156 GError **error)
157 {
158 GSettings *settings;
159 const char *save_dir;
160 SessionSaveData data;
161
162 g_debug ("GsmSessionSave: Saving session");
163
164 /* Clear one shot key autosave in the event its set (so that it's actually
165 * one shot only)
166 */
167 settings = g_settings_new (GSM_MANAGER_SCHEMA);
168 g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE);
169 g_object_unref (settings);
170
171 save_dir = gsm_util_get_saved_session_dir ();
172 if (save_dir == NULL) {
173 g_warning ("GsmSessionSave: cannot create saved session directory");
174 return;
175 }
176
177 data.dir = save_dir;
178 data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
179 g_free, NULL);
180 data.app_store = app_store;
181
182 /* remove old saved session */
183 gsm_session_clear_saved_session (save_dir, data.discard_hash);
184 data.error = error;
185
186 gsm_store_foreach (client_store,
187 (GsmStoreFunc) save_one_client,
188 &data);
189
190 g_hash_table_destroy (data.discard_hash);
191 }
192
193 static gboolean
gsm_session_clear_one_client(const char * filename,GHashTable * discard_hash)194 gsm_session_clear_one_client (const char *filename,
195 GHashTable *discard_hash)
196 {
197 gboolean result = TRUE;
198 GKeyFile *key_file;
199 char *discard_exec = NULL;
200 char **envp;
201
202 g_debug ("GsmSessionSave: removing '%s' from saved session", filename);
203
204 envp = (char **) gsm_util_listenv ();
205 key_file = g_key_file_new ();
206 if (g_key_file_load_from_file (key_file, filename,
207 G_KEY_FILE_NONE, NULL)) {
208 char **argv;
209 int argc;
210
211 discard_exec = g_key_file_get_string (key_file,
212 G_KEY_FILE_DESKTOP_GROUP,
213 GSM_AUTOSTART_APP_DISCARD_KEY,
214 NULL);
215 if (!discard_exec)
216 goto out;
217
218 if (discard_hash && g_hash_table_lookup (discard_hash, discard_exec))
219 goto out;
220
221 if (!g_shell_parse_argv (discard_exec, &argc, &argv, NULL))
222 goto out;
223
224 result = g_spawn_async (NULL, argv, envp, G_SPAWN_SEARCH_PATH,
225 NULL, NULL, NULL, NULL) && result;
226
227 g_strfreev (argv);
228 } else {
229 result = FALSE;
230 }
231
232 out:
233 if (key_file)
234 g_key_file_free (key_file);
235 if (discard_exec)
236 g_free (discard_exec);
237
238 result = (g_unlink (filename) == 0) && result;
239
240 return result;
241 }
242
243 static gboolean
gsm_session_clear_saved_session(const char * directory,GHashTable * discard_hash)244 gsm_session_clear_saved_session (const char *directory,
245 GHashTable *discard_hash)
246 {
247 GDir *dir;
248 const char *filename;
249 gboolean result = TRUE;
250 GError *error;
251
252 g_debug ("GsmSessionSave: clearing currently saved session at %s",
253 directory);
254
255 if (directory == NULL) {
256 return FALSE;
257 }
258
259 error = NULL;
260 dir = g_dir_open (directory, 0, &error);
261 if (error) {
262 g_warning ("GsmSessionSave: error loading saved session directory: %s", error->message);
263 g_error_free (error);
264 return FALSE;
265 }
266
267 while ((filename = g_dir_read_name (dir))) {
268 char *path = g_build_filename (directory,
269 filename, NULL);
270
271 result = gsm_session_clear_one_client (path, discard_hash)
272 && result;
273
274 g_free (path);
275 }
276
277 g_dir_close (dir);
278
279 return result;
280 }
281
282 void
gsm_session_save_clear(void)283 gsm_session_save_clear (void)
284 {
285 const char *save_dir;
286
287 g_debug ("GsmSessionSave: Clearing saved session");
288
289 save_dir = gsm_util_get_saved_session_dir ();
290 if (save_dir == NULL) {
291 g_warning ("GsmSessionSave: cannot create saved session directory");
292 return;
293 }
294
295 gsm_session_clear_saved_session (save_dir, NULL);
296 }
297