]> git.lizzy.rs Git - torbrowser-launcher.git/blob - torbrowser_launcher/common.py
Merge pull request #196 from kraai/master
[torbrowser-launcher.git] / torbrowser_launcher / common.py
1 """
2 Tor Browser Launcher
3 https://github.com/micahflee/torbrowser-launcher/
4
5 Copyright (c) 2013-2014 Micah Lee <micah@micahflee.com>
6
7 Permission is hereby granted, free of charge, to any person
8 obtaining a copy of this software and associated documentation
9 files (the "Software"), to deal in the Software without
10 restriction, including without limitation the rights to use,
11 copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the
13 Software is furnished to do so, subject to the following
14 conditions:
15
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 OTHER DEALINGS IN THE SOFTWARE.
27 """
28
29 import os, sys, platform, subprocess, locale, pickle, json, psutil
30
31 import pygtk
32 pygtk.require('2.0')
33 import gtk
34
35 SHARE = os.getenv('TBL_SHARE', sys.prefix+'/share/torbrowser-launcher')
36
37 import gettext
38 gettext.install('torbrowser-launcher', os.path.join(SHARE, 'locale'))
39
40 from twisted.internet import gtk2reactor
41 gtk2reactor.install()
42
43 class Common:
44
45     def __init__(self, tbl_version):
46         self.tbl_version = tbl_version
47
48         # initialize the app
49         self.default_mirror = 'https://dist.torproject.org/'
50         self.discover_arch_lang()
51         self.build_paths()
52         for d in self.paths['dirs']:
53             self.mkdir(self.paths['dirs'][d])
54         self.load_mirrors()
55         self.load_settings()
56         self.mkdir(self.paths['download_dir'])
57         self.mkdir(self.paths['tbb']['dir'])
58         self.init_gnupg()
59
60         # allow buttons to have icons
61         try:
62             gtk_settings = gtk.settings_get_default()
63             gtk_settings.props.gtk_button_images = True
64         except:
65             pass
66
67     # discover the architecture and language
68     def discover_arch_lang(self):
69         # figure out the architecture
70         self.architecture = 'x86_64' if '64' in platform.architecture()[0] else 'i686'
71
72         # figure out the language
73         available_languages = ['en-US', 'ar', 'de', 'es-ES', 'fa', 'fr', 'it', 'ko', 'nl', 'pl', 'pt-PT', 'ru', 'vi', 'zh-CN']
74         default_locale = locale.getdefaultlocale()[0]
75         if default_locale is None:
76             self.language = 'en-US'
77         else:
78             self.language = default_locale.replace('_', '-')
79             if self.language not in available_languages:
80                 self.language = self.language.split('-')[0]
81                 if self.language not in available_languages:
82                     for l in available_languages:
83                         if l[0:2] == self.language:
84                             self.language = l
85             # if language isn't available, default to english
86             if self.language not in available_languages:
87                 self.language = 'en-US'
88
89     # build all relevant paths
90     def build_paths(self, tbb_version=None):
91         homedir = os.getenv('HOME')
92         if not homedir:
93             homedir = '/tmp/.torbrowser-'+os.getenv('USER')
94             if not os.path.exists(homedir):
95                 try:
96                     os.mkdir(homedir, 0700)
97                 except:
98                     self.set_gui('error', _("Error creating {0}").format(homedir), [], False)
99         if not os.access(homedir, os.W_OK):
100             self.set_gui('error', _("{0} is not writable").format(homedir), [], False)
101
102         tbb_config = '{0}/.config/torbrowser'.format(homedir)
103         tbb_cache = '{0}/.cache/torbrowser'.format(homedir)
104         tbb_local = '{0}/.local/share/torbrowser'.format(homedir)
105         old_tbb_data = '{0}/.torbrowser'.format(homedir)
106
107         if tbb_version:
108             # tarball filename
109             if self.architecture == 'x86_64':
110                 arch = 'linux64'
111             else:
112                 arch = 'linux32'
113             tarball_filename = 'tor-browser-'+arch+'-'+tbb_version+'_'+self.language+'.tar.xz'
114
115             # tarball
116             self.paths['tarball_url'] = '{0}torbrowser/'+tbb_version+'/'+tarball_filename
117             self.paths['tarball_file'] = tbb_cache+'/download/'+tarball_filename
118             self.paths['tarball_filename'] = tarball_filename
119
120             # sig
121             self.paths['sha256_file'] = tbb_cache+'/download/sha256sums.txt'
122             self.paths['sha256_sig_file'] = tbb_cache+'/download/sha256sums.txt.asc'
123             self.paths['sha256_url'] = '{0}torbrowser/'+tbb_version+'/sha256sums.txt'
124             self.paths['sha256_sig_url'] = '{0}torbrowser/'+tbb_version+'/sha256sums.txt.asc'
125         else:
126             self.paths = {
127                 'dirs': {
128                     'config': tbb_config,
129                     'cache': tbb_cache,
130                     'local': tbb_local,
131                 },
132                 'old_data_dir': old_tbb_data,
133                 'tbl_bin': sys.argv[0],
134                 'icon_file': os.path.join(os.path.dirname(SHARE), 'pixmaps/torbrowser80.xpm'),
135                 'torproject_pem': os.path.join(SHARE, 'torproject.pem'),
136                 'signing_keys': [os.path.join(SHARE, 'tor-browser-developers.asc')],
137                 'mirrors_txt': [os.path.join(SHARE, 'mirrors.txt'),
138                                 tbb_config+'/mirrors.txt'],
139                 'modem_sound': os.path.join(SHARE, 'modem.ogg'),
140                 'download_dir': tbb_cache+'/download',
141                 'gnupg_homedir': tbb_local+'/gnupg_homedir',
142                 'settings_file': tbb_config+'/settings.json',
143                 'settings_file_pickle': tbb_config+'/settings',
144                 'update_check_url': 'https://www.torproject.org/projects/torbrowser/RecommendedTBBVersions',
145                 'update_check_file': tbb_cache+'/download/RecommendedTBBVersions',
146                 'tbb': {
147                     'dir': tbb_local+'/tbb/'+self.architecture,
148                     'dir_tbb': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language,
149                     'start': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/start-tor-browser.desktop',
150                     'versions': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/Browser/TorBrowser/Docs/sources/versions',
151                 },
152             }
153
154     # create a directory
155     @staticmethod
156     def mkdir(path):
157         try:
158             if not os.path.exists(path):
159                 os.makedirs(path, 0700)
160                 return True
161         except:
162             print _("Cannot create directory {0}").format(path)
163             return False
164         if not os.access(path, os.W_OK):
165             print _("{0} is not writable").format(path)
166             return False
167         return True
168
169     # if gnupg_homedir isn't set up, set it up
170     def init_gnupg(self):
171         if not os.path.exists(self.paths['gnupg_homedir']):
172             print _('Creating GnuPG homedir'), self.paths['gnupg_homedir']
173             self.mkdir(self.paths['gnupg_homedir'])
174         self.import_keys()
175
176     # import gpg keys
177     def import_keys(self):
178         for key in self.paths['signing_keys']:
179             subprocess.Popen(['/usr/bin/gpg', '--quiet', '--homedir', self.paths['gnupg_homedir'], '--import', key]).wait()
180
181     # load mirrors
182     def load_mirrors(self):
183         self.mirrors = []
184         for srcfile in self.paths['mirrors_txt']:
185             if not os.path.exists(srcfile):
186                 continue
187             for mirror in open(srcfile, 'r').readlines():
188                 if mirror.strip() not in self.mirrors:
189                     self.mirrors.append(mirror.strip())
190
191     # load settings
192     def load_settings(self):
193         default_settings = {
194             'tbl_version': self.tbl_version,
195             'installed_version': False,
196             'latest_version': '0',
197             'update_over_tor': True,
198             'check_for_updates': False,
199             'modem_sound': False,
200             'last_update_check_timestamp': 0,
201             'mirror': self.default_mirror
202         }
203
204         if os.path.isfile(self.paths['settings_file']):
205             settings = json.load(open(self.paths['settings_file']))
206             resave = False
207
208             # make sure settings file is up-to-date
209             for setting in default_settings:
210                 if setting not in settings:
211                     settings[setting] = default_settings[setting]
212                     resave = True
213
214             # make sure the version is current
215             if settings['tbl_version'] != self.tbl_version:
216                 settings['tbl_version'] = self.tbl_version
217                 resave = True
218
219             self.settings = settings
220             if resave:
221                 self.save_settings()
222
223         # if settings file is still using old pickle format, convert to json
224         elif os.path.isfile(self.paths['settings_file_pickle']):
225             self.settings = pickle.load(open(self.paths['settings_file_pickle']))
226             self.save_settings()
227             os.remove(self.paths['settings_file_pickle'])
228             self.load_settings()
229
230         else:
231             self.settings = default_settings
232             self.save_settings()
233
234     # save settings
235     def save_settings(self):
236         json.dump(self.settings, open(self.paths['settings_file'], 'w'))
237         return True
238
239     # get the process id of a program
240     @staticmethod
241     def get_pid(bin_path, python=False):
242         pid = None
243
244         for p in psutil.process_iter():
245             try:
246                 if p.pid != os.getpid():
247                     exe = None
248                     if python:
249                         if len(p.cmdline) > 1:
250                             if 'python' in p.cmdline[0]:
251                                 exe = p.cmdline[1]
252                     else:
253                         if len(p.cmdline) > 0:
254                             exe = p.cmdline[0]
255
256                     if exe == bin_path:
257                         pid = p.pid
258
259             except:
260                 pass
261
262         return pid
263
264     # bring program's x window to front
265     @staticmethod
266     def bring_window_to_front(pid):
267         # figure out the window id
268         win_id = None
269         p = subprocess.Popen(['wmctrl', '-l', '-p'], stdout=subprocess.PIPE)
270         for line in p.stdout.readlines():
271             line_split = line.split()
272             cur_win_id = line_split[0]
273             cur_win_pid = int(line_split[2])
274             if cur_win_pid == pid:
275                 win_id = cur_win_id
276
277         # bring to front
278         if win_id:
279             subprocess.call(['wmctrl', '-i', '-a', win_id])