1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2007 Novell, Inc.
4 * Copyright (C) 2008 Red Hat, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include <gio/gio.h>
30 #include <glib/gi18n.h>
31
32 #include "gsm-xsmp-client.h"
33
34 #include "gsm-util.h"
35 #include "gsm-autostart-app.h"
36 #include "gsm-icon-names.h"
37 #include "gsm-manager.h"
38
39 #define GsmDesktopFile "_GSM_DesktopFile"
40
41 #define GSM_XSMP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientPrivate))
42
43 struct GsmXSMPClientPrivate
44 {
45
46 SmsConn conn;
47 IceConn ice_connection;
48
49 guint watch_id;
50
51 char *description;
52 GPtrArray *props;
53
54 /* SaveYourself state */
55 int current_save_yourself;
56 int next_save_yourself;
57 guint next_save_yourself_allow_interact : 1;
58 };
59
60 enum {
61 PROP_0,
62 PROP_ICE_CONNECTION
63 };
64
65 enum {
66 REGISTER_REQUEST,
67 REGISTER_CONFIRMED,
68 LOGOUT_REQUEST,
69 LAST_SIGNAL
70 };
71
72 static guint signals[LAST_SIGNAL] = { 0 };
73
G_DEFINE_TYPE(GsmXSMPClient,gsm_xsmp_client,GSM_TYPE_CLIENT)74 G_DEFINE_TYPE (GsmXSMPClient, gsm_xsmp_client, GSM_TYPE_CLIENT)
75
76 static gboolean
77 client_iochannel_watch (GIOChannel *channel,
78 GIOCondition condition,
79 GsmXSMPClient *client)
80 {
81 gboolean keep_going;
82
83 g_object_ref (client);
84 switch (IceProcessMessages (client->priv->ice_connection, NULL, NULL)) {
85 case IceProcessMessagesSuccess:
86 keep_going = TRUE;
87 break;
88
89 case IceProcessMessagesIOError:
90 g_debug ("GsmXSMPClient: IceProcessMessagesIOError on '%s'", client->priv->description);
91 gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FAILED);
92 /* Emitting "disconnected" will eventually cause
93 * IceCloseConnection() to be called.
94 */
95 gsm_client_disconnected (GSM_CLIENT (client));
96 keep_going = FALSE;
97 break;
98
99 case IceProcessMessagesConnectionClosed:
100 g_debug ("GsmXSMPClient: IceProcessMessagesConnectionClosed on '%s'",
101 client->priv->description);
102 client->priv->ice_connection = NULL;
103 keep_going = FALSE;
104 break;
105
106 default:
107 g_assert_not_reached ();
108 }
109 g_object_unref (client);
110
111 return keep_going;
112 }
113
114 static SmProp *
find_property(GsmXSMPClient * client,const char * name,int * index)115 find_property (GsmXSMPClient *client,
116 const char *name,
117 int *index)
118 {
119 SmProp *prop;
120 int i;
121
122 for (i = 0; i < client->priv->props->len; i++) {
123 prop = client->priv->props->pdata[i];
124
125 if (!strcmp (prop->name, name)) {
126 if (index) {
127 *index = i;
128 }
129 return prop;
130 }
131 }
132
133 return NULL;
134 }
135
136 static void
set_description(GsmXSMPClient * client)137 set_description (GsmXSMPClient *client)
138 {
139 SmProp *prop;
140 const char *id;
141
142 prop = find_property (client, SmProgram, NULL);
143 id = gsm_client_peek_startup_id (GSM_CLIENT (client));
144
145 g_free (client->priv->description);
146 if (prop) {
147 client->priv->description = g_strdup_printf ("%p [%.*s %s]",
148 client,
149 prop->vals[0].length,
150 (char *)prop->vals[0].value,
151 id);
152 } else if (id != NULL) {
153 client->priv->description = g_strdup_printf ("%p [%s]", client, id);
154 } else {
155 client->priv->description = g_strdup_printf ("%p", client);
156 }
157 }
158
159 static void
setup_connection(GsmXSMPClient * client)160 setup_connection (GsmXSMPClient *client)
161 {
162 GIOChannel *channel;
163 int fd;
164
165 g_debug ("GsmXSMPClient: Setting up new connection");
166
167 fd = IceConnectionNumber (client->priv->ice_connection);
168 fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
169 channel = g_io_channel_unix_new (fd);
170 client->priv->watch_id = g_io_add_watch (channel,
171 G_IO_IN | G_IO_ERR,
172 (GIOFunc)client_iochannel_watch,
173 client);
174 g_io_channel_unref (channel);
175
176 set_description (client);
177
178 g_debug ("GsmXSMPClient: New client '%s'", client->priv->description);
179 }
180
181 static GObject *
gsm_xsmp_client_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)182 gsm_xsmp_client_constructor (GType type,
183 guint n_construct_properties,
184 GObjectConstructParam *construct_properties)
185 {
186 GsmXSMPClient *client;
187
188 client = GSM_XSMP_CLIENT (G_OBJECT_CLASS (gsm_xsmp_client_parent_class)->constructor (type,
189 n_construct_properties,
190 construct_properties));
191 setup_connection (client);
192
193 return G_OBJECT (client);
194 }
195
196 static void
gsm_xsmp_client_init(GsmXSMPClient * client)197 gsm_xsmp_client_init (GsmXSMPClient *client)
198 {
199 client->priv = GSM_XSMP_CLIENT_GET_PRIVATE (client);
200
201 client->priv->props = g_ptr_array_new ();
202 client->priv->current_save_yourself = -1;
203 client->priv->next_save_yourself = -1;
204 client->priv->next_save_yourself_allow_interact = FALSE;
205 }
206
207
208 static void
delete_property(GsmXSMPClient * client,const char * name)209 delete_property (GsmXSMPClient *client,
210 const char *name)
211 {
212 int index;
213 SmProp *prop;
214
215 prop = find_property (client, name, &index);
216 if (!prop) {
217 return;
218 }
219
220 #if 0
221 /* This is wrong anyway; we can't unconditionally run the current
222 * discard command; if this client corresponds to a GsmAppResumed,
223 * and the current discard command is identical to the app's
224 * discard_command, then we don't run the discard command now,
225 * because that would delete a saved state we may want to resume
226 * again later.
227 */
228 if (!strcmp (name, SmDiscardCommand)) {
229 gsm_client_run_discard (GSM_CLIENT (client));
230 }
231 #endif
232
233 g_ptr_array_remove_index_fast (client->priv->props, index);
234 SmFreeProperty (prop);
235 }
236
237
238 static void
debug_print_property(SmProp * prop)239 debug_print_property (SmProp *prop)
240 {
241 GString *tmp;
242 int i;
243
244 switch (prop->type[0]) {
245 case 'C': /* CARD8 */
246 g_debug ("GsmXSMPClient: %s = %d", prop->name, *(unsigned char *)prop->vals[0].value);
247 break;
248
249 case 'A': /* ARRAY8 */
250 g_debug ("GsmXSMPClient: %s = '%s'", prop->name, (char *)prop->vals[0].value);
251 break;
252
253 case 'L': /* LISTofARRAY8 */
254 tmp = g_string_new (NULL);
255 for (i = 0; i < prop->num_vals; i++) {
256 g_string_append_printf (tmp, "'%.*s' ", prop->vals[i].length,
257 (char *)prop->vals[i].value);
258 }
259 g_debug ("GsmXSMPClient: %s = %s", prop->name, tmp->str);
260 g_string_free (tmp, TRUE);
261 break;
262
263 default:
264 g_debug ("GsmXSMPClient: %s = ??? (%s)", prop->name, prop->type);
265 break;
266 }
267 }
268
269
270 static void
set_properties_callback(SmsConn conn,SmPointer manager_data,int num_props,SmProp ** props)271 set_properties_callback (SmsConn conn,
272 SmPointer manager_data,
273 int num_props,
274 SmProp **props)
275 {
276 GsmXSMPClient *client = manager_data;
277 int i;
278
279 g_debug ("GsmXSMPClient: Set properties from client '%s'", client->priv->description);
280
281 for (i = 0; i < num_props; i++) {
282 delete_property (client, props[i]->name);
283 g_ptr_array_add (client->priv->props, props[i]);
284
285 debug_print_property (props[i]);
286
287 if (!strcmp (props[i]->name, SmProgram))
288 set_description (client);
289 }
290
291 free (props);
292
293 }
294
295 static void
delete_properties_callback(SmsConn conn,SmPointer manager_data,int num_props,char ** prop_names)296 delete_properties_callback (SmsConn conn,
297 SmPointer manager_data,
298 int num_props,
299 char **prop_names)
300 {
301 GsmXSMPClient *client = manager_data;
302 int i;
303
304 g_debug ("GsmXSMPClient: Delete properties from '%s'", client->priv->description);
305
306 for (i = 0; i < num_props; i++) {
307 delete_property (client, prop_names[i]);
308
309 g_debug (" %s", prop_names[i]);
310 }
311
312 free (prop_names);
313 }
314
315 static void
get_properties_callback(SmsConn conn,SmPointer manager_data)316 get_properties_callback (SmsConn conn,
317 SmPointer manager_data)
318 {
319 GsmXSMPClient *client = manager_data;
320
321 g_debug ("GsmXSMPClient: Get properties request from '%s'", client->priv->description);
322
323 SmsReturnProperties (conn,
324 client->priv->props->len,
325 (SmProp **)client->priv->props->pdata);
326 }
327
328 static char *
prop_to_command(SmProp * prop)329 prop_to_command (SmProp *prop)
330 {
331 GString *str;
332 int i, j;
333 gboolean need_quotes;
334
335 str = g_string_new (NULL);
336 for (i = 0; i < prop->num_vals; i++) {
337 char *val = prop->vals[i].value;
338
339 need_quotes = FALSE;
340 for (j = 0; j < prop->vals[i].length; j++) {
341 if (!g_ascii_isalnum (val[j]) && !strchr ("-_=:./", val[j])) {
342 need_quotes = TRUE;
343 break;
344 }
345 }
346
347 if (i > 0) {
348 g_string_append_c (str, ' ');
349 }
350
351 if (!need_quotes) {
352 g_string_append_printf (str,
353 "%.*s",
354 prop->vals[i].length,
355 (char *)prop->vals[i].value);
356 } else {
357 g_string_append_c (str, '\'');
358 while (val < (char *)prop->vals[i].value + prop->vals[i].length) {
359 if (*val == '\'') {
360 g_string_append (str, "'\''");
361 } else {
362 g_string_append_c (str, *val);
363 }
364 val++;
365 }
366 g_string_append_c (str, '\'');
367 }
368 }
369
370 return g_string_free (str, FALSE);
371 }
372
373 static char *
xsmp_get_restart_command(GsmClient * client)374 xsmp_get_restart_command (GsmClient *client)
375 {
376 SmProp *prop;
377
378 prop = find_property (GSM_XSMP_CLIENT (client), SmRestartCommand, NULL);
379
380 if (!prop || strcmp (prop->type, SmLISTofARRAY8) != 0) {
381 return NULL;
382 }
383
384 return prop_to_command (prop);
385 }
386
387 static char *
xsmp_get_discard_command(GsmClient * client)388 xsmp_get_discard_command (GsmClient *client)
389 {
390 SmProp *prop;
391
392 prop = find_property (GSM_XSMP_CLIENT (client), SmDiscardCommand, NULL);
393
394 if (!prop || strcmp (prop->type, SmLISTofARRAY8) != 0) {
395 return NULL;
396 }
397
398 return prop_to_command (prop);
399 }
400
401 static void
do_save_yourself(GsmXSMPClient * client,int save_type,gboolean allow_interact)402 do_save_yourself (GsmXSMPClient *client,
403 int save_type,
404 gboolean allow_interact)
405 {
406 g_assert (client->priv->conn != NULL);
407
408 if (client->priv->next_save_yourself != -1) {
409 /* Either we're currently doing a shutdown and there's a checkpoint
410 * queued after it, or vice versa. Either way, the new SaveYourself
411 * is redundant.
412 */
413 g_debug ("GsmXSMPClient: skipping redundant SaveYourself for '%s'",
414 client->priv->description);
415 } else if (client->priv->current_save_yourself != -1) {
416 g_debug ("GsmXSMPClient: queuing new SaveYourself for '%s'",
417 client->priv->description);
418 client->priv->next_save_yourself = save_type;
419 client->priv->next_save_yourself_allow_interact = allow_interact;
420 } else {
421 client->priv->current_save_yourself = save_type;
422 /* make sure we don't have anything queued */
423 client->priv->next_save_yourself = -1;
424 client->priv->next_save_yourself_allow_interact = FALSE;
425
426 switch (save_type) {
427 case SmSaveLocal:
428 /* Save state */
429 SmsSaveYourself (client->priv->conn,
430 SmSaveLocal,
431 FALSE,
432 SmInteractStyleNone,
433 FALSE);
434 break;
435
436 default:
437 /* Logout */
438 if (!allow_interact) {
439 SmsSaveYourself (client->priv->conn,
440 save_type, /* save type */
441 TRUE, /* shutdown */
442 SmInteractStyleNone, /* interact style */
443 TRUE); /* fast */
444 } else {
445 SmsSaveYourself (client->priv->conn,
446 save_type, /* save type */
447 TRUE, /* shutdown */
448 SmInteractStyleAny, /* interact style */
449 FALSE /* fast */);
450 }
451 break;
452 }
453 }
454 }
455
456 static void
xsmp_save_yourself_phase2(GsmClient * client)457 xsmp_save_yourself_phase2 (GsmClient *client)
458 {
459 GsmXSMPClient *xsmp = (GsmXSMPClient *) client;
460
461 g_debug ("GsmXSMPClient: xsmp_save_yourself_phase2 ('%s')", xsmp->priv->description);
462
463 SmsSaveYourselfPhase2 (xsmp->priv->conn);
464 }
465
466 static void
xsmp_interact(GsmClient * client)467 xsmp_interact (GsmClient *client)
468 {
469 GsmXSMPClient *xsmp = (GsmXSMPClient *) client;
470
471 g_debug ("GsmXSMPClient: xsmp_interact ('%s')", xsmp->priv->description);
472
473 SmsInteract (xsmp->priv->conn);
474 }
475
476 static gboolean
xsmp_cancel_end_session(GsmClient * client,GError ** error)477 xsmp_cancel_end_session (GsmClient *client,
478 GError **error)
479 {
480 GsmXSMPClient *xsmp = (GsmXSMPClient *) client;
481
482 g_debug ("GsmXSMPClient: xsmp_cancel_end_session ('%s')", xsmp->priv->description);
483
484 if (xsmp->priv->conn == NULL) {
485 g_set_error (error,
486 GSM_CLIENT_ERROR,
487 GSM_CLIENT_ERROR_NOT_REGISTERED,
488 "Client is not registered");
489 return FALSE;
490 }
491
492 SmsShutdownCancelled (xsmp->priv->conn);
493
494 /* reset the state */
495 xsmp->priv->current_save_yourself = -1;
496 xsmp->priv->next_save_yourself = -1;
497 xsmp->priv->next_save_yourself_allow_interact = FALSE;
498
499 return TRUE;
500 }
501
502 static char *
get_desktop_file_path(GsmXSMPClient * client)503 get_desktop_file_path (GsmXSMPClient *client)
504 {
505 SmProp *prop;
506 char *desktop_file_path = NULL;
507 const char *program_name;
508
509 /* XSMP clients using eggsmclient defines a special property
510 * pointing to their respective desktop entry file */
511 prop = find_property (client, GsmDesktopFile, NULL);
512
513 if (prop) {
514 GFile *file = g_file_new_for_uri (prop->vals[0].value);
515 desktop_file_path = g_file_get_path (file);
516 g_object_unref (file);
517 goto out;
518 }
519
520 /* If we can't get desktop file from GsmDesktopFile then we
521 * try to find the desktop file from its program name */
522 prop = find_property (client, SmProgram, NULL);
523
524 if (!prop) {
525 goto out;
526 }
527
528 program_name = prop->vals[0].value;
529 desktop_file_path =
530 gsm_util_find_desktop_file_for_app_name (program_name,
531 TRUE, FALSE);
532
533 out:
534 g_debug ("GsmXSMPClient: desktop file for client %s is %s",
535 gsm_client_peek_id (GSM_CLIENT (client)),
536 desktop_file_path ? desktop_file_path : "(null)");
537
538 return desktop_file_path;
539 }
540
541 static void
set_desktop_file_keys_from_client(GsmClient * client,GKeyFile * keyfile)542 set_desktop_file_keys_from_client (GsmClient *client,
543 GKeyFile *keyfile)
544 {
545 SmProp *prop;
546 const char *name;
547 char *comment;
548
549 prop = find_property (GSM_XSMP_CLIENT (client), SmProgram, NULL);
550 if (prop) {
551 name = prop->vals[0].value;
552 } else {
553 /* It'd be really surprising to reach this code: if we're here,
554 * then the XSMP client already has set several XSMP
555 * properties. But it could still be that SmProgram is not set.
556 */
557 name = _("Remembered Application");
558 }
559
560 comment = g_strdup_printf ("Client %s which was automatically saved",
561 gsm_client_peek_startup_id (client));
562
563 g_key_file_set_string (keyfile,
564 G_KEY_FILE_DESKTOP_GROUP,
565 G_KEY_FILE_DESKTOP_KEY_NAME,
566 name);
567
568 g_key_file_set_string (keyfile,
569 G_KEY_FILE_DESKTOP_GROUP,
570 G_KEY_FILE_DESKTOP_KEY_COMMENT,
571 comment);
572
573 g_key_file_set_string (keyfile,
574 G_KEY_FILE_DESKTOP_GROUP,
575 G_KEY_FILE_DESKTOP_KEY_ICON,
576 GSM_ICON_XSMP_DEFAULT);
577
578 g_key_file_set_string (keyfile,
579 G_KEY_FILE_DESKTOP_GROUP,
580 G_KEY_FILE_DESKTOP_KEY_TYPE,
581 "Application");
582
583 g_key_file_set_boolean (keyfile,
584 G_KEY_FILE_DESKTOP_GROUP,
585 G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY,
586 TRUE);
587
588 g_free (comment);
589 }
590
591 static GKeyFile *
create_client_key_file(GsmClient * client,const char * desktop_file_path,GError ** error)592 create_client_key_file (GsmClient *client,
593 const char *desktop_file_path,
594 GError **error) {
595 GKeyFile *keyfile;
596
597 keyfile = g_key_file_new ();
598
599 if (desktop_file_path != NULL) {
600 g_key_file_load_from_file (keyfile,
601 desktop_file_path,
602 G_KEY_FILE_KEEP_COMMENTS |
603 G_KEY_FILE_KEEP_TRANSLATIONS,
604 error);
605 } else {
606 set_desktop_file_keys_from_client (client, keyfile);
607 }
608
609 return keyfile;
610 }
611
612 static GsmClientRestartStyle
613 xsmp_get_restart_style_hint (GsmClient *client);
614
615 static GKeyFile *
xsmp_save(GsmClient * client,GsmApp * app,GError ** error)616 xsmp_save (GsmClient *client,
617 GsmApp *app,
618 GError **error)
619 {
620 GsmClientRestartStyle restart_style;
621
622 GKeyFile *keyfile = NULL;
623 char *desktop_file_path = NULL;
624 char *exec_program = NULL;
625 char *exec_discard = NULL;
626 char *startup_id = NULL;
627 GError *local_error;
628
629 g_debug ("GsmXSMPClient: saving client with id %s",
630 gsm_client_peek_id (client));
631
632 local_error = NULL;
633
634 restart_style = xsmp_get_restart_style_hint (client);
635 if (restart_style == GSM_CLIENT_RESTART_NEVER) {
636 goto out;
637 }
638
639 exec_program = xsmp_get_restart_command (client);
640 if (!exec_program) {
641 goto out;
642 }
643
644 desktop_file_path = get_desktop_file_path (GSM_XSMP_CLIENT (client));
645
646 /* this can accept desktop_file_path == NULL */
647 keyfile = create_client_key_file (client,
648 desktop_file_path,
649 &local_error);
650
651 if (local_error) {
652 goto out;
653 }
654
655 g_object_get (client,
656 "startup-id", &startup_id,
657 NULL);
658
659 g_key_file_set_string (keyfile,
660 G_KEY_FILE_DESKTOP_GROUP,
661 GSM_AUTOSTART_APP_STARTUP_ID_KEY,
662 startup_id);
663
664 g_key_file_set_string (keyfile,
665 G_KEY_FILE_DESKTOP_GROUP,
666 G_KEY_FILE_DESKTOP_KEY_EXEC,
667 exec_program);
668
669 exec_discard = xsmp_get_discard_command (client);
670 if (exec_discard)
671 g_key_file_set_string (keyfile,
672 G_KEY_FILE_DESKTOP_GROUP,
673 GSM_AUTOSTART_APP_DISCARD_KEY,
674 exec_discard);
675
676 if (app != NULL) {
677 gsm_app_save_to_keyfile (app, keyfile, &local_error);
678
679 if (local_error) {
680 goto out;
681 }
682 }
683
684 out:
685 g_free (desktop_file_path);
686 g_free (exec_program);
687 g_free (exec_discard);
688 g_free (startup_id);
689
690 if (local_error != NULL) {
691 g_propagate_error (error, local_error);
692 g_key_file_free (keyfile);
693
694 return NULL;
695 }
696
697 return keyfile;
698 }
699
700 static gboolean
xsmp_stop(GsmClient * client,GError ** error)701 xsmp_stop (GsmClient *client,
702 GError **error)
703 {
704 GsmXSMPClient *xsmp = (GsmXSMPClient *) client;
705
706 g_debug ("GsmXSMPClient: xsmp_stop ('%s')", xsmp->priv->description);
707
708 if (xsmp->priv->conn == NULL) {
709 g_set_error (error,
710 GSM_CLIENT_ERROR,
711 GSM_CLIENT_ERROR_NOT_REGISTERED,
712 "Client is not registered");
713 return FALSE;
714 }
715
716 SmsDie (xsmp->priv->conn);
717
718 return TRUE;
719 }
720
721 static gboolean
xsmp_query_end_session(GsmClient * client,GsmClientEndSessionFlag flags,GError ** error)722 xsmp_query_end_session (GsmClient *client,
723 GsmClientEndSessionFlag flags,
724 GError **error)
725 {
726 gboolean allow_interact;
727 int save_type;
728
729 if (GSM_XSMP_CLIENT (client)->priv->conn == NULL) {
730 g_set_error (error,
731 GSM_CLIENT_ERROR,
732 GSM_CLIENT_ERROR_NOT_REGISTERED,
733 "Client is not registered");
734 return FALSE;
735 }
736
737 allow_interact = !(flags & GSM_CLIENT_END_SESSION_FLAG_FORCEFUL);
738
739 /* we don't want to save the session state, but we just want to know if
740 * there's user data the client has to save and we want to give the
741 * client a chance to tell the user about it. This is consistent with
742 * the manager not setting GSM_CLIENT_END_SESSION_FLAG_SAVE for this
743 * phase. */
744 save_type = SmSaveGlobal;
745
746 do_save_yourself (GSM_XSMP_CLIENT (client), save_type, allow_interact);
747 return TRUE;
748 }
749
750 static gboolean
xsmp_end_session(GsmClient * client,GsmClientEndSessionFlag flags,GError ** error)751 xsmp_end_session (GsmClient *client,
752 GsmClientEndSessionFlag flags,
753 GError **error)
754 {
755 gboolean phase2;
756
757 if (GSM_XSMP_CLIENT (client)->priv->conn == NULL) {
758 g_set_error (error,
759 GSM_CLIENT_ERROR,
760 GSM_CLIENT_ERROR_NOT_REGISTERED,
761 "Client is not registered");
762 return FALSE;
763 }
764
765 phase2 = (flags & GSM_CLIENT_END_SESSION_FLAG_LAST);
766
767 if (phase2) {
768 xsmp_save_yourself_phase2 (client);
769 } else {
770 gboolean allow_interact;
771 int save_type;
772
773 /* we gave a chance to interact to the app during
774 * xsmp_query_end_session(), now it's too late to interact */
775 allow_interact = FALSE;
776
777 if (flags & GSM_CLIENT_END_SESSION_FLAG_SAVE) {
778 save_type = SmSaveBoth;
779 } else {
780 save_type = SmSaveGlobal;
781 }
782
783 do_save_yourself (GSM_XSMP_CLIENT (client),
784 save_type, allow_interact);
785 }
786
787 return TRUE;
788 }
789
790 static char *
xsmp_get_app_name(GsmClient * client)791 xsmp_get_app_name (GsmClient *client)
792 {
793 SmProp *prop;
794 char *name = NULL;
795
796 prop = find_property (GSM_XSMP_CLIENT (client), SmProgram, NULL);
797 if (prop) {
798 name = prop_to_command (prop);
799 }
800
801 return name;
802 }
803
804 static void
gsm_client_set_ice_connection(GsmXSMPClient * client,gpointer conn)805 gsm_client_set_ice_connection (GsmXSMPClient *client,
806 gpointer conn)
807 {
808 client->priv->ice_connection = conn;
809 }
810
811 static void
gsm_xsmp_client_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)812 gsm_xsmp_client_set_property (GObject *object,
813 guint prop_id,
814 const GValue *value,
815 GParamSpec *pspec)
816 {
817 GsmXSMPClient *self;
818
819 self = GSM_XSMP_CLIENT (object);
820
821 switch (prop_id) {
822 case PROP_ICE_CONNECTION:
823 gsm_client_set_ice_connection (self, g_value_get_pointer (value));
824 break;
825 default:
826 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
827 break;
828 }
829 }
830
831 static void
gsm_xsmp_client_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)832 gsm_xsmp_client_get_property (GObject *object,
833 guint prop_id,
834 GValue *value,
835 GParamSpec *pspec)
836 {
837 GsmXSMPClient *self;
838
839 self = GSM_XSMP_CLIENT (object);
840
841 switch (prop_id) {
842 case PROP_ICE_CONNECTION:
843 g_value_set_pointer (value, self->priv->ice_connection);
844 break;
845 default:
846 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
847 break;
848 }
849 }
850
851 static void
gsm_xsmp_client_disconnect(GsmXSMPClient * client)852 gsm_xsmp_client_disconnect (GsmXSMPClient *client)
853 {
854 if (client->priv->watch_id > 0) {
855 g_source_remove (client->priv->watch_id);
856 }
857
858 if (client->priv->conn != NULL) {
859 SmsCleanUp (client->priv->conn);
860 }
861
862 if (client->priv->ice_connection != NULL) {
863 IceSetShutdownNegotiation (client->priv->ice_connection, FALSE);
864 IceCloseConnection (client->priv->ice_connection);
865 }
866 }
867
868 static void
gsm_xsmp_client_finalize(GObject * object)869 gsm_xsmp_client_finalize (GObject *object)
870 {
871 GsmXSMPClient *client = (GsmXSMPClient *) object;
872
873 g_debug ("GsmXSMPClient: xsmp_finalize (%s)", client->priv->description);
874 gsm_xsmp_client_disconnect (client);
875
876 g_free (client->priv->description);
877 g_ptr_array_foreach (client->priv->props, (GFunc)SmFreeProperty, NULL);
878 g_ptr_array_free (client->priv->props, TRUE);
879
880 G_OBJECT_CLASS (gsm_xsmp_client_parent_class)->finalize (object);
881 }
882
883 static gboolean
_boolean_handled_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)884 _boolean_handled_accumulator (GSignalInvocationHint *ihint,
885 GValue *return_accu,
886 const GValue *handler_return,
887 gpointer dummy)
888 {
889 gboolean continue_emission;
890 gboolean signal_handled;
891
892 signal_handled = g_value_get_boolean (handler_return);
893 g_value_set_boolean (return_accu, signal_handled);
894 continue_emission = !signal_handled;
895
896 return continue_emission;
897 }
898
899 static GsmClientRestartStyle
xsmp_get_restart_style_hint(GsmClient * client)900 xsmp_get_restart_style_hint (GsmClient *client)
901 {
902 SmProp *prop;
903 GsmClientRestartStyle hint;
904
905 g_debug ("GsmXSMPClient: getting restart style");
906 hint = GSM_CLIENT_RESTART_IF_RUNNING;
907
908 prop = find_property (GSM_XSMP_CLIENT (client), SmRestartStyleHint, NULL);
909
910 if (!prop || strcmp (prop->type, SmCARD8) != 0) {
911 return GSM_CLIENT_RESTART_IF_RUNNING;
912 }
913
914 switch (((unsigned char *)prop->vals[0].value)[0]) {
915 case SmRestartIfRunning:
916 hint = GSM_CLIENT_RESTART_IF_RUNNING;
917 break;
918 case SmRestartAnyway:
919 hint = GSM_CLIENT_RESTART_ANYWAY;
920 break;
921 case SmRestartImmediately:
922 hint = GSM_CLIENT_RESTART_IMMEDIATELY;
923 break;
924 case SmRestartNever:
925 hint = GSM_CLIENT_RESTART_NEVER;
926 break;
927 default:
928 break;
929 }
930
931 return hint;
932 }
933
934 static gboolean
_parse_value_as_uint(const char * value,guint * uintval)935 _parse_value_as_uint (const char *value,
936 guint *uintval)
937 {
938 char *end_of_valid_uint;
939 gulong ulong_value;
940 guint uint_value;
941
942 errno = 0;
943 ulong_value = strtoul (value, &end_of_valid_uint, 10);
944
945 if (*value == '\0' || *end_of_valid_uint != '\0') {
946 return FALSE;
947 }
948
949 uint_value = ulong_value;
950 if (uint_value != ulong_value || errno == ERANGE) {
951 return FALSE;
952 }
953
954 *uintval = uint_value;
955
956 return TRUE;
957 }
958
959 static guint
xsmp_get_unix_process_id(GsmClient * client)960 xsmp_get_unix_process_id (GsmClient *client)
961 {
962 SmProp *prop;
963 guint pid;
964 gboolean res;
965
966 g_debug ("GsmXSMPClient: getting pid");
967
968 prop = find_property (GSM_XSMP_CLIENT (client), SmProcessID, NULL);
969
970 if (!prop || strcmp (prop->type, SmARRAY8) != 0) {
971 return 0;
972 }
973
974 pid = 0;
975 res = _parse_value_as_uint ((char *)prop->vals[0].value, &pid);
976 if (! res) {
977 pid = 0;
978 }
979
980 return pid;
981 }
982
983 static void
gsm_xsmp_client_class_init(GsmXSMPClientClass * klass)984 gsm_xsmp_client_class_init (GsmXSMPClientClass *klass)
985 {
986 GObjectClass *object_class = G_OBJECT_CLASS (klass);
987 GsmClientClass *client_class = GSM_CLIENT_CLASS (klass);
988
989 object_class->finalize = gsm_xsmp_client_finalize;
990 object_class->constructor = gsm_xsmp_client_constructor;
991 object_class->get_property = gsm_xsmp_client_get_property;
992 object_class->set_property = gsm_xsmp_client_set_property;
993
994 client_class->impl_save = xsmp_save;
995 client_class->impl_stop = xsmp_stop;
996 client_class->impl_query_end_session = xsmp_query_end_session;
997 client_class->impl_end_session = xsmp_end_session;
998 client_class->impl_cancel_end_session = xsmp_cancel_end_session;
999 client_class->impl_get_app_name = xsmp_get_app_name;
1000 client_class->impl_get_restart_style_hint = xsmp_get_restart_style_hint;
1001 client_class->impl_get_unix_process_id = xsmp_get_unix_process_id;
1002
1003 signals[REGISTER_REQUEST] =
1004 g_signal_new ("register-request",
1005 G_OBJECT_CLASS_TYPE (object_class),
1006 G_SIGNAL_RUN_LAST,
1007 G_STRUCT_OFFSET (GsmXSMPClientClass, register_request),
1008 _boolean_handled_accumulator,
1009 NULL,
1010 NULL,
1011 G_TYPE_BOOLEAN,
1012 1, G_TYPE_POINTER);
1013 signals[REGISTER_CONFIRMED] =
1014 g_signal_new ("register-confirmed",
1015 G_OBJECT_CLASS_TYPE (object_class),
1016 G_SIGNAL_RUN_LAST,
1017 G_STRUCT_OFFSET (GsmXSMPClientClass, register_confirmed),
1018 NULL,
1019 NULL,
1020 NULL,
1021 G_TYPE_NONE,
1022 1, G_TYPE_POINTER);
1023 signals[LOGOUT_REQUEST] =
1024 g_signal_new ("logout-request",
1025 G_OBJECT_CLASS_TYPE (object_class),
1026 G_SIGNAL_RUN_LAST,
1027 G_STRUCT_OFFSET (GsmXSMPClientClass, logout_request),
1028 NULL,
1029 NULL,
1030 NULL,
1031 G_TYPE_NONE,
1032 1, G_TYPE_BOOLEAN);
1033
1034 g_object_class_install_property (object_class,
1035 PROP_ICE_CONNECTION,
1036 g_param_spec_pointer ("ice-connection",
1037 "ice-connection",
1038 "ice-connection",
1039 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1040
1041 g_type_class_add_private (klass, sizeof (GsmXSMPClientPrivate));
1042 }
1043
1044 GsmClient *
gsm_xsmp_client_new(IceConn ice_conn)1045 gsm_xsmp_client_new (IceConn ice_conn)
1046 {
1047 GsmXSMPClient *xsmp;
1048
1049 xsmp = g_object_new (GSM_TYPE_XSMP_CLIENT,
1050 "ice-connection", ice_conn,
1051 NULL);
1052
1053 return GSM_CLIENT (xsmp);
1054 }
1055
1056 static Status
register_client_callback(SmsConn conn,SmPointer manager_data,char * previous_id)1057 register_client_callback (SmsConn conn,
1058 SmPointer manager_data,
1059 char *previous_id)
1060 {
1061 GsmXSMPClient *client = manager_data;
1062 gboolean handled;
1063 char *id;
1064
1065 g_debug ("GsmXSMPClient: Client '%s' received RegisterClient(%s)",
1066 client->priv->description,
1067 previous_id ? previous_id : "NULL");
1068
1069
1070 /* There are three cases:
1071 * 1. id is NULL - we'll use a new one
1072 * 2. id is known - we'll use known one
1073 * 3. id is unknown - this is an error
1074 */
1075 id = g_strdup (previous_id);
1076
1077 handled = FALSE;
1078 g_signal_emit (client, signals[REGISTER_REQUEST], 0, &id, &handled);
1079 if (! handled) {
1080 g_debug ("GsmXSMPClient: RegisterClient not handled!");
1081 g_free (id);
1082 free (previous_id);
1083 g_assert_not_reached ();
1084 return FALSE;
1085 }
1086
1087 if (IS_STRING_EMPTY (id)) {
1088 g_debug ("GsmXSMPClient: rejected: invalid previous_id");
1089 free (previous_id);
1090 return FALSE;
1091 }
1092
1093 g_object_set (client, "startup-id", id, NULL);
1094
1095 set_description (client);
1096
1097 g_debug ("GsmXSMPClient: Sending RegisterClientReply to '%s'", client->priv->description);
1098
1099 SmsRegisterClientReply (conn, id);
1100
1101 if (IS_STRING_EMPTY (previous_id)) {
1102 /* Send the initial SaveYourself. */
1103 g_debug ("GsmXSMPClient: Sending initial SaveYourself");
1104 SmsSaveYourself (conn, SmSaveLocal, False, SmInteractStyleNone, False);
1105 client->priv->current_save_yourself = SmSaveLocal;
1106 }
1107
1108 gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_REGISTERED);
1109
1110 g_signal_emit (client, signals[REGISTER_CONFIRMED], 0, id);
1111
1112 g_free (id);
1113 free (previous_id);
1114
1115 return TRUE;
1116 }
1117
1118
1119 static void
save_yourself_request_callback(SmsConn conn,SmPointer manager_data,int save_type,Bool shutdown,int interact_style,Bool fast,Bool global)1120 save_yourself_request_callback (SmsConn conn,
1121 SmPointer manager_data,
1122 int save_type,
1123 Bool shutdown,
1124 int interact_style,
1125 Bool fast,
1126 Bool global)
1127 {
1128 GsmXSMPClient *client = manager_data;
1129
1130 g_debug ("GsmXSMPClient: Client '%s' received SaveYourselfRequest(%s, %s, %s, %s, %s)",
1131 client->priv->description,
1132 save_type == SmSaveLocal ? "SmSaveLocal" :
1133 save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
1134 shutdown ? "Shutdown" : "!Shutdown",
1135 interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
1136 interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
1137 "SmInteractStyleNone", fast ? "Fast" : "!Fast",
1138 global ? "Global" : "!Global");
1139
1140 /* Examining the g_debug above, you can see that there are a total
1141 * of 72 different combinations of options that this could have been
1142 * called with. However, most of them are stupid.
1143 *
1144 * If @shutdown and @global are both TRUE, that means the caller is
1145 * requesting that a logout message be sent to all clients, so we do
1146 * that. We use @fast to decide whether or not to show a
1147 * confirmation dialog. (This isn't really what @fast is for, but
1148 * the old gnome-session and ksmserver both interpret it that way,
1149 * so we do too.) We ignore @save_type because we pick the correct
1150 * save_type ourselves later based on user prefs, dialog choices,
1151 * etc, and we ignore @interact_style, because clients have not used
1152 * it correctly consistently enough to make it worth honoring.
1153 *
1154 * If @shutdown is TRUE and @global is FALSE, the caller is
1155 * confused, so we ignore the request.
1156 *
1157 * If @shutdown is FALSE and @save_type is SmSaveGlobal or
1158 * SmSaveBoth, then the client wants us to ask some or all open
1159 * applications to save open files to disk, but NOT quit. This is
1160 * silly and so we ignore the request.
1161 *
1162 * If @shutdown is FALSE and @save_type is SmSaveLocal, then the
1163 * client wants us to ask some or all open applications to update
1164 * their current saved state, but not log out. At the moment, the
1165 * code only supports this for the !global case (ie, a client
1166 * requesting that it be allowed to update *its own* saved state,
1167 * but not having everyone else update their saved state).
1168 */
1169
1170 if (shutdown && global) {
1171 g_debug ("GsmXSMPClient: initiating shutdown");
1172 g_signal_emit (client, signals[LOGOUT_REQUEST], 0, !fast);
1173 } else if (!shutdown && !global) {
1174 g_debug ("GsmXSMPClient: initiating checkpoint");
1175 do_save_yourself (client, SmSaveLocal, TRUE);
1176 } else {
1177 g_debug ("GsmXSMPClient: ignoring");
1178 }
1179 }
1180
1181 static void
save_yourself_phase2_request_callback(SmsConn conn,SmPointer manager_data)1182 save_yourself_phase2_request_callback (SmsConn conn,
1183 SmPointer manager_data)
1184 {
1185 GsmXSMPClient *client = manager_data;
1186
1187 g_debug ("GsmXSMPClient: Client '%s' received SaveYourselfPhase2Request",
1188 client->priv->description);
1189
1190 client->priv->current_save_yourself = -1;
1191
1192 /* this is a valid response to SaveYourself and therefore
1193 may be a response to a QES or ES */
1194 gsm_client_end_session_response (GSM_CLIENT (client),
1195 TRUE, TRUE, FALSE,
1196 NULL);
1197 }
1198
1199 static void
interact_request_callback(SmsConn conn,SmPointer manager_data,int dialog_type)1200 interact_request_callback (SmsConn conn,
1201 SmPointer manager_data,
1202 int dialog_type)
1203 {
1204 GsmXSMPClient *client = manager_data;
1205 #if 0
1206 gboolean res;
1207 GError *error;
1208 #endif
1209
1210 g_debug ("GsmXSMPClient: Client '%s' received InteractRequest(%s)",
1211 client->priv->description,
1212 dialog_type == SmDialogNormal ? "Dialog" : "Errors");
1213
1214 gsm_client_end_session_response (GSM_CLIENT (client),
1215 FALSE, FALSE, FALSE,
1216 _("This program is blocking logout."));
1217
1218 #if 0
1219 /* Can't just call back with Interact because session client
1220 grabs the keyboard! So, we try to get it to release
1221 grabs by telling it we've cancelled the shutdown.
1222 This grabbing is clearly bullshit and is not supported by
1223 the client spec or protocol spec.
1224 */
1225 res = xsmp_cancel_end_session (GSM_CLIENT (client), &error);
1226 if (! res) {
1227 g_warning ("Unable to cancel end session: %s", error->message);
1228 g_error_free (error);
1229 }
1230 #endif
1231 xsmp_interact (GSM_CLIENT (client));
1232 }
1233
1234 static void
interact_done_callback(SmsConn conn,SmPointer manager_data,Bool cancel_shutdown)1235 interact_done_callback (SmsConn conn,
1236 SmPointer manager_data,
1237 Bool cancel_shutdown)
1238 {
1239 GsmXSMPClient *client = manager_data;
1240
1241 g_debug ("GsmXSMPClient: Client '%s' received InteractDone(cancel_shutdown = %s)",
1242 client->priv->description,
1243 cancel_shutdown ? "True" : "False");
1244
1245 gsm_client_end_session_response (GSM_CLIENT (client),
1246 TRUE, FALSE, cancel_shutdown,
1247 NULL);
1248 }
1249
1250 static void
save_yourself_done_callback(SmsConn conn,SmPointer manager_data,Bool success)1251 save_yourself_done_callback (SmsConn conn,
1252 SmPointer manager_data,
1253 Bool success)
1254 {
1255 GsmXSMPClient *client = manager_data;
1256
1257 g_debug ("GsmXSMPClient: Client '%s' received SaveYourselfDone(success = %s)",
1258 client->priv->description,
1259 success ? "True" : "False");
1260
1261 if (client->priv->current_save_yourself != -1) {
1262 SmsSaveComplete (client->priv->conn);
1263 client->priv->current_save_yourself = -1;
1264 }
1265
1266 /* If success is false then the application couldn't save data. Nothing
1267 * the session manager can do about, though. FIXME: we could display a
1268 * dialog about this, I guess. */
1269 gsm_client_end_session_response (GSM_CLIENT (client),
1270 TRUE, FALSE, FALSE,
1271 NULL);
1272
1273 if (client->priv->next_save_yourself) {
1274 int save_type = client->priv->next_save_yourself;
1275 gboolean allow_interact = client->priv->next_save_yourself_allow_interact;
1276
1277 client->priv->next_save_yourself = -1;
1278 client->priv->next_save_yourself_allow_interact = -1;
1279 do_save_yourself (client, save_type, allow_interact);
1280 }
1281 }
1282
1283 static void
close_connection_callback(SmsConn conn,SmPointer manager_data,int count,char ** reason_msgs)1284 close_connection_callback (SmsConn conn,
1285 SmPointer manager_data,
1286 int count,
1287 char **reason_msgs)
1288 {
1289 GsmXSMPClient *client = manager_data;
1290 int i;
1291
1292 g_debug ("GsmXSMPClient: Client '%s' received CloseConnection", client->priv->description);
1293 for (i = 0; i < count; i++) {
1294 g_debug ("GsmXSMPClient: close reason: '%s'", reason_msgs[i]);
1295 }
1296 SmFreeReasons (count, reason_msgs);
1297
1298 gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FINISHED);
1299 gsm_client_disconnected (GSM_CLIENT (client));
1300 }
1301
1302 void
gsm_xsmp_client_connect(GsmXSMPClient * client,SmsConn conn,unsigned long * mask_ret,SmsCallbacks * callbacks_ret)1303 gsm_xsmp_client_connect (GsmXSMPClient *client,
1304 SmsConn conn,
1305 unsigned long *mask_ret,
1306 SmsCallbacks *callbacks_ret)
1307 {
1308 client->priv->conn = conn;
1309
1310 g_debug ("GsmXSMPClient: Initializing client %s", client->priv->description);
1311
1312 *mask_ret = 0;
1313
1314 *mask_ret |= SmsRegisterClientProcMask;
1315 callbacks_ret->register_client.callback = register_client_callback;
1316 callbacks_ret->register_client.manager_data = client;
1317
1318 *mask_ret |= SmsInteractRequestProcMask;
1319 callbacks_ret->interact_request.callback = interact_request_callback;
1320 callbacks_ret->interact_request.manager_data = client;
1321
1322 *mask_ret |= SmsInteractDoneProcMask;
1323 callbacks_ret->interact_done.callback = interact_done_callback;
1324 callbacks_ret->interact_done.manager_data = client;
1325
1326 *mask_ret |= SmsSaveYourselfRequestProcMask;
1327 callbacks_ret->save_yourself_request.callback = save_yourself_request_callback;
1328 callbacks_ret->save_yourself_request.manager_data = client;
1329
1330 *mask_ret |= SmsSaveYourselfP2RequestProcMask;
1331 callbacks_ret->save_yourself_phase2_request.callback = save_yourself_phase2_request_callback;
1332 callbacks_ret->save_yourself_phase2_request.manager_data = client;
1333
1334 *mask_ret |= SmsSaveYourselfDoneProcMask;
1335 callbacks_ret->save_yourself_done.callback = save_yourself_done_callback;
1336 callbacks_ret->save_yourself_done.manager_data = client;
1337
1338 *mask_ret |= SmsCloseConnectionProcMask;
1339 callbacks_ret->close_connection.callback = close_connection_callback;
1340 callbacks_ret->close_connection.manager_data = client;
1341
1342 *mask_ret |= SmsSetPropertiesProcMask;
1343 callbacks_ret->set_properties.callback = set_properties_callback;
1344 callbacks_ret->set_properties.manager_data = client;
1345
1346 *mask_ret |= SmsDeletePropertiesProcMask;
1347 callbacks_ret->delete_properties.callback = delete_properties_callback;
1348 callbacks_ret->delete_properties.manager_data = client;
1349
1350 *mask_ret |= SmsGetPropertiesProcMask;
1351 callbacks_ret->get_properties.callback = get_properties_callback;
1352 callbacks_ret->get_properties.manager_data = client;
1353 }
1354
1355 void
gsm_xsmp_client_save_state(GsmXSMPClient * client)1356 gsm_xsmp_client_save_state (GsmXSMPClient *client)
1357 {
1358 g_return_if_fail (GSM_IS_XSMP_CLIENT (client));
1359 }
1360