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://dist.torproject.org/'
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 'signing_keys': [os.path.join(SHARE, 'erinn.asc'), os.path.join(SHARE, 'tor-browser-developers.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 for key in self.paths['signing_keys']:
179 subprocess.Popen(['/usr/bin/gpg', '--homedir', self.paths['gnupg_homedir'], '--import', key]).wait()
182 def load_mirrors(self):
184 for srcfile in self.paths['mirrors_txt']:
185 if not os.path.exists(srcfile):
187 for mirror in open(srcfile, 'r').readlines():
188 if mirror.strip() not in self.mirrors:
189 self.mirrors.append(mirror.strip())
192 def load_settings(self):
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
204 if os.path.isfile(self.paths['settings_file']):
205 settings = pickle.load(open(self.paths['settings_file']))
208 # settings migrations
209 if settings['tbl_version'] <= '0.1.0':
210 print '0.1.0 migration'
211 settings['installed_version'] = settings['installed_version']['stable']
212 settings['latest_version'] = settings['latest_version']['stable']
215 # make new tbb folder
216 self.mkdir(self.paths['tbb']['dir'])
217 old_tbb_dir = self.paths['old_data_dir']+'/tbb/stable/'+self.architecture+'/tor-browser_'+self.language
218 new_tbb_dir = self.paths['tbb']['dir']+'/tor-browser_'+self.language
219 if os.path.isdir(old_tbb_dir):
220 os.rename(old_tbb_dir, new_tbb_dir)
222 # make sure settings file is up-to-date
223 for setting in default_settings:
224 if setting not in settings:
225 settings[setting] = default_settings[setting]
228 # make sure the version is current
229 if settings['tbl_version'] != self.tbl_version:
230 settings['tbl_version'] = self.tbl_version
233 self.settings = settings
238 self.settings = default_settings
242 def save_settings(self):
243 pickle.dump(self.settings, open(self.paths['settings_file'], 'w'))
246 # get the process id of a program
248 def get_pid(bin_path, python=False):
251 for p in psutil.process_iter():
253 if p.pid != os.getpid():
256 if len(p.cmdline) > 1:
257 if 'python' in p.cmdline[0]:
260 if len(p.cmdline) > 0:
271 # bring program's x window to front
273 def bring_window_to_front(pid):
274 # figure out the window id
276 p = subprocess.Popen(['wmctrl', '-l', '-p'], stdout=subprocess.PIPE)
277 for line in p.stdout.readlines():
278 line_split = line.split()
279 cur_win_id = line_split[0]
280 cur_win_pid = int(line_split[2])
281 if cur_win_pid == pid:
286 subprocess.call(['wmctrl', '-i', '-a', win_id])