1 /*
2  * Copyright (C) 2010 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; either version 2 or version 3 of the License.
8  * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
9  */
10 
11 #include <string.h>
12 #include <gio/gdesktopappinfo.h>
13 
14 #include "configuration.h"
15 #include "lightdm/session.h"
16 
17 /**
18  * SECTION:session
19  * @short_description: Choose the session to use
20  * @include: lightdm.h
21  *
22  * Object containing information about a session type. #LightDMSession objects are not created by the user, but provided by the #LightDMGreeter object.
23  */
24 
25 /**
26  * LightDMSession:
27  *
28  * #LightDMSession is an opaque data structure and can only be accessed
29  * using the provided functions.
30  */
31 
32 /**
33  * LightDMSessionClass:
34  *
35  * Class structure for #LightDMSession.
36  */
37 
38 enum {
39     PROP_KEY = 1,
40     PROP_NAME,
41     PROP_COMMENT
42 };
43 
44 typedef struct
45 {
46     gchar *key;
47     gchar *type;
48     gchar *name;
49     gchar *comment;
50 } LightDMSessionPrivate;
51 
52 G_DEFINE_TYPE_WITH_PRIVATE (LightDMSession, lightdm_session, G_TYPE_OBJECT)
53 
54 #define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_SESSION, LightDMSessionPrivate)
55 
56 static gboolean have_sessions = FALSE;
57 static GList *local_sessions = NULL;
58 static GList *remote_sessions = NULL;
59 
60 static gint
compare_session(gconstpointer a,gconstpointer b)61 compare_session (gconstpointer a, gconstpointer b)
62 {
63     LightDMSessionPrivate *priv_a = GET_PRIVATE (a);
64     LightDMSessionPrivate *priv_b = GET_PRIVATE (b);
65     return strcmp (priv_a->name, priv_b->name);
66 }
67 
68 static LightDMSession *
load_session(GKeyFile * key_file,const gchar * key,const gchar * default_type)69 load_session (GKeyFile *key_file, const gchar *key, const gchar *default_type)
70 {
71     if (g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) ||
72         g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL))
73         return NULL;
74 
75 #ifdef G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN
76     g_autofree gchar *domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN, NULL);
77 #else
78     g_autofree gchar *domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL);
79 #endif
80     g_autofree gchar *name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, domain, NULL);
81     if (!name)
82     {
83         g_warning ("Ignoring session without name");
84         return NULL;
85     }
86 
87     g_autofree gchar *try_exec = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TRY_EXEC, domain, NULL);
88     if (try_exec)
89     {
90         g_autofree gchar *full_path = g_find_program_in_path (try_exec);
91         if (!full_path)
92             return NULL;
93     }
94 
95     g_autofree gchar *type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-LightDM-Session-Type", NULL);
96     if (!type)
97         type = strdup (default_type);
98 
99     LightDMSession *session = g_object_new (LIGHTDM_TYPE_SESSION, NULL);
100     LightDMSessionPrivate *priv = GET_PRIVATE (session);
101 
102     g_free (priv->key);
103     priv->key = g_strdup (key);
104 
105     g_free (priv->type);
106     priv->type = g_steal_pointer (&type);
107 
108     g_free (priv->name);
109     priv->name = g_steal_pointer (&name);
110 
111     g_free (priv->comment);
112     priv->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, domain, NULL);
113     if (!priv->comment)
114         priv->comment = g_strdup ("");
115 
116     return session;
117 }
118 
119 static GList *
load_sessions_dir(GList * sessions,const gchar * sessions_dir,const gchar * default_type)120 load_sessions_dir (GList *sessions, const gchar *sessions_dir, const gchar *default_type)
121 {
122     g_autoptr(GError) error = NULL;
123     GDir *directory = g_dir_open (sessions_dir, 0, &error);
124     if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
125         g_warning ("Failed to open sessions directory: %s", error->message);
126     if (!directory)
127         return sessions;
128 
129     while (TRUE)
130     {
131         const gchar *filename = g_dir_read_name (directory);
132         if (filename == NULL)
133             break;
134 
135         if (!g_str_has_suffix (filename, ".desktop"))
136             continue;
137 
138         g_autofree gchar *path = g_build_filename (sessions_dir, filename, NULL);
139 
140         g_autoptr(GKeyFile) key_file = g_key_file_new ();
141         g_autoptr(GError) e = NULL;
142         gboolean result = g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &e);
143         if (e)
144             g_warning ("Failed to load session file %s: %s:", path, e->message);
145 
146         if (result)
147         {
148             g_autofree gchar *key = g_strndup (filename, strlen (filename) - strlen (".desktop"));
149             LightDMSession *session = load_session (key_file, key, default_type);
150             if (session)
151             {
152                 g_debug ("Loaded session %s (%s, %s)", path, GET_PRIVATE (session)->name, GET_PRIVATE (session)->comment);
153                 sessions = g_list_insert_sorted (sessions, session, compare_session);
154             }
155             else
156                 g_debug ("Ignoring session %s", path);
157         }
158     }
159 
160     g_dir_close (directory);
161 
162     return sessions;
163 }
164 
165 static GList *
load_sessions(const gchar * sessions_dir)166 load_sessions (const gchar *sessions_dir)
167 {
168     g_auto(GStrv) dirs = g_strsplit (sessions_dir, ":", -1);
169     GList *sessions = NULL;
170     for (int i = 0; dirs[i]; i++)
171     {
172         const gchar *default_type = "x";
173 
174         if (strcmp (dirs[i], WAYLAND_SESSIONS_DIR) == 0)
175             default_type = "wayland";
176 
177         sessions = load_sessions_dir (sessions, dirs[i], default_type);
178     }
179 
180     return sessions;
181 }
182 
183 static void
update_sessions(void)184 update_sessions (void)
185 {
186     if (have_sessions)
187         return;
188 
189     g_autofree gchar *sessions_dir = g_strdup (SESSIONS_DIR);
190     g_autofree gchar *remote_sessions_dir = g_strdup (REMOTE_SESSIONS_DIR);
191 
192     /* Use session directory from configuration */
193     config_load_from_standard_locations (config_get_instance (), NULL, NULL);
194 
195     gchar *value = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
196     if (value)
197     {
198         g_free (sessions_dir);
199         sessions_dir = value;
200     }
201 
202     value = config_get_string (config_get_instance (), "LightDM", "remote-sessions-directory");
203     if (value)
204     {
205         g_free (remote_sessions_dir);
206         remote_sessions_dir = value;
207     }
208 
209     local_sessions = load_sessions (sessions_dir);
210     remote_sessions = load_sessions (remote_sessions_dir);
211 
212     have_sessions = TRUE;
213 }
214 
215 /**
216  * lightdm_get_sessions:
217  *
218  * Get the available sessions.
219  *
220  * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession
221  **/
222 GList *
lightdm_get_sessions(void)223 lightdm_get_sessions (void)
224 {
225     update_sessions ();
226     return local_sessions;
227 }
228 
229 /**
230  * lightdm_get_remote_sessions:
231  *
232  * Get the available remote sessions.
233  *
234  * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession
235  **/
236 GList *
lightdm_get_remote_sessions(void)237 lightdm_get_remote_sessions (void)
238 {
239     update_sessions ();
240     return remote_sessions;
241 }
242 
243 /**
244  * lightdm_session_get_key:
245  * @session: A #LightDMSession
246  *
247  * Get the key for a session
248  *
249  * Return value: The session key
250  **/
251 const gchar *
lightdm_session_get_key(LightDMSession * session)252 lightdm_session_get_key (LightDMSession *session)
253 {
254     g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
255     return GET_PRIVATE (session)->key;
256 }
257 
258 /**
259  * lightdm_session_get_session_type:
260  * @session: A #LightDMSession
261  *
262  * Get the type a session
263  *
264  * Return value: The session type, e.g. x or mir
265  **/
266 const gchar *
lightdm_session_get_session_type(LightDMSession * session)267 lightdm_session_get_session_type (LightDMSession *session)
268 {
269     g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
270     return GET_PRIVATE (session)->type;
271 }
272 
273 /**
274  * lightdm_session_get_name:
275  * @session: A #LightDMSession
276  *
277  * Get the name for a session
278  *
279  * Return value: The session name
280  **/
281 const gchar *
lightdm_session_get_name(LightDMSession * session)282 lightdm_session_get_name (LightDMSession *session)
283 {
284     g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
285     return GET_PRIVATE (session)->name;
286 }
287 
288 /**
289  * lightdm_session_get_comment:
290  * @session: A #LightDMSession
291  *
292  * Get the comment for a session
293  *
294  * Return value: The session comment
295  **/
296 const gchar *
lightdm_session_get_comment(LightDMSession * session)297 lightdm_session_get_comment (LightDMSession *session)
298 {
299     g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
300     return GET_PRIVATE (session)->comment;
301 }
302 
303 static void
lightdm_session_init(LightDMSession * session)304 lightdm_session_init (LightDMSession *session)
305 {
306 }
307 
308 static void
lightdm_session_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)309 lightdm_session_set_property (GObject      *object,
310                               guint         prop_id,
311                               const GValue *value,
312                               GParamSpec   *pspec)
313 {
314     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
315 }
316 
317 static void
lightdm_session_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)318 lightdm_session_get_property (GObject    *object,
319                               guint       prop_id,
320                               GValue     *value,
321                               GParamSpec *pspec)
322 {
323     LightDMSession *self = LIGHTDM_SESSION (object);
324 
325     switch (prop_id) {
326     case PROP_KEY:
327         g_value_set_string (value, lightdm_session_get_key (self));
328         break;
329     case PROP_NAME:
330         g_value_set_string (value, lightdm_session_get_name (self));
331         break;
332     case PROP_COMMENT:
333         g_value_set_string (value, lightdm_session_get_comment (self));
334         break;
335     default:
336         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
337         break;
338     }
339 }
340 
341 static void
lightdm_session_finalize(GObject * object)342 lightdm_session_finalize (GObject *object)
343 {
344     LightDMSession *self = LIGHTDM_SESSION (object);
345     LightDMSessionPrivate *priv = GET_PRIVATE (self);
346 
347     g_free (priv->key);
348     g_free (priv->type);
349     g_free (priv->name);
350     g_free (priv->comment);
351 }
352 
353 static void
lightdm_session_class_init(LightDMSessionClass * klass)354 lightdm_session_class_init (LightDMSessionClass *klass)
355 {
356     GObjectClass *object_class = G_OBJECT_CLASS (klass);
357 
358     object_class->set_property = lightdm_session_set_property;
359     object_class->get_property = lightdm_session_get_property;
360     object_class->finalize = lightdm_session_finalize;
361 
362     g_object_class_install_property (object_class,
363                                      PROP_KEY,
364                                      g_param_spec_string ("key",
365                                                           "key",
366                                                           "Session key",
367                                                           NULL,
368                                                           G_PARAM_READABLE));
369     g_object_class_install_property (object_class,
370                                      PROP_NAME,
371                                      g_param_spec_string ("name",
372                                                           "name",
373                                                           "Session name",
374                                                           NULL,
375                                                           G_PARAM_READABLE));
376     g_object_class_install_property (object_class,
377                                      PROP_COMMENT,
378                                      g_param_spec_string ("comment",
379                                                           "comment",
380                                                           "Session comment",
381                                                           NULL,
382                                                           G_PARAM_READABLE));
383 }
384