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