]> git.lizzy.rs Git - torbrowser-launcher.git/blob - torbrowser_launcher/common.py
fixes versions path for TBB 4.x that was forcing TBL to try to always upgrade (#139)
[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         print _('Initializing Tor Browser Launcher')
47         self.tbl_version = tbl_version
48
49         # initialize the app
50         self.default_mirror = 'https://www.torproject.org/dist/'
51         self.discover_arch_lang()
52         self.build_paths()
53         for d in self.paths['dirs']:
54             self.mkdir(self.paths['dirs'][d])
55         self.load_mirrors()
56         self.load_settings()
57         self.mkdir(self.paths['download_dir'])
58         self.mkdir(self.paths['tbb']['dir'])
59         self.init_gnupg()
60
61         # allow buttons to have icons
62         try:
63             gtk_settings = gtk.settings_get_default()
64             gtk_settings.props.gtk_button_images = True
65         except:
66             pass
67
68     # discover the architecture and language
69     def discover_arch_lang(self):
70         # figure out the architecture
71         self.architecture = 'x86_64' if '64' in platform.architecture()[0] else 'i686'
72
73         # figure out the language
74         available_languages = ['en-US', 'ar', 'de', 'es-ES', 'fa', 'fr', 'it', 'ko', 'nl', 'pl', 'pt-PT', 'ru', 'vi', 'zh-CN']
75         default_locale = locale.getdefaultlocale()[0]
76         if default_locale is None:
77             self.language = 'en-US'
78         else:
79             self.language = default_locale.replace('_', '-')
80             if self.language not in available_languages:
81                 self.language = self.language.split('-')[0]
82                 if self.language not in available_languages:
83                     for l in available_languages:
84                         if l[0:2] == self.language:
85                             self.language = l
86             # if language isn't available, default to english
87             if self.language not in available_languages:
88                 self.language = 'en-US'
89
90     # build all relevant paths
91     def build_paths(self, tbb_version=None):
92         homedir = os.getenv('HOME')
93         if not homedir:
94             homedir = '/tmp/.torbrowser-'+os.getenv('USER')
95             if not os.path.exists(homedir):
96                 try:
97                     os.mkdir(homedir, 0700)
98                 except:
99                     self.set_gui('error', _("Error creating {0}").format(homedir), [], False)
100         if not os.access(homedir, os.W_OK):
101             self.set_gui('error', _("{0} is not writable").format(homedir), [], False)
102
103         tbb_config = '{0}/.config/torbrowser'.format(homedir)
104         tbb_cache = '{0}/.cache/torbrowser'.format(homedir)
105         tbb_local = '{0}/.local/share/torbrowser'.format(homedir)
106         old_tbb_data = '{0}/.torbrowser'.format(homedir)
107
108         if tbb_version:
109             # tarball filename
110             if self.architecture == 'x86_64':
111                 arch = 'linux64'
112             else:
113                 arch = 'linux32'
114             tarball_filename = 'tor-browser-'+arch+'-'+tbb_version+'_'+self.language+'.tar.xz'
115
116             # tarball
117             self.paths['tarball_url'] = '{0}torbrowser/'+tbb_version+'/'+tarball_filename
118             self.paths['tarball_file'] = tbb_cache+'/download/'+tarball_filename
119             self.paths['tarball_filename'] = tarball_filename
120
121             # sig
122             self.paths['sha256_file'] = tbb_cache+'/download/sha256sums.txt'
123             self.paths['sha256_sig_file'] = tbb_cache+'/download/sha256sums.txt.asc'
124             self.paths['sha256_url'] = '{0}torbrowser/'+tbb_version+'/sha256sums.txt'
125             self.paths['sha256_sig_url'] = '{0}torbrowser/'+tbb_version+'/sha256sums.txt.asc'
126         else:
127             self.paths = {
128                 'dirs': {
129                     'config': tbb_config,
130                     'cache': tbb_cache,
131                     'local': tbb_local,
132                 },
133                 'old_data_dir': old_tbb_data,
134                 'tbl_bin': sys.argv[0],
135                 'icon_file': os.path.join(os.path.dirname(SHARE), 'pixmaps/torbrowser80.xpm'),
136                 'torproject_pem': os.path.join(SHARE, 'torproject.pem'),
137                 'erinn_key': os.path.join(SHARE, 'erinn.asc'),
138                 'mirrors_txt': [os.path.join(SHARE, 'mirrors.txt'),
139                                 tbb_config+'/mirrors.txt'],
140                 'modem_sound': os.path.join(SHARE, 'modem.ogg'),
141                 'download_dir': tbb_cache+'/download',
142                 'gnupg_homedir': tbb_local+'/gnupg_homedir',
143                 'settings_file': 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                     'start': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/start-tor-browser',
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         print _('Importing keys')
178         subprocess.Popen(['/usr/bin/gpg', '--homedir', self.paths['gnupg_homedir'], '--import', self.paths['erinn_key']]).wait()
179
180     # load mirrors
181     def load_mirrors(self):
182         self.mirrors = []
183         for srcfile in self.paths['mirrors_txt']:
184             if os.path.exists(srcfile):
185                 print "Successfully loaded mirrors from %s" % srcfile
186             elif not os.path.exists(srcfile):
187                 print "Warning: can't load mirrors from %s" % srcfile
188                 continue
189             for mirror in open(srcfile, 'r').readlines():
190                 if mirror.strip() not in self.mirrors:
191                     self.mirrors.append(mirror.strip())
192
193     # load settings
194     def load_settings(self):
195         default_settings = {
196             'tbl_version': self.tbl_version,
197             'installed_version': False,
198             'latest_version': '0',
199             'update_over_tor': True,
200             'check_for_updates': False,
201             'modem_sound': False,
202             'last_update_check_timestamp': 0,
203             'mirror': self.default_mirror
204         }
205
206         if os.path.isfile(self.paths['settings_file']):
207             settings = pickle.load(open(self.paths['settings_file']))
208             resave = False
209
210             # settings migrations
211             if settings['tbl_version'] <= '0.1.0':
212                 print '0.1.0 migration'
213                 settings['installed_version'] = settings['installed_version']['stable']
214                 settings['latest_version'] = settings['latest_version']['stable']
215                 resave = True
216
217                 # make new tbb folder
218                 self.mkdir(self.paths['tbb']['dir'])
219                 old_tbb_dir = self.paths['old_data_dir']+'/tbb/stable/'+self.architecture+'/tor-browser_'+self.language
220                 new_tbb_dir = self.paths['tbb']['dir']+'/tor-browser_'+self.language
221                 if os.path.isdir(old_tbb_dir):
222                     os.rename(old_tbb_dir, new_tbb_dir)
223
224             # make sure settings file is up-to-date
225             for setting in default_settings:
226                 if setting not in settings:
227                     settings[setting] = default_settings[setting]
228                     resave = True
229
230             # make sure the version is current
231             if settings['tbl_version'] != self.tbl_version:
232                 settings['tbl_version'] = self.tbl_version
233                 resave = True
234
235             self.settings = settings
236             if resave:
237                 self.save_settings()
238
239         else:
240             self.settings = default_settings
241             self.save_settings()
242
243     # save settings
244     def save_settings(self):
245         pickle.dump(self.settings, open(self.paths['settings_file'], 'w'))
246         return True
247
248     # get the process id of a program
249     @staticmethod
250     def get_pid(bin_path, python=False):
251         pid = None
252
253         for p in psutil.process_iter():
254             try:
255                 if p.pid != os.getpid():
256                     exe = None
257                     if python:
258                         if len(p.cmdline) > 1:
259                             if 'python' in p.cmdline[0]:
260                                 exe = p.cmdline[1]
261                     else:
262                         if len(p.cmdline) > 0:
263                             exe = p.cmdline[0]
264
265                     if exe == bin_path:
266                         pid = p.pid
267
268             except:
269                 pass
270
271         return pid
272
273     # bring program's x window to front
274     @staticmethod
275     def bring_window_to_front(pid):
276         # figure out the window id
277         win_id = None
278         p = subprocess.Popen(['wmctrl', '-l', '-p'], stdout=subprocess.PIPE)
279         for line in p.stdout.readlines():
280             line_split = line.split()
281             cur_win_id = line_split[0]
282             cur_win_pid = int(line_split[2])
283             if cur_win_pid == pid:
284                 win_id = cur_win_id
285
286         # bring to front
287         if win_id:
288             subprocess.call(['wmctrl', '-i', '-a', win_id])