]> git.lizzy.rs Git - torbrowser-launcher.git/blob - torbrowser_launcher/common.py
Merge branch 'master' of github.com:micahflee/torbrowser-launcher
[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, 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, 'erinn.asc'), 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',
143                 'update_check_url': 'https://www.torproject.org/projects/torbrowser/RecommendedTBBVersions',
144                 'update_check_file': tbb_cache+'/download/RecommendedTBBVersions',
145                 'tbb': {
146                     'dir': tbb_local+'/tbb/'+self.architecture,
147                     'dir_tbb': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language,
148                     'start': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/start-tor-browser.desktop',
149                     'versions': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/Browser/TorBrowser/Docs/sources/versions',
150                 },
151             }
152
153     # create a directory
154     @staticmethod
155     def mkdir(path):
156         try:
157             if not os.path.exists(path):
158                 os.makedirs(path, 0700)
159                 return True
160         except:
161             print _("Cannot create directory {0}").format(path)
162             return False
163         if not os.access(path, os.W_OK):
164             print _("{0} is not writable").format(path)
165             return False
166         return True
167
168     # if gnupg_homedir isn't set up, set it up
169     def init_gnupg(self):
170         if not os.path.exists(self.paths['gnupg_homedir']):
171             print _('Creating GnuPG homedir'), self.paths['gnupg_homedir']
172             self.mkdir(self.paths['gnupg_homedir'])
173         self.import_keys()
174
175     # import gpg keys
176     def import_keys(self):
177         for key in self.paths['signing_keys']:
178             subprocess.Popen(['/usr/bin/gpg', '--quiet', '--homedir', self.paths['gnupg_homedir'], '--import', key]).wait()
179
180     # load mirrors
181     def load_mirrors(self):
182         self.mirrors = []
183         for srcfile in self.paths['mirrors_txt']:
184             if not os.path.exists(srcfile):
185                 continue
186             for mirror in open(srcfile, 'r').readlines():
187                 if mirror.strip() not in self.mirrors:
188                     self.mirrors.append(mirror.strip())
189
190     # load settings
191     def load_settings(self):
192         default_settings = {
193             'tbl_version': self.tbl_version,
194             'installed_version': False,
195             'latest_version': '0',
196             'update_over_tor': True,
197             'check_for_updates': False,
198             'modem_sound': False,
199             'last_update_check_timestamp': 0,
200             'mirror': self.default_mirror
201         }
202
203         if os.path.isfile(self.paths['settings_file']):
204             settings = pickle.load(open(self.paths['settings_file']))
205             resave = False
206
207             # settings migrations
208             if settings['tbl_version'] <= '0.1.0':
209                 print '0.1.0 migration'
210                 settings['installed_version'] = settings['installed_version']['stable']
211                 settings['latest_version'] = settings['latest_version']['stable']
212                 resave = True
213
214                 # make new tbb folder
215                 self.mkdir(self.paths['tbb']['dir'])
216                 old_tbb_dir = self.paths['old_data_dir']+'/tbb/stable/'+self.architecture+'/tor-browser_'+self.language
217                 new_tbb_dir = self.paths['tbb']['dir']+'/tor-browser_'+self.language
218                 if os.path.isdir(old_tbb_dir):
219                     os.rename(old_tbb_dir, new_tbb_dir)
220
221             # make sure settings file is up-to-date
222             for setting in default_settings:
223                 if setting not in settings:
224                     settings[setting] = default_settings[setting]
225                     resave = True
226
227             # make sure the version is current
228             if settings['tbl_version'] != self.tbl_version:
229                 settings['tbl_version'] = self.tbl_version
230                 resave = True
231
232             self.settings = settings
233             if resave:
234                 self.save_settings()
235
236         else:
237             self.settings = default_settings
238             self.save_settings()
239
240     # save settings
241     def save_settings(self):
242         pickle.dump(self.settings, open(self.paths['settings_file'], 'w'))
243         return True
244
245     # get the process id of a program
246     @staticmethod
247     def get_pid(bin_path, python=False):
248         pid = None
249
250         for p in psutil.process_iter():
251             try:
252                 if p.pid != os.getpid():
253                     exe = None
254                     if python:
255                         if len(p.cmdline) > 1:
256                             if 'python' in p.cmdline[0]:
257                                 exe = p.cmdline[1]
258                     else:
259                         if len(p.cmdline) > 0:
260                             exe = p.cmdline[0]
261
262                     if exe == bin_path:
263                         pid = p.pid
264
265             except:
266                 pass
267
268         return pid
269
270     # bring program's x window to front
271     @staticmethod
272     def bring_window_to_front(pid):
273         # figure out the window id
274         win_id = None
275         p = subprocess.Popen(['wmctrl', '-l', '-p'], stdout=subprocess.PIPE)
276         for line in p.stdout.readlines():
277             line_split = line.split()
278             cur_win_id = line_split[0]
279             cur_win_pid = int(line_split[2])
280             if cur_win_pid == pid:
281                 win_id = cur_win_id
282
283         # bring to front
284         if win_id:
285             subprocess.call(['wmctrl', '-i', '-a', win_id])