1#!/usr/local/bin/python3.8
2
3# openbox-xdg-autostart runs things based on the XDG autostart specification
4# Copyright (C) 2008       Dana Jansens
5#
6# XDG autostart specification can be found here:
7# http://standards.freedesktop.org/autostart-spec/
8#
9#
10#
11# LICENSE:
12#   This program is free software; you can redistribute it and/or modify
13#   it under the terms of the GNU General Public License as published by
14#   the Free Software Foundation; either version 2 of the License, or
15#   (at your option) any later version.
16#
17#   This program is distributed in the hope that it will be useful,
18#   but WITHOUT ANY WARRANTY; without even the implied warranty of
19#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20#   GNU General Public License for more details.
21
22from __future__ import print_function
23
24ME="openbox-xdg-autostart"
25VERSION="1.1"
26
27import os, glob, sys
28try:
29    from xdg import BaseDirectory
30    from xdg.DesktopEntry import DesktopEntry
31    from xdg.Exceptions import ParsingError
32except ImportError:
33    print()
34    print("ERROR:", ME, "requires PyXDG to be installed", file=sys.stderr)
35    print()
36    sys.exit(1)
37
38def main(argv=sys.argv):
39    if "--help" in argv[1:]:
40        show_help()
41        return 0
42    if "--version" in argv[1:]:
43        show_version()
44        return 0
45
46    # get the autostart directories
47    autodirs = BaseDirectory.load_config_paths("autostart")
48
49    # find all the autostart files
50    files = []
51    for dir in autodirs:
52        for path in glob.glob(os.path.join(dir, '*.desktop')):
53            try:
54                autofile = AutostartFile(path)
55            except ParsingError:
56                print("Invalid .desktop file: " + path)
57            else:
58                if not autofile in files:
59                    files.append(autofile)
60
61    list = False
62    if "--list" in argv[1:]:
63        list = True
64        argv.remove("--list")
65
66    # run them !
67    environments = argv[1:]
68    for autofile in files:
69        if list: autofile.display(environments)
70        else: autofile.run(environments)
71
72class AutostartFile:
73    def __init__(self, path):
74        self.path = path
75        self.filename = os.path.basename(path)
76        self.dirname = os.path.dirname(path)
77        self.de = DesktopEntry(path)
78
79    def __eq__(self, other):
80        return self.filename == other.filename
81
82    def __str__(self):
83        return self.path + " : " + self.de.getName()
84
85    def _isexecfile(self, path):
86        return os.access(path, os.X_OK)
87
88    def _findFile(self, path, search, match_func):
89        # check empty path
90        if not path: return None
91        # check absolute path
92        if path[0] == '/':
93            if match_func(path): return path
94            else: return None
95        else:
96            # check relative path
97            for dirname in search.split(os.pathsep):
98                if dirname != "":
99                    candidate = os.path.join(dirname, path)
100                    if (match_func(candidate)): return candidate
101
102    def _alert(self, str, info=False):
103        if info:
104            print("\t ", str)
105        else:
106            print("\t*", str)
107
108    def _showInEnvironment(self, envs, verbose=False):
109        default = not self.de.getOnlyShowIn()
110        noshow = False
111        force = False
112        for i in self.de.getOnlyShowIn():
113            if i in envs: force = True
114        for i in self.de.getNotShowIn():
115            if i in envs: noshow = True
116
117        if verbose:
118            if not default and not force:
119                s = ""
120                for i in self.de.getOnlyShowIn():
121                    if s: s += ", "
122                    s += i
123                self._alert("Excluded by: OnlyShowIn (" + s + ")")
124            if default and noshow and not force:
125                s = ""
126                for i in self.de.getNotShowIn():
127                    if s: s += ", "
128                    s += i
129                self._alert("Excluded by: NotShowIn (" + s + ")")
130        return (default and not noshow) or force
131
132    def _shouldRun(self, envs, verbose=False):
133        if not self.de.getExec():
134            if verbose: self._alert("Excluded by: Missing Exec field")
135            return False
136        if self.de.getHidden():
137            if verbose: self._alert("Excluded by: Hidden")
138            return False
139        if self.de.getTryExec():
140            if not self._findFile(self.de.getTryExec(), os.getenv("PATH"),
141                                  self._isexecfile):
142                if verbose: self._alert("Excluded by: TryExec (" +
143                                        self.de.getTryExec() + ")")
144                return False
145        if not self._showInEnvironment(envs, verbose):
146            return False
147        return True
148
149    def display(self, envs):
150        if self._shouldRun(envs):
151            print("[*] " + self.de.getName())
152        else:
153            print("[ ] " + self.de.getName())
154        self._alert("File: " + self.path, info=True)
155        if self.de.getExec():
156            self._alert("Executes: " + self.de.getExec(), info=True)
157        self._shouldRun(envs, True)
158        print()
159
160    def run(self, envs):
161        here = os.getcwd()
162        if self.de.getPath():
163            os.chdir(self.de.getPath())
164        if self._shouldRun(envs):
165            args = ["/bin/sh", "-c", "exec " + self.de.getExec()]
166            os.spawnv(os.P_NOWAIT, args[0], args);
167        os.chdir(here)
168
169def show_help():
170    print("Usage:", ME, "[OPTION]... [ENVIRONMENT]...")
171    print()
172    print("This tool will run xdg autostart .desktop files")
173    print()
174    print("OPTIONS")
175    print("  --list        Show a list of the files which would be run")
176    print("                Files which would be run are marked with an asterix")
177    print("                symbol [*].  For files which would not be run,")
178    print("                information is given for why they are excluded")
179    print("  --help        Show this help and exit")
180    print("  --version     Show version and copyright information")
181    print()
182    print("ENVIRONMENT specifies a list of environments for which to run autostart")
183    print("applications.  If none are specified, only applications which do not ")
184    print("limit themselves to certain environments will be run.")
185    print()
186    print("ENVIRONMENT can be one or more of:")
187    print("  GNOME         Gnome Desktop")
188    print("  KDE           KDE Desktop")
189    print("  ROX           ROX Desktop")
190    print("  XFCE          XFCE Desktop")
191    print("  Old           Legacy systems")
192    print()
193
194def show_version():
195    print(ME, VERSION)
196    print("Copyright (c) 2008        Dana Jansens")
197    print()
198
199if __name__ == "__main__":
200        sys.exit(main())
201