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