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 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, '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',
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',
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 for key in self.paths['signing_keys']:
178 subprocess.Popen(['/usr/bin/gpg', '--quiet', '--homedir', self.paths['gnupg_homedir'], '--import', key]).wait()
181 def load_mirrors(self):
183 for srcfile in self.paths['mirrors_txt']:
184 if not os.path.exists(srcfile):
186 for mirror in open(srcfile, 'r').readlines():
187 if mirror.strip() not in self.mirrors:
188 self.mirrors.append(mirror.strip())
191 def load_settings(self):
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
203 if os.path.isfile(self.paths['settings_file']):
204 settings = pickle.load(open(self.paths['settings_file']))
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']
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)
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]
227 # make sure the version is current
228 if settings['tbl_version'] != self.tbl_version:
229 settings['tbl_version'] = self.tbl_version
232 self.settings = settings
237 self.settings = default_settings
241 def save_settings(self):
242 pickle.dump(self.settings, open(self.paths['settings_file'], 'w'))
245 # get the process id of a program
247 def get_pid(bin_path, python=False):
250 for p in psutil.process_iter():
252 if p.pid != os.getpid():
255 if len(p.cmdline) > 1:
256 if 'python' in p.cmdline[0]:
259 if len(p.cmdline) > 0:
270 # bring program's x window to front
272 def bring_window_to_front(pid):
273 # figure out the window id
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:
285 subprocess.call(['wmctrl', '-i', '-a', win_id])