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, json, 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 self.tbl_version = tbl_version
49 self.default_mirror = 'https://dist.torproject.org/'
50 self.discover_arch_lang()
52 for d in self.paths['dirs']:
53 self.mkdir(self.paths['dirs'][d])
56 self.mkdir(self.paths['download_dir'])
57 self.mkdir(self.paths['tbb']['dir'])
60 # allow buttons to have icons
62 gtk_settings = gtk.settings_get_default()
63 gtk_settings.props.gtk_button_images = True
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'
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'
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:
85 # if language isn't available, default to english
86 if self.language not in available_languages:
87 self.language = 'en-US'
89 # build all relevant paths
90 def build_paths(self, tbb_version=None):
91 homedir = os.getenv('HOME')
93 homedir = '/tmp/.torbrowser-'+os.getenv('USER')
94 if not os.path.exists(homedir):
96 os.mkdir(homedir, 0700)
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)
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)
109 if self.architecture == 'x86_64':
113 tarball_filename = 'tor-browser-'+arch+'-'+tbb_version+'_'+self.language+'.tar.xz'
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
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'
128 'config': tbb_config,
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',
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',
158 if not os.path.exists(path):
159 os.makedirs(path, 0700)
162 print _("Cannot create directory {0}").format(path)
164 if not os.access(path, os.W_OK):
165 print _("{0} is not writable").format(path)
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'])
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()
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 = json.load(open(self.paths['settings_file']))
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]
214 # make sure the version is current
215 if settings['tbl_version'] != self.tbl_version:
216 settings['tbl_version'] = self.tbl_version
219 self.settings = settings
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']))
227 os.remove(self.paths['settings_file_pickle'])
231 self.settings = default_settings
235 def save_settings(self):
236 json.dump(self.settings, open(self.paths['settings_file'], 'w'))
239 # get the process id of a program
241 def get_pid(bin_path, python=False):
244 for p in psutil.process_iter():
246 if p.pid != os.getpid():
249 if len(p.cmdline) > 1:
250 if 'python' in p.cmdline[0]:
253 if len(p.cmdline) > 0:
264 # bring program's x window to front
266 def bring_window_to_front(pid):
267 # figure out the window id
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:
279 subprocess.call(['wmctrl', '-i', '-a', win_id])