3 https://github.com/micahflee/torbrowser-launcher/
5 Copyright (c) 2013-2014 Micah Lee <micah@micahflee.com>
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
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
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.
29 import os, sys, platform, subprocess, locale, pickle, psutil
35 SHARE = os.getenv('TBL_SHARE', sys.prefix+'/share/torbrowser-launcher')
38 gettext.install('torbrowser-launcher', os.path.join(SHARE, 'locale'))
40 from twisted.internet import gtk2reactor
45 def __init__(self, tbl_version):
46 print _('Initializing Tor Browser Launcher')
47 self.tbl_version = tbl_version
50 self.default_mirror = 'https://www.torproject.org/dist/'
51 self.discover_arch_lang()
53 for d in self.paths['dirs']:
54 self.mkdir(self.paths['dirs'][d])
57 self.mkdir(self.paths['download_dir'])
58 self.mkdir(self.paths['tbb']['dir'])
61 # allow buttons to have icons
63 gtk_settings = gtk.settings_get_default()
64 gtk_settings.props.gtk_button_images = True
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'
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'
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:
86 # if language isn't available, default to english
87 if self.language not in available_languages:
88 self.language = 'en-US'
90 # build all relevant paths
91 def build_paths(self, tbb_version=None):
92 homedir = os.getenv('HOME')
94 homedir = '/tmp/.torbrowser-'+os.getenv('USER')
95 if not os.path.exists(homedir):
97 os.mkdir(homedir, 0700)
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)
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)
110 if self.architecture == 'x86_64':
114 tarball_filename = 'tor-browser-'+arch+'-'+tbb_version+'_'+self.language+'.tar.xz'
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
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'
129 'config': tbb_config,
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',
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',
157 if not os.path.exists(path):
158 os.makedirs(path, 0700)
161 print _("Cannot create directory {0}").format(path)
163 if not os.access(path, os.W_OK):
164 print _("{0} is not writable").format(path)
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'])
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()
181 def load_mirrors(self):
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
189 for mirror in open(srcfile, 'r').readlines():
190 if mirror.strip() not in self.mirrors:
191 self.mirrors.append(mirror.strip())
194 def load_settings(self):
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
206 if os.path.isfile(self.paths['settings_file']):
207 settings = pickle.load(open(self.paths['settings_file']))
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']
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)
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]
230 # make sure the version is current
231 if settings['tbl_version'] != self.tbl_version:
232 settings['tbl_version'] = self.tbl_version
235 self.settings = settings
240 self.settings = default_settings
244 def save_settings(self):
245 pickle.dump(self.settings, open(self.paths['settings_file'], 'w'))
248 # get the process id of a program
250 def get_pid(bin_path, python=False):
253 for p in psutil.process_iter():
255 if p.pid != os.getpid():
258 if len(p.cmdline) > 1:
259 if 'python' in p.cmdline[0]:
262 if len(p.cmdline) > 0:
273 # bring program's x window to front
275 def bring_window_to_front(pid):
276 # figure out the window id
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:
288 subprocess.call(['wmctrl', '-i', '-a', win_id])