1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 1999 Free Software Foundation, Inc.
4  * Copyright (C) 2007, 2009 Vincent Untz.
5  * Copyright (C) 2008 Lucas Rocha.
6  * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
7  * Copyright (C) 2012-2021 MATE Developers
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <string.h>
30 #include <sys/stat.h>
31 
32 #include <glib/gi18n.h>
33 #include <glib/gstdio.h>
34 
35 #include "gsm-app-dialog.h"
36 #include "gsm-properties-dialog.h"
37 #include "gsm-util.h"
38 #include "gsp-app-manager.h"
39 #include "gsp-keyfile.h"
40 
41 #include "gsp-app.h"
42 
43 #define GSP_APP_SAVE_DELAY 2
44 
45 #define GSP_ASP_SAVE_MASK_HIDDEN   0x0001
46 #define GSP_ASP_SAVE_MASK_NAME     0x0002
47 #define GSP_ASP_SAVE_MASK_EXEC     0x0004
48 #define GSP_ASP_SAVE_MASK_COMMENT  0x0008
49 #define GSP_ASP_SAVE_MASK_DELAY    0x0010
50 #define GSP_ASP_SAVE_MASK_ALL      0xffff
51 
52 typedef struct {
53         char         *basename;
54         char         *path;
55 
56         gboolean      hidden;
57         gboolean      nodisplay;
58 
59         char         *name;
60         char         *exec;
61         char         *comment;
62         char         *icon;
63         gint         delay;
64 
65         GIcon        *gicon;
66         char         *description;
67 
68         /* position of the directory in the XDG environment variable */
69         unsigned int  xdg_position;
70         /* position of the first system directory in the XDG env var containing
71          * this autostart app too (G_MAXUINT means none) */
72         unsigned int  xdg_system_position;
73 
74         unsigned int  save_timeout;
75         /* mask of what has changed */
76         unsigned int  save_mask;
77         /* path that contains the original file that needs to be saved */
78         char         *old_system_path;
79         /* after writing to file, we skip the next file monitor event of type
80          * CHANGED */
81         gboolean      skip_next_monitor_event;
82 } GspAppPrivate;
83 
84 enum {
85         CHANGED,
86         REMOVED,
87         LAST_SIGNAL
88 };
89 
90 static guint gsp_app_signals[LAST_SIGNAL] = { 0 };
91 
92 
93 G_DEFINE_TYPE_WITH_PRIVATE (GspApp, gsp_app, G_TYPE_OBJECT)
94 
95 static void     gsp_app_dispose  (GObject *object);
96 static void     gsp_app_finalize (GObject *object);
97 static gboolean _gsp_app_save    (gpointer data);
98 
99 
100 static gboolean
_gsp_str_equal(const char * a,const char * b)101 _gsp_str_equal (const char *a,
102                 const char *b)
103 {
104         if (g_strcmp0 (a, b) == 0) {
105                 return TRUE;
106         }
107 
108         if (a && !b && a[0] == '\0') {
109                 return TRUE;
110         }
111 
112         if (b && !a && b[0] == '\0') {
113                 return TRUE;
114         }
115 
116         return FALSE;
117 }
118 
119 
120 static void
gsp_app_class_init(GspAppClass * class)121 gsp_app_class_init (GspAppClass *class)
122 {
123         GObjectClass *gobject_class = G_OBJECT_CLASS (class);
124 
125         gobject_class->dispose  = gsp_app_dispose;
126         gobject_class->finalize = gsp_app_finalize;
127 
128         gsp_app_signals[CHANGED] =
129                 g_signal_new ("changed",
130                               G_TYPE_FROM_CLASS (gobject_class),
131                               G_SIGNAL_RUN_LAST,
132                               G_STRUCT_OFFSET (GspAppClass,
133                                                changed),
134                               NULL,
135                               NULL,
136                               g_cclosure_marshal_VOID__VOID,
137                               G_TYPE_NONE, 0);
138 
139         gsp_app_signals[REMOVED] =
140                 g_signal_new ("removed",
141                               G_TYPE_FROM_CLASS (gobject_class),
142                               G_SIGNAL_RUN_LAST,
143                               G_STRUCT_OFFSET (GspAppClass,
144                                                removed),
145                               NULL,
146                               NULL,
147                               g_cclosure_marshal_VOID__VOID,
148                               G_TYPE_NONE, 0);
149 
150 }
151 
152 static void
gsp_app_init(GspApp * app)153 gsp_app_init (GspApp *app)
154 {
155         GspAppPrivate *priv;
156 
157         priv = gsp_app_get_instance_private (app);
158 
159         memset (priv, 0, sizeof (GspAppPrivate));
160         priv->xdg_position        = G_MAXUINT;
161         priv->xdg_system_position = G_MAXUINT;
162 }
163 
164 static void
_gsp_app_free_reusable_data(GspApp * app)165 _gsp_app_free_reusable_data (GspApp *app)
166 {
167         GspAppPrivate *priv;
168 
169         priv = gsp_app_get_instance_private (app);
170         if (priv->path) {
171                 g_free (priv->path);
172                 priv->path = NULL;
173         }
174 
175         if (priv->name) {
176                 g_free (priv->name);
177                 priv->name = NULL;
178         }
179 
180         if (priv->exec) {
181                 g_free (priv->exec);
182                 priv->exec = NULL;
183         }
184 
185         if (priv->comment) {
186                 g_free (priv->comment);
187                 priv->comment = NULL;
188         }
189 
190         if (priv->icon) {
191                 g_free (priv->icon);
192                 priv->icon = NULL;
193         }
194 
195         if (priv->gicon) {
196                 g_object_unref (priv->gicon);
197                 priv->gicon = NULL;
198         }
199 
200         if (priv->description) {
201                 g_free (priv->description);
202                 priv->description = NULL;
203         }
204 
205         if (priv->old_system_path) {
206                 g_free (priv->old_system_path);
207                 priv->old_system_path = NULL;
208         }
209 }
210 
211 static void
gsp_app_dispose(GObject * object)212 gsp_app_dispose (GObject *object)
213 {
214         GspApp *app;
215         GspAppPrivate *priv;
216 
217         g_return_if_fail (object != NULL);
218         g_return_if_fail (GSP_IS_APP (object));
219 
220         app = GSP_APP (object);
221         priv = gsp_app_get_instance_private (app);
222 
223         /* we save in dispose since we might need to reference GspAppManager */
224         if (priv->save_timeout) {
225                 g_source_remove (priv->save_timeout);
226                 priv->save_timeout = 0;
227 
228                 /* save now */
229                 _gsp_app_save (app);
230         }
231 
232         G_OBJECT_CLASS (gsp_app_parent_class)->dispose (object);
233 }
234 
235 static void
gsp_app_finalize(GObject * object)236 gsp_app_finalize (GObject *object)
237 {
238         GspApp *app;
239         GspAppPrivate *priv;
240 
241         g_return_if_fail (object != NULL);
242         g_return_if_fail (GSP_IS_APP (object));
243 
244         app = GSP_APP (object);
245         priv = gsp_app_get_instance_private (app);
246 
247         if (priv->basename) {
248                 g_free (priv->basename);
249                 priv->basename = NULL;
250         }
251 
252         _gsp_app_free_reusable_data (app);
253 
254         G_OBJECT_CLASS (gsp_app_parent_class)->finalize (object);
255 }
256 
257 static void
_gsp_app_emit_changed(GspApp * app)258 _gsp_app_emit_changed (GspApp *app)
259 {
260         g_signal_emit (G_OBJECT (app), gsp_app_signals[CHANGED], 0);
261 }
262 
263 static void
_gsp_app_emit_removed(GspApp * app)264 _gsp_app_emit_removed (GspApp *app)
265 {
266         g_signal_emit (G_OBJECT (app), gsp_app_signals[REMOVED], 0);
267 }
268 
269 static void
_gsp_app_update_description(GspApp * app)270 _gsp_app_update_description (GspApp *app)
271 {
272         const char *primary;
273         const char *secondary;
274         GspAppPrivate *priv;
275 
276         priv = gsp_app_get_instance_private (app);
277 
278         if (!gsm_util_text_is_blank (priv->name)) {
279                 primary = priv->name;
280         } else if (!gsm_util_text_is_blank (priv->exec)) {
281                 primary = priv->exec;
282         } else {
283                 primary = _("No name");
284         }
285 
286         if (!gsm_util_text_is_blank (priv->comment)) {
287                 secondary = priv->comment;
288         } else {
289                 secondary = _("No description");
290         }
291 
292         g_free (priv->description);
293         priv->description = g_markup_printf_escaped ("<b>%s</b>\n%s",
294                                                      primary,
295                                                      secondary);
296 }
297 
298 /*
299  * Saving
300  */
301 
302 static void
_gsp_ensure_user_autostart_dir(void)303 _gsp_ensure_user_autostart_dir (void)
304 {
305         char *dir;
306 
307         dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
308         g_mkdir_with_parents (dir, S_IRWXU);
309 
310         g_free (dir);
311 }
312 
313 static gboolean
_gsp_app_user_equal_system(GspApp * app,char ** system_path)314 _gsp_app_user_equal_system (GspApp  *app,
315                             char   **system_path)
316 {
317         GspAppManager *manager;
318         GspAppPrivate *priv;
319         const char    *system_dir;
320         char          *path;
321         char          *str;
322         GKeyFile      *keyfile;
323         guint          delay;
324 
325         manager = gsp_app_manager_get ();
326         priv = gsp_app_get_instance_private (app);
327         system_dir = gsp_app_manager_get_dir (manager,
328                                               priv->xdg_system_position);
329         g_object_unref (manager);
330         if (!system_dir) {
331                 return FALSE;
332         }
333 
334         path = g_build_filename (system_dir, priv->basename, NULL);
335 
336         keyfile = g_key_file_new ();
337         if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
338                 g_free (path);
339                 g_key_file_free (keyfile);
340                 return FALSE;
341         }
342 
343         if (gsp_key_file_get_boolean (keyfile,
344                                       G_KEY_FILE_DESKTOP_KEY_HIDDEN,
345                                       FALSE) != priv->hidden) {
346                 g_free (path);
347                 g_key_file_free (keyfile);
348                 return FALSE;
349         }
350 
351         str = gsp_key_file_get_locale_string (keyfile,
352                                               G_KEY_FILE_DESKTOP_KEY_NAME);
353         if (!_gsp_str_equal (str, priv->name)) {
354                 g_free (str);
355                 g_free (path);
356                 g_key_file_free (keyfile);
357                 return FALSE;
358         }
359         g_free (str);
360 
361         str = gsp_key_file_get_locale_string (keyfile,
362                                               G_KEY_FILE_DESKTOP_KEY_COMMENT);
363         if (!_gsp_str_equal (str, priv->comment)) {
364                 g_free (str);
365                 g_free (path);
366                 g_key_file_free (keyfile);
367                 return FALSE;
368         }
369         g_free (str);
370 
371         str = gsp_key_file_get_string (keyfile,
372                                        G_KEY_FILE_DESKTOP_KEY_EXEC);
373         if (!_gsp_str_equal (str, priv->exec)) {
374                 g_free (str);
375                 g_free (path);
376                 g_key_file_free (keyfile);
377                 return FALSE;
378         }
379         g_free (str);
380 
381         str = gsp_key_file_get_locale_string (keyfile,
382                                               G_KEY_FILE_DESKTOP_KEY_ICON);
383         if (!_gsp_str_equal (str, priv->icon)) {
384                 g_free (str);
385                 g_free (path);
386                 g_key_file_free (keyfile);
387                 return FALSE;
388         }
389         g_free (str);
390 
391         delay = gsp_key_file_get_delay(keyfile);
392         if (delay != priv->delay) {
393                 g_free (path);
394                 g_key_file_free (keyfile);
395                 return FALSE;
396         }
397 
398         g_key_file_free (keyfile);
399 
400         *system_path = path;
401 
402         return TRUE;
403 }
404 
405 static inline void
_gsp_app_save_done_success(GspApp * app)406 _gsp_app_save_done_success (GspApp *app)
407 {
408         GspAppPrivate *priv;
409 
410         priv = gsp_app_get_instance_private (app);
411         priv->save_mask = 0;
412 
413         if (priv->old_system_path) {
414                 g_free (priv->old_system_path);
415                 priv->old_system_path = NULL;
416         }
417 }
418 
419 static gboolean
_gsp_app_save(gpointer data)420 _gsp_app_save (gpointer data)
421 {
422         GspApp   *app;
423         char     *use_path;
424         GKeyFile *keyfile;
425         GError   *error;
426         GspAppPrivate *priv;
427 
428         app = GSP_APP (data);
429         priv = gsp_app_get_instance_private (app);
430 
431         /* first check if removing the data from the user dir and using the
432          * data from the system dir is enough -- this helps us keep clean the
433          * user config dir by removing unneeded files */
434         if (_gsp_app_user_equal_system (app, &use_path)) {
435                 if (g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
436                         g_remove (priv->path);
437                 }
438 
439                 g_free (priv->path);
440                 priv->path = use_path;
441 
442                 priv->xdg_position = priv->xdg_system_position;
443 
444                 _gsp_app_save_done_success (app);
445                 return FALSE;
446         }
447 
448         if (priv->old_system_path)
449                 use_path = priv->old_system_path;
450         else
451                 use_path = priv->path;
452 
453         keyfile = g_key_file_new ();
454 
455         error = NULL;
456         g_key_file_load_from_file (keyfile, use_path,
457                                    G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
458                                    &error);
459 
460         if (error) {
461                 g_error_free (error);
462                 gsp_key_file_populate (keyfile);
463         }
464 
465         if (priv->save_mask & GSP_ASP_SAVE_MASK_HIDDEN) {
466                 gsp_key_file_set_boolean (keyfile,
467                                           G_KEY_FILE_DESKTOP_KEY_HIDDEN,
468                                           priv->hidden);
469         }
470 
471         if (priv->save_mask & GSP_ASP_SAVE_MASK_NAME) {
472                 gsp_key_file_set_locale_string (keyfile,
473                                                 G_KEY_FILE_DESKTOP_KEY_NAME,
474                                                 priv->name);
475                 gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME);
476         }
477 
478         if (priv->save_mask & GSP_ASP_SAVE_MASK_COMMENT) {
479                 gsp_key_file_set_locale_string (keyfile,
480                                                 G_KEY_FILE_DESKTOP_KEY_COMMENT,
481                                                 priv->comment);
482                 gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT);
483         }
484 
485         if (priv->save_mask & GSP_ASP_SAVE_MASK_EXEC) {
486                 gsp_key_file_set_string (keyfile,
487                                          G_KEY_FILE_DESKTOP_KEY_EXEC,
488                                          priv->exec);
489         }
490 
491         if (priv->save_mask & GSP_ASP_SAVE_MASK_DELAY) {
492                 gsp_key_file_set_delay (keyfile, priv->delay);
493         }
494 
495         _gsp_ensure_user_autostart_dir ();
496         if (gsp_key_file_to_file (keyfile, priv->path, NULL)) {
497                 priv->skip_next_monitor_event = TRUE;
498                 _gsp_app_save_done_success (app);
499         } else {
500                 g_warning ("Could not save %s file", priv->path);
501         }
502 
503         g_key_file_free (keyfile);
504 
505         priv->save_timeout = 0;
506         return FALSE;
507 }
508 
509 static void
_gsp_app_queue_save(GspApp * app)510 _gsp_app_queue_save (GspApp *app)
511 {
512         GspAppPrivate *priv;
513 
514         priv = gsp_app_get_instance_private (app);
515         if (priv->save_timeout) {
516                 g_source_remove (priv->save_timeout);
517                 priv->save_timeout = 0;
518         }
519 
520         /* if the file was not in the user directory, then we'll create a copy
521          * there */
522         if (priv->xdg_position != 0) {
523                 priv->xdg_position = 0;
524 
525                 if (priv->old_system_path == NULL) {
526                         priv->old_system_path = priv->path;
527                         /* if old_system_path was not NULL, then it means we
528                          * tried to save and we failed; in that case, we want
529                          * to try again and use the old file as a basis again */
530                 }
531 
532                 priv->path = g_build_filename (g_get_user_config_dir (),
533                                                "autostart",
534                                                priv->basename, NULL);
535         }
536 
537         priv->save_timeout = g_timeout_add_seconds (GSP_APP_SAVE_DELAY,
538                                                     _gsp_app_save,
539                                                     app);
540 }
541 
542 /*
543  * Accessors
544  */
545 
546 const char *
gsp_app_get_basename(GspApp * app)547 gsp_app_get_basename (GspApp *app)
548 {
549         GspAppPrivate *priv;
550 
551         g_return_val_if_fail (GSP_IS_APP (app), NULL);
552 
553         priv = gsp_app_get_instance_private (app);
554 
555         return priv->basename;
556 }
557 
558 const char *
gsp_app_get_path(GspApp * app)559 gsp_app_get_path (GspApp *app)
560 {
561         GspAppPrivate *priv;
562 
563         g_return_val_if_fail (GSP_IS_APP (app), NULL);
564 
565         priv = gsp_app_get_instance_private (app);
566 
567         return priv->path;
568 }
569 
570 gboolean
gsp_app_get_hidden(GspApp * app)571 gsp_app_get_hidden (GspApp *app)
572 {
573         GspAppPrivate *priv;
574 
575         g_return_val_if_fail (GSP_IS_APP (app), FALSE);
576 
577         priv = gsp_app_get_instance_private (app);
578 
579         return priv->hidden;
580 }
581 
582 void
gsp_app_set_hidden(GspApp * app,gboolean hidden)583 gsp_app_set_hidden (GspApp   *app,
584                     gboolean  hidden)
585 {
586         GspAppPrivate *priv;
587 
588         g_return_if_fail (GSP_IS_APP (app));
589 
590         priv = gsp_app_get_instance_private (app);
591 
592         if (hidden == priv->hidden) {
593                 return;
594         }
595 
596         priv->hidden = hidden;
597         priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
598 
599         _gsp_app_queue_save (app);
600         _gsp_app_emit_changed (app);
601 }
602 
603 gboolean
gsp_app_get_nodisplay(GspApp * app)604 gsp_app_get_nodisplay (GspApp *app)
605 {
606         GspAppPrivate *priv;
607 
608         g_return_val_if_fail (GSP_IS_APP (app), FALSE);
609 
610         priv = gsp_app_get_instance_private (app);
611 
612         return priv->nodisplay;
613 }
614 
615 const char *
gsp_app_get_name(GspApp * app)616 gsp_app_get_name (GspApp *app)
617 {
618         GspAppPrivate *priv;
619 
620         g_return_val_if_fail (GSP_IS_APP (app), NULL);
621 
622         priv = gsp_app_get_instance_private (app);
623 
624         return priv->name;
625 }
626 
627 const char *
gsp_app_get_exec(GspApp * app)628 gsp_app_get_exec (GspApp *app)
629 {
630         GspAppPrivate *priv;
631 
632         g_return_val_if_fail (GSP_IS_APP (app), NULL);
633 
634         priv = gsp_app_get_instance_private (app);
635 
636         return priv->exec;
637 }
638 
639 const char *
gsp_app_get_comment(GspApp * app)640 gsp_app_get_comment (GspApp *app)
641 {
642         GspAppPrivate *priv;
643 
644         g_return_val_if_fail (GSP_IS_APP (app), NULL);
645 
646         priv = gsp_app_get_instance_private (app);
647 
648         return priv->comment;
649 }
650 
651 guint
gsp_app_get_delay(GspApp * app)652 gsp_app_get_delay (GspApp *app)
653 {
654         GspAppPrivate *priv;
655 
656         g_return_val_if_fail (GSP_IS_APP (app), 0);
657         priv = gsp_app_get_instance_private (app);
658 
659         return priv->delay;
660 }
661 
662 GIcon *
gsp_app_get_icon(GspApp * app)663 gsp_app_get_icon (GspApp *app)
664 {
665         GspAppPrivate *priv;
666 
667         g_return_val_if_fail (GSP_IS_APP (app), NULL);
668 
669         priv = gsp_app_get_instance_private (app);
670 
671         if (priv->gicon) {
672                 return g_object_ref (priv->gicon);
673         } else {
674                 return NULL;
675         }
676 }
677 
678 unsigned int
gsp_app_get_xdg_position(GspApp * app)679 gsp_app_get_xdg_position (GspApp *app)
680 {
681         GspAppPrivate *priv;
682 
683         g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
684 
685         priv = gsp_app_get_instance_private (app);
686 
687         return priv->xdg_position;
688 }
689 
690 unsigned int
gsp_app_get_xdg_system_position(GspApp * app)691 gsp_app_get_xdg_system_position (GspApp *app)
692 {
693         GspAppPrivate *priv;
694 
695         g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
696 
697         priv = gsp_app_get_instance_private (app);
698 
699         return priv->xdg_system_position;
700 }
701 
702 void
gsp_app_set_xdg_system_position(GspApp * app,unsigned int position)703 gsp_app_set_xdg_system_position (GspApp       *app,
704                                  unsigned int  position)
705 {
706         GspAppPrivate *priv;
707 
708         g_return_if_fail (GSP_IS_APP (app));
709 
710         priv = gsp_app_get_instance_private (app);
711 
712         priv->xdg_system_position = position;
713 }
714 
715 const char *
gsp_app_get_description(GspApp * app)716 gsp_app_get_description (GspApp *app)
717 {
718         GspAppPrivate *priv;
719 
720         g_return_val_if_fail (GSP_IS_APP (app), NULL);
721 
722         priv = gsp_app_get_instance_private (app);
723 
724         return priv->description;
725 }
726 
727 /*
728  * High-level edition
729  */
730 
731 void
gsp_app_update(GspApp * app,const char * name,const char * comment,const char * exec,guint delay)732 gsp_app_update (GspApp     *app,
733                 const char *name,
734                 const char *comment,
735                 const char *exec,
736                 guint       delay)
737 {
738         gboolean    changed;
739         GspAppPrivate *priv;
740 
741         g_return_if_fail (GSP_IS_APP (app));
742 
743         changed = FALSE;
744         priv = gsp_app_get_instance_private (app);
745 
746         if (!_gsp_str_equal (name, priv->name)) {
747                 changed = TRUE;
748                 g_free (priv->name);
749                 priv->name = g_strdup (name);
750                 priv->save_mask |= GSP_ASP_SAVE_MASK_NAME;
751         }
752 
753         if (!_gsp_str_equal (comment, priv->comment)) {
754                 changed = TRUE;
755                 g_free (priv->comment);
756                 priv->comment = g_strdup (comment);
757                 priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT;
758         }
759 
760         if (changed) {
761                 _gsp_app_update_description (app);
762         }
763 
764         if (!_gsp_str_equal (exec, priv->exec)) {
765                 changed = TRUE;
766                 g_free (priv->exec);
767                 priv->exec = g_strdup (exec);
768                 priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC;
769         }
770 
771         if ( delay != priv->delay) {
772                 changed = TRUE;
773                 priv->delay = delay;
774                 priv->save_mask |= GSP_ASP_SAVE_MASK_DELAY;
775         }
776 
777         if (changed) {
778                 _gsp_app_queue_save (app);
779                 _gsp_app_emit_changed (app);
780         }
781 }
782 
783 void
gsp_app_delete(GspApp * app)784 gsp_app_delete (GspApp *app)
785 {
786         GspAppPrivate *priv;
787 
788         g_return_if_fail (GSP_IS_APP (app));
789 
790         priv = gsp_app_get_instance_private (app);
791         if (priv->xdg_position == 0 &&
792             priv->xdg_system_position == G_MAXUINT) {
793                 /* exists in user directory only */
794                 if (priv->save_timeout) {
795                         g_source_remove (priv->save_timeout);
796                         priv->save_timeout = 0;
797                 }
798 
799                 if (g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
800                         g_remove (priv->path);
801                 }
802 
803                 /* for extra safety */
804                 priv->hidden = TRUE;
805                 priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
806 
807                 _gsp_app_emit_removed (app);
808         } else {
809                 /* also exists in system directory, so we have to keep a file
810                  * in the user directory */
811                 priv->hidden = TRUE;
812                 priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
813 
814                 _gsp_app_queue_save (app);
815                 _gsp_app_emit_changed (app);
816         }
817 }
818 
819 /*
820  * New autostart app
821  */
822 
823 void
gsp_app_reload_at(GspApp * app,const char * path,unsigned int xdg_position)824 gsp_app_reload_at (GspApp       *app,
825                    const char   *path,
826                    unsigned int  xdg_position)
827 {
828         GspAppPrivate *priv;
829 
830         g_return_if_fail (GSP_IS_APP (app));
831 
832         priv = gsp_app_get_instance_private (app);
833 
834         priv->xdg_position = G_MAXUINT;
835         gsp_app_new (path, xdg_position);
836 }
837 
838 static gboolean
gsp_app_can_launch(GKeyFile * keyfile)839 gsp_app_can_launch (GKeyFile *keyfile)
840 {
841         char **only_show_in, **not_show_in;
842         gboolean found;
843         int i;
844 
845         only_show_in = g_key_file_get_string_list (keyfile,
846                                                    G_KEY_FILE_DESKTOP_GROUP,
847                                                    G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN,
848                                                    NULL, NULL);
849         if (only_show_in) {
850                 for (i = 0, found = FALSE; only_show_in[i] && !found; i++) {
851                         if (!strcmp (only_show_in[i], "MATE"))
852                                 found = TRUE;
853                 }
854                 g_strfreev (only_show_in);
855                 if (!found)
856                     return FALSE;
857         }
858         not_show_in = g_key_file_get_string_list (keyfile,
859                                                   G_KEY_FILE_DESKTOP_GROUP,
860                                                   G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN,
861                                                   NULL, NULL);
862         if (not_show_in) {
863                 for (i = 0, found = FALSE; not_show_in[i] && !found; i++) {
864                         if (!strcmp (not_show_in[i], "MATE"))
865                                 found = TRUE;
866                 }
867                 g_strfreev (not_show_in);
868                 if (found)
869                         return FALSE;
870         }
871         return TRUE;
872 }
873 
874 GspApp *
gsp_app_new(const char * path,unsigned int xdg_position)875 gsp_app_new (const char   *path,
876              unsigned int  xdg_position)
877 {
878         GspAppManager *manager;
879         GspApp        *app;
880         GKeyFile      *keyfile;
881         char          *basename;
882         gboolean       new;
883         GspAppPrivate *priv;
884 
885         basename = g_path_get_basename (path);
886 
887         manager = gsp_app_manager_get ();
888         app = gsp_app_manager_find_app_with_basename (manager, basename);
889         priv = gsp_app_get_instance_private (app);
890         g_object_unref (manager);
891 
892         new = (app == NULL);
893 
894         if (!new) {
895                 if (priv->xdg_position == xdg_position) {
896                         if (priv->skip_next_monitor_event) {
897                                 priv->skip_next_monitor_event = FALSE;
898                                 return NULL;
899                         }
900                         /* else: the file got changed but not by us, we'll
901                          * update our data from disk */
902                 }
903 
904                 if (priv->xdg_position < xdg_position ||
905                     priv->save_timeout != 0) {
906                         /* we don't really care about this file, since we
907                          * already have something with a higher priority, or
908                          * we're going to write something in the user config
909                          * anyway.
910                          * Note: xdg_position >= 1 so it's a system dir */
911                         priv->xdg_system_position = MIN (xdg_position,
912                                                               priv->xdg_system_position);
913                         return NULL;
914                 }
915         }
916 
917         keyfile = g_key_file_new ();
918         if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
919                 g_key_file_free (keyfile);
920                 g_free (basename);
921                 return NULL;
922         }
923 
924         if (!gsp_app_can_launch (keyfile)) {
925                 g_key_file_free (keyfile);
926                 g_free (basename);
927                 return NULL;
928         }
929 
930         if (new) {
931                 app = g_object_new (GSP_TYPE_APP, NULL);
932                 priv = gsp_app_get_instance_private (app);
933                 priv->basename = basename;
934         } else {
935                 g_free (basename);
936                 _gsp_app_free_reusable_data (app);
937         }
938 
939         priv->path = g_strdup (path);
940 
941         priv->hidden = gsp_key_file_get_boolean (keyfile,
942                                                  G_KEY_FILE_DESKTOP_KEY_HIDDEN,
943                                                  FALSE);
944         priv->nodisplay = gsp_key_file_get_boolean (keyfile,
945                                                     G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
946                                                     FALSE);
947         priv->name = gsp_key_file_get_locale_string (keyfile,
948                                                      G_KEY_FILE_DESKTOP_KEY_NAME);
949         priv->exec = gsp_key_file_get_string (keyfile,
950                                               G_KEY_FILE_DESKTOP_KEY_EXEC);
951         priv->comment = gsp_key_file_get_locale_string (keyfile,
952                                                         G_KEY_FILE_DESKTOP_KEY_COMMENT);
953         priv->delay = gsp_key_file_get_delay (keyfile);
954 
955         if (gsm_util_text_is_blank (priv->name)) {
956                 g_free (priv->name);
957                 priv->name = g_strdup (priv->exec);
958         }
959 
960         priv->icon = gsp_key_file_get_locale_string (keyfile,
961                                                      G_KEY_FILE_DESKTOP_KEY_ICON);
962 
963         if (priv->icon) {
964                 /* look at icon and see if it's a themed icon or not */
965                 if (g_path_is_absolute (priv->icon)) {
966                         GFile *iconfile;
967 
968                         iconfile = g_file_new_for_path (priv->icon);
969                         priv->gicon = g_file_icon_new (iconfile);
970                         g_object_unref (iconfile);
971                 } else {
972                         priv->gicon = g_themed_icon_new (priv->icon);
973                 }
974         } else {
975                 priv->gicon = NULL;
976         }
977 
978         g_key_file_free (keyfile);
979 
980         _gsp_app_update_description (app);
981 
982         if (xdg_position > 0) {
983                 g_assert (xdg_position <= priv->xdg_system_position);
984                 priv->xdg_system_position = xdg_position;
985         }
986         /* else we keep the old value (which is G_MAXUINT if it wasn't set) */
987         priv->xdg_position = xdg_position;
988 
989         g_assert (!new || priv->save_timeout == 0);
990         priv->save_timeout = 0;
991         priv->old_system_path = NULL;
992         priv->skip_next_monitor_event = FALSE;
993 
994         if (!new) {
995                 _gsp_app_emit_changed (app);
996         }
997 
998         return app;
999 }
1000 
1001 static char *
_gsp_find_free_basename(const char * suggested_basename)1002 _gsp_find_free_basename (const char *suggested_basename)
1003 {
1004         GspAppManager *manager;
1005         char          *base_path;
1006         char          *filename;
1007         char          *basename;
1008         int            i;
1009 
1010         if (g_str_has_suffix (suggested_basename, ".desktop")) {
1011                 char *basename_no_ext;
1012 
1013                 basename_no_ext = g_strndup (suggested_basename,
1014                                              strlen (suggested_basename) - strlen (".desktop"));
1015                 base_path = g_build_filename (g_get_user_config_dir (),
1016                                               "autostart",
1017                                               basename_no_ext, NULL);
1018                 g_free (basename_no_ext);
1019         } else {
1020                 base_path = g_build_filename (g_get_user_config_dir (),
1021                                               "autostart",
1022                                               suggested_basename, NULL);
1023         }
1024 
1025         filename = g_strdup_printf ("%s.desktop", base_path);
1026         basename = g_path_get_basename (filename);
1027 
1028         manager = gsp_app_manager_get ();
1029 
1030         i = 1;
1031 #define _GSP_FIND_MAX_TRY 10000
1032         while (gsp_app_manager_find_app_with_basename (manager,
1033                                                        basename) != NULL &&
1034                g_file_test (filename, G_FILE_TEST_EXISTS) &&
1035                i < _GSP_FIND_MAX_TRY) {
1036                 g_free (filename);
1037                 g_free (basename);
1038 
1039                 filename = g_strdup_printf ("%s-%d.desktop", base_path, i);
1040                 basename = g_path_get_basename (filename);
1041 
1042                 i++;
1043         }
1044 
1045         g_object_unref (manager);
1046 
1047         g_free (base_path);
1048         g_free (filename);
1049 
1050         if (i == _GSP_FIND_MAX_TRY) {
1051                 g_free (basename);
1052                 return NULL;
1053         }
1054 
1055         return basename;
1056 }
1057 
1058 void
gsp_app_create(const char * name,const char * comment,const char * exec,guint delay)1059 gsp_app_create (const char *name,
1060                 const char *comment,
1061                 const char *exec,
1062                 guint       delay)
1063 {
1064         GspAppManager  *manager;
1065         GspAppPrivate *priv;
1066         GspApp         *app;
1067         char           *basename;
1068         char          **argv;
1069         int             argc;
1070 
1071         g_return_if_fail (!gsm_util_text_is_blank (exec));
1072 
1073         if (!g_shell_parse_argv (exec, &argc, &argv, NULL)) {
1074                 return;
1075         }
1076 
1077         basename = _gsp_find_free_basename (argv[0]);
1078         g_strfreev (argv);
1079         if (basename == NULL) {
1080                 return;
1081         }
1082 
1083         app = g_object_new (GSP_TYPE_APP, NULL);
1084         priv = gsp_app_get_instance_private (app);
1085 
1086         priv->basename = basename;
1087         priv->path = g_build_filename (g_get_user_config_dir (),
1088                                        "autostart",
1089                                        priv->basename, NULL);
1090 
1091         priv->hidden = FALSE;
1092         priv->nodisplay = FALSE;
1093 
1094         if (!gsm_util_text_is_blank (name)) {
1095                 priv->name = g_strdup (name);
1096         } else {
1097                 priv->name = g_strdup (exec);
1098         }
1099         priv->exec = g_strdup (exec);
1100         priv->comment = g_strdup (comment);
1101         priv->delay = delay;
1102         priv->icon = NULL;
1103 
1104         priv->gicon = NULL;
1105         _gsp_app_update_description (app);
1106 
1107         /* by definition */
1108         priv->xdg_position = 0;
1109         priv->xdg_system_position = G_MAXUINT;
1110 
1111         priv->save_timeout = 0;
1112         priv->save_mask |= GSP_ASP_SAVE_MASK_ALL;
1113         priv->old_system_path = NULL;
1114         priv->skip_next_monitor_event = FALSE;
1115 
1116         _gsp_app_queue_save (app);
1117 
1118         manager = gsp_app_manager_get ();
1119         gsp_app_manager_add (manager, app);
1120         g_object_unref (app);
1121         g_object_unref (manager);
1122 }
1123 
1124 gboolean
gsp_app_copy_desktop_file(const char * uri)1125 gsp_app_copy_desktop_file (const char *uri)
1126 {
1127         GspAppManager *manager;
1128         GspAppPrivate *priv;
1129         GspApp        *app;
1130         GFile         *src_file;
1131         char          *src_basename;
1132         char          *dst_basename;
1133         char          *dst_path;
1134         GFile         *dst_file;
1135         gboolean       changed;
1136 
1137         g_return_val_if_fail (uri != NULL, FALSE);
1138 
1139         src_file = g_file_new_for_uri (uri);
1140         src_basename = g_file_get_basename (src_file);
1141 
1142         if (src_basename == NULL) {
1143                 g_object_unref (src_file);
1144                 return FALSE;
1145         }
1146 
1147         dst_basename = _gsp_find_free_basename (src_basename);
1148         g_free (src_basename);
1149 
1150         if (dst_basename == NULL) {
1151                 g_object_unref (src_file);
1152                 return FALSE;
1153         }
1154 
1155         dst_path = g_build_filename (g_get_user_config_dir (),
1156                                      "autostart",
1157                                      dst_basename, NULL);
1158         g_free (dst_basename);
1159 
1160         dst_file = g_file_new_for_path (dst_path);
1161 
1162         _gsp_ensure_user_autostart_dir ();
1163         if (!g_file_copy (src_file, dst_file, G_FILE_COPY_NONE,
1164                           NULL, NULL, NULL, NULL)) {
1165                 g_object_unref (src_file);
1166                 g_object_unref (dst_file);
1167                 g_free (dst_path);
1168                 return FALSE;
1169         }
1170 
1171         g_object_unref (src_file);
1172         g_object_unref (dst_file);
1173 
1174         app = gsp_app_new (dst_path, 0);
1175         priv = gsp_app_get_instance_private (app);
1176         if (!app) {
1177                 g_remove (dst_path);
1178                 g_free (dst_path);
1179                 return FALSE;
1180         }
1181 
1182         g_free (dst_path);
1183 
1184         changed = FALSE;
1185         if (priv->hidden) {
1186                 changed = TRUE;
1187                 priv->hidden = FALSE;
1188                 priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
1189         }
1190 
1191         if (changed) {
1192                 _gsp_app_queue_save (app);
1193         }
1194 
1195         manager = gsp_app_manager_get ();
1196         gsp_app_manager_add (manager, app);
1197         g_object_unref (app);
1198         g_object_unref (manager);
1199 
1200         return TRUE;
1201 }
1202