4 https://github.com/micahflee/torbrowser-launcher/
6 Copyright (c) 2013 Micah Lee <micahflee@riseup.net>
8 Permission is hereby granted, free of charge, to any person
9 obtaining a copy of this software and associated documentation
10 files (the "Software"), to deal in the Software without
11 restriction, including without limitation the rights to use,
12 copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 OTHER DEALINGS IN THE SOFTWARE.
31 gettext.install('torbrowser-launcher', '/usr/share/torbrowser-launcher/locale')
33 from twisted.internet import gtk2reactor
35 from twisted.internet import reactor
41 import os, sys, subprocess, locale, urllib2, gobject, time, pickle, json, tarfile, psutil
43 from twisted.web.client import Agent, ResponseDone
44 from twisted.web.http_headers import Headers
45 from twisted.internet.protocol import Protocol
46 from twisted.internet.ssl import ClientContextFactory
48 from OpenSSL.SSL import Context, VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT
49 from OpenSSL.crypto import load_certificate, FILETYPE_PEM
51 class VerifyTorProjectCert(ClientContextFactory):
53 def __init__(self, torproject_pem):
54 self.torproject_ca = load_certificate(FILETYPE_PEM, open(torproject_pem, 'r').read())
56 def getContext(self, host, port):
57 ctx = ClientContextFactory.getContext(self)
58 ctx.set_verify_depth(0)
59 ctx.set_verify(VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, self.verifyHostname)
62 def verifyHostname(self, connection, cert, errno, depth, preverifyOK):
63 return cert.digest('sha256') == self.torproject_ca.digest('sha256')
68 print _('Initializing Tor Browser Launcher')
71 self.discover_arch_lang()
73 self.mkdir(self.paths['dir']['download'])
74 self.mkdir(self.paths['dir']['tbb'])
77 # allow buttons to have icons
79 settings = gtk.settings_get_default()
80 settings.props.gtk_button_images = True
84 # discover the architecture and language
85 def discover_arch_lang(self):
86 # figure out the architecture
87 (sysname, nodename, release, version, machine) = os.uname()
88 self.architecture = machine
90 # figure out the language
91 available_languages = ['en-US', 'ar', 'de', 'es-ES', 'fa', 'fr', 'it', 'ko', 'nl', 'pl', 'pt-PT', 'ru', 'vi', 'zh-CN']
92 default_locale = locale.getdefaultlocale()[0]
93 if default_locale == None:
94 self.language = 'en-US'
96 self.language = default_locale.replace('_', '-')
97 if self.language not in available_languages:
98 self.language = self.language.split('-')[0]
99 if self.language not in available_languages:
100 for l in available_languages:
101 if l[0:2] == self.language:
103 # if language isn't available, default to english
104 if self.language not in available_languages:
105 self.language = 'en-US'
107 # build all relevant paths
108 def build_paths(self, tbb_version = None):
109 homedir = os.getenv('HOME')
111 homedir = '/tmp/.torbrowser-'+os.getenv('USER')
112 if os.path.exists(homedir) == False:
114 os.mkdir(homedir, 0700)
116 self.set_gui('error', _("Error creating {0}").format(homedir), [], False)
117 if not os.access(homedir, os.W_OK):
118 self.set_gui('error', _("{0} is not writable").format(homedir), [], False)
120 tbb_data = '%s/.torbrowser' % homedir
123 tarball_filename = 'tor-browser-gnu-linux-'+self.architecture+'-'+tbb_version+'-dev-'+self.language+'.tar.gz'
124 self.paths['file']['tarball'] = tbb_data+'/download/'+tarball_filename
125 self.paths['file']['tarball_sig'] = tbb_data+'/download/'+tarball_filename+'.asc'
126 self.paths['url']['tarball'] = 'https://www.torproject.org/dist/torbrowser/linux/'+tarball_filename
127 self.paths['url']['tarball_sig'] = 'https://www.torproject.org/dist/torbrowser/linux/'+tarball_filename+'.asc'
128 self.paths['filename']['tarball'] = tarball_filename
129 self.paths['filename']['tarball_sig'] = tarball_filename+'.asc'
135 'download': tbb_data+'/download',
136 'tbb': tbb_data+'/tbb/'+self.architecture,
137 'gnupg_homedir': tbb_data+'/gnupg_homedir'
140 'tbl_bin': '/usr/bin/torbrowser-launcher',
141 'settings': tbb_data+'/settings',
142 'version': tbb_data+'/version',
143 'start': tbb_data+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/start-tor-browser',
144 'vidalia_bin': tbb_data+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/App/vidalia',
145 'firefox_bin': tbb_data+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/App/Firefox/firefox',
146 'firefox_profile': tbb_data+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/Data/profile',
147 'update_check': tbb_data+'/download/RecommendedTBBVersions',
148 'icon': '/usr/share/pixmaps/torbrowser80.xpm',
149 'torproject_pem': '/usr/share/torbrowser-launcher/torproject.pem',
150 'erinn_key': '/usr/share/torbrowser-launcher/erinn.asc',
151 'sebastian_key': '/usr/share/torbrowser-launcher/sebastian.asc'
154 'update_check': 'https://check.torproject.org/RecommendedTBBVersions'
160 def mkdir(self, path):
162 if os.path.exists(path) == False:
163 os.makedirs(path, 0700)
166 self.set_gui('error', _("Cannot create directory {0}").format(path), [], False)
168 if not os.access(path, os.W_OK):
169 self.set_gui('error', _("{0} is not writable").format(path), [], False)
173 # if gnupg_homedir isn't set up, set it up
174 def init_gnupg(self):
175 if not os.path.exists(self.paths['dir']['gnupg_homedir']):
176 print _('Creating GnuPG homedir'), self.paths['dir']['gnupg_homedir']
177 if self.mkdir(self.paths['dir']['gnupg_homedir']):
179 print _('Importing keys')
180 p1 = subprocess.Popen(['/usr/bin/gpg', '--homedir', self.paths['dir']['gnupg_homedir'], '--import', self.paths['file']['erinn_key']])
182 p2 = subprocess.Popen(['/usr/bin/gpg', '--homedir', self.paths['dir']['gnupg_homedir'], '--import', self.paths['file']['sebastian_key']])
186 def load_settings(self):
187 if os.path.isfile(self.paths['file']['settings']):
188 self.settings = pickle.load(open(self.paths['file']['settings']))
190 if not 'installed_version' in self.settings:
192 if not 'latest_version' in self.settings:
194 if not 'last_update_check_timestamp' in self.settings:
198 'installed_version': False,
199 'latest_version': '0',
200 'last_update_check_timestamp': 0
206 def save_settings(self):
207 pickle.dump(self.settings, open(self.paths['file']['settings'], 'w'))
210 # get the process id of a program
211 def get_pid(self, bin_path, python = False):
214 for p in psutil.process_iter():
216 if p.pid != os.getpid():
219 if len(p.cmdline) > 1:
220 if 'python' in p.cmdline[0]:
223 if len(p.cmdline) > 0:
234 # bring program's x window to front
235 def bring_window_to_front(self, pid):
236 # figure out the window id
238 p = subprocess.Popen(['wmctrl', '-l', '-p'], stdout=subprocess.PIPE)
239 for line in p.stdout.readlines():
240 line_split = line.split()
241 cur_win_id = line_split[0]
242 cur_win_pid = int(line_split[2])
243 if cur_win_pid == pid:
248 subprocess.call(['wmctrl', '-i', '-a', win_id])
251 def __init__(self, common):
252 print _('Starting settings dialog')
254 self.common.load_settings()
257 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
258 self.window.set_title(_("Tor Browser Launcher Settings"))
259 self.window.set_icon_from_file(self.common.paths['file']['icon'])
260 self.window.set_position(gtk.WIN_POS_CENTER)
261 self.window.set_border_width(10)
262 self.window.connect("delete_event", self.delete_event)
263 self.window.connect("destroy", self.destroy)
265 # build the rest of the UI
266 self.box = gtk.VBox(False, 10)
267 self.window.add(self.box)
270 self.hbox = gtk.HBox(False, 10)
271 self.box.pack_start(self.hbox, True, True, 0)
274 self.settings_box = gtk.VBox(False, 10)
275 self.hbox.pack_start(self.settings_box, True, True, 0)
276 self.settings_box.show()
278 self.labels_box = gtk.VBox(False, 10)
279 self.hbox.pack_start(self.labels_box, True, True, 0)
280 self.labels_box.show()
283 self.pref_ver_box = gtk.HBox(False, 10)
284 self.settings_box.pack_start(self.pref_ver_box, True, True, 0)
285 self.pref_ver_box.show()
287 self.pref_ver_label = gtk.Label(_('I prefer'))
288 self.pref_ver_label.set_line_wrap(True)
289 self.pref_ver_box.pack_start(self.pref_ver_label, True, True, 0)
290 self.pref_ver_label.show()
293 _('Tor Browser Bundle - stable'),
294 _('Tor Browser Bundle - alpha'),
295 _('Obsfproxy Tor Browser Bundle - stable'),
296 _('Obsfproxy Tor Browser Bundle - alpha')
298 self.pref_ver = gtk.combo_box_new_text()
299 for option in options:
300 self.pref_ver.append_text(option)
301 self.pref_ver.set_active(0)
302 self.pref_ver_box.pack_start(self.pref_ver, True, True, 0)
306 self.tor_update_checkbox = gtk.CheckButton(_("Check for and download updates over Tor"))
307 self.settings_box.pack_start(self.tor_update_checkbox, True, True, 0)
308 self.tor_update_checkbox.show()
311 self.update_checkbox = gtk.CheckButton(_("Check for updates next launch"))
312 self.settings_box.pack_start(self.update_checkbox, True, True, 0)
313 self.update_checkbox.show()
316 if(self.common.settings['installed_version']):
317 self.label1 = gtk.Label(_('Installed version:\n{0}').format(self.common.settings['installed_version']))
319 self.label1 = gtk.Label(_('Not installed'))
320 self.label1.set_line_wrap(True)
321 self.labels_box.pack_start(self.label1, True, True, 0)
324 if(self.common.settings['last_update_check_timestamp'] > 0):
325 self.label1 = gtk.Label(_('Last checked for updates:\n{0}').format(time.strftime("%B %d, %Y %I:%M %P", time.gmtime(self.common.settings['last_update_check_timestamp']))))
327 self.label1 = gtk.Label(_('Never checked for updates'))
328 self.label1.set_line_wrap(True)
329 self.labels_box.pack_start(self.label1, True, True, 0)
333 self.button_box = gtk.HButtonBox()
334 self.button_box.set_layout(gtk.BUTTONBOX_SPREAD)
335 self.box.pack_start(self.button_box, True, True, 0)
336 self.button_box.show()
338 # save and launch button
339 save_launch_image = gtk.Image()
340 save_launch_image.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_BUTTON)
341 self.save_launch_button = gtk.Button(_("Launch Tor Browser"))
342 self.save_launch_button.set_image(save_launch_image)
343 self.save_launch_button.connect("clicked", self.save_launch, None)
344 self.button_box.add(self.save_launch_button)
345 self.save_launch_button.show()
347 # save and exit button
348 save_exit_image = gtk.Image()
349 save_exit_image.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_BUTTON)
350 self.save_exit_button = gtk.Button(_("Save & Exit"))
351 self.save_exit_button.set_image(save_exit_image)
352 self.save_exit_button.connect("clicked", self.save_exit, None)
353 self.button_box.add(self.save_exit_button)
354 self.save_exit_button.show()
357 cancel_image = gtk.Image()
358 cancel_image.set_from_stock(gtk.STOCK_CANCEL, gtk.ICON_SIZE_BUTTON)
359 self.cancel_button = gtk.Button(_("Cancel"))
360 self.cancel_button.set_image(cancel_image)
361 self.cancel_button.connect("clicked", self.destroy, None)
362 self.button_box.add(self.cancel_button)
363 self.cancel_button.show()
372 def save_launch(self, widget, data=None):
374 p = subprocess.Popen([self.common.paths['file']['tbl_bin']])
378 def save_exit(self, widget, data=None):
387 def delete_event(self, widget, event, data=None):
389 def destroy(self, widget, data=None):
394 def __init__(self, common):
395 print _('Starting launcher dialog')
398 self.set_gui(None, '', [])
399 self.launch_gui = True
401 # if we haven't already hit an error
402 if self.gui != 'error':
404 if self.common.load_settings():
405 self.common.build_paths(self.common.settings['latest_version'])
407 # is vidalia already running and we just need to open a new firefox?
408 if self.common.settings['installed_version']:
409 vidalia_pid = self.common.get_pid('./App/vidalia')
410 firefox_pid = self.common.get_pid(self.common.paths['file']['firefox_bin'])
412 if vidalia_pid and not firefox_pid:
413 print _('Vidalia is already open, but Firefox is closed. Launching new Firefox.')
414 self.common.bring_window_to_front(vidalia_pid)
415 subprocess.Popen([self.common.paths['file']['firefox_bin'], '-no-remote', '-profile', self.common.paths['file']['firefox_profile']])
417 elif vidalia_pid and firefox_pid:
418 print _('Vidalia and Firefox are already open, bringing them to focus')
420 # bring firefox to front, then vidalia
421 self.common.bring_window_to_front(firefox_pid)
422 self.common.bring_window_to_front(vidalia_pid)
425 # how long was it since the last update check?
426 # 86400 seconds = 24 hours
427 current_timestamp = int(time.time())
428 if current_timestamp - self.common.settings['last_update_check_timestamp'] >= 86400:
430 print 'Checking for update'
431 self.set_gui('task', _("Checking for Tor Browser update."),
432 ['download_update_check',
436 # no need to check for update
437 print _('Checked for update within 24 hours, skipping')
438 self.start_launcher()
441 self.set_gui('error', _("Error loading settings. Delete ~/.torbrowser and try again."), [])
445 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
446 self.window.set_title(_("Tor Browser"))
447 self.window.set_icon_from_file(self.common.paths['file']['icon'])
448 self.window.set_position(gtk.WIN_POS_CENTER)
449 self.window.set_border_width(10)
450 self.window.connect("delete_event", self.delete_event)
451 self.window.connect("destroy", self.destroy)
453 # build the rest of the UI
456 # download or run TBB
457 def start_launcher(self):
458 # is TBB already installed?
459 if os.path.isfile(self.common.paths['file']['start']) and os.access(self.common.paths['file']['start'], os.X_OK):
460 if self.common.settings['installed_version'] == self.common.settings['latest_version']:
461 # current version of tbb is installed, launch it
463 self.launch_gui = False
464 elif self.common.settings['installed_version'] < self.common.settings['latest_version']:
465 # there is a tbb upgrade available
466 self.set_gui('task', _("Your Tor Browser is out of date."),
468 'download_tarball_sig',
473 # for some reason the installed tbb is newer than the current version?
474 self.set_gui('error', _("Something is wrong. The version of Tor Browser Bundle you have installed is newer than the current version?"), [])
478 # are the tarball and sig already downloaded?
479 if os.path.isfile(self.common.paths['file']['tarball']) and os.path.isfile(self.common.paths['file']['tarball_sig']):
480 # start the gui with verify
481 self.set_gui('task', _("Installing Tor Browser."),
488 self.set_gui('task', _("Downloading and installing Tor Browser."),
490 'download_tarball_sig',
495 # there are different GUIs that might appear, this sets which one we want
496 def set_gui(self, gui, message, tasks, autostart=True):
498 self.gui_message = message
499 self.gui_tasks = tasks
501 self.gui_autostart = autostart
503 # set all gtk variables to False
505 if hasattr(self, 'box'):
510 self.progressbar = False
511 self.button_box = False
512 self.start_button = False
513 self.exit_button = False
515 # build the application's UI
517 self.box = gtk.VBox(False, 20)
518 self.window.add(self.box)
520 if self.gui == 'error':
522 self.label = gtk.Label( self.gui_message )
523 self.label.set_line_wrap(True)
524 self.box.pack_start(self.label, True, True, 0)
528 exit_image = gtk.Image()
529 exit_image.set_from_stock(gtk.STOCK_CANCEL, gtk.ICON_SIZE_BUTTON)
530 self.exit_button = gtk.Button("Exit")
531 self.exit_button.set_image(exit_image)
532 self.exit_button.connect("clicked", self.destroy, None)
533 self.box.add(self.exit_button)
534 self.exit_button.show()
536 elif self.gui == 'task':
538 self.label = gtk.Label( self.gui_message )
539 self.label.set_line_wrap(True)
540 self.box.pack_start(self.label, True, True, 0)
544 self.progressbar = gtk.ProgressBar(adjustment=None)
545 self.progressbar.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
546 self.progressbar.set_pulse_step(0.01)
547 self.box.pack_start(self.progressbar, True, True, 0)
550 self.button_box = gtk.HButtonBox()
551 self.button_box.set_layout(gtk.BUTTONBOX_SPREAD)
552 self.box.pack_start(self.button_box, True, True, 0)
553 self.button_box.show()
556 start_image = gtk.Image()
557 start_image.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_BUTTON)
558 self.start_button = gtk.Button(_("Start"))
559 self.start_button.set_image(start_image)
560 self.start_button.connect("clicked", self.start, None)
561 self.button_box.add(self.start_button)
562 if not self.gui_autostart:
563 self.start_button.show()
566 exit_image = gtk.Image()
567 exit_image.set_from_stock(gtk.STOCK_CANCEL, gtk.ICON_SIZE_BUTTON)
568 self.exit_button = gtk.Button(_("Exit"))
569 self.exit_button.set_image(exit_image)
570 self.exit_button.connect("clicked", self.destroy, None)
571 self.button_box.add(self.exit_button)
572 self.exit_button.show()
577 if self.gui_autostart:
580 # start button clicked, begin tasks
581 def start(self, widget, data=None):
582 # disable the start button
583 if self.start_button:
584 self.start_button.set_sensitive(False)
586 # start running tasks
589 # run the next task in the task list
593 if self.gui_task_i >= len(self.gui_tasks):
597 task = self.gui_tasks[self.gui_task_i]
599 # get ready for the next task
602 if task == 'download_update_check':
603 print _('Downloading'), self.common.paths['url']['update_check']
604 self.download('update check', self.common.paths['url']['update_check'], self.common.paths['file']['update_check'])
606 if task == 'attempt_update':
607 print _('Checking to see if update it needed')
608 self.attempt_update()
610 elif task == 'download_tarball':
611 print _('Downloading'), self.common.paths['url']['tarball']
612 self.download('tarball', self.common.paths['url']['tarball'], self.common.paths['file']['tarball'])
614 elif task == 'download_tarball_sig':
615 print _('Downloading'), self.common.paths['url']['tarball_sig']
616 self.download('signature', self.common.paths['url']['tarball_sig'], self.common.paths['file']['tarball_sig'])
618 elif task == 'verify':
619 print _('Verifying signature')
622 elif task == 'extract':
623 print _('Extracting'), self.common.paths['filename']['tarball']
627 print _('Running'), self.common.paths['file']['start']
630 elif task == 'start_over':
631 print _('Starting download over again')
634 def response_received(self, response):
635 class FileDownloader(Protocol):
636 def __init__(self, file, total, progress, done_cb):
640 self.progress = progress
641 self.all_done = done_cb
643 def dataReceived(self, bytes):
644 self.file.write(bytes)
645 self.so_far += len(bytes)
646 percent = float(self.so_far) / float(self.total)
647 self.progress.set_fraction(percent)
648 amount = float(self.so_far)
650 for (size, unit) in [(1024 * 1024, "MiB"), (1024, "KiB")]:
653 amount = amount / float(size)
656 self.progress.set_text(_('Downloaded')+(' %2.1f%% (%2.1f %s)' % ((percent * 100.0), amount, units)))
658 def connectionLost(self, reason):
659 print _('Finished receiving body:'), reason.getErrorMessage()
660 self.all_done(reason)
662 dl = FileDownloader(self.file_download, response.length, self.progressbar, self.response_finished)
663 response.deliverBody(dl)
665 def response_finished(self, msg):
666 if msg.check(ResponseDone):
667 self.file_download.close()
672 print "FINISHED", msg
673 ## FIXME handle errors
675 def download_error(self, f):
676 print _("Download error"), f
677 self.set_gui('error', _("Error starting download:\n\n{0}\n\nAre you connected to the internet?").format(f.value), [], False)
681 def download(self, name, url, path):
682 # initialize the progress bar
683 self.progressbar.set_fraction(0)
684 self.progressbar.set_text(_('Downloading {0}').format(name))
685 self.progressbar.show()
688 agent = Agent(reactor, VerifyTorProjectCert(self.common.paths['file']['torproject_pem']))
689 d = agent.request('GET', url,
690 Headers({'User-Agent': ['torbrowser-launcher']}),
693 self.file_download = open(path, 'w')
694 d.addCallback(self.response_received).addErrback(self.download_error)
696 if not reactor.running:
699 def attempt_update(self):
700 # load the update check file
702 versions = json.load(open(self.common.paths['file']['update_check']))
703 latest_version = None
706 for version in versions:
707 if str(version).find(end) != -1:
708 latest_version = str(version)
711 self.common.settings['latest_version'] = latest_version[:-len(end)]
712 self.common.settings['last_update_check_timestamp'] = int(time.time())
713 self.common.save_settings()
714 self.common.build_paths(self.common.settings['latest_version'])
715 self.start_launcher()
718 # failed to find the latest version
719 self.set_gui('error', _("Error checking for updates."), [], False)
722 # not a valid JSON object
723 self.set_gui('error', _("Error checking for updates."), [], False)
730 # initialize the progress bar
731 self.progressbar.set_fraction(0)
732 self.progressbar.set_text(_('Verifying Signature'))
733 self.progressbar.show()
735 p = subprocess.Popen(['/usr/bin/gpg', '--homedir', self.common.paths['dir']['gnupg_homedir'], '--verify', self.common.paths['file']['tarball_sig']])
736 self.pulse_until_process_exits(p)
738 if p.returncode == 0:
741 self.set_gui('task', _("SIGNATURE VERIFICATION FAILED!\n\nYou might be under attack, or there might just be a networking problem. Click Start try the download again."), ['start_over'], False)
745 if not reactor.running:
749 # initialize the progress bar
750 self.progressbar.set_fraction(0)
751 self.progressbar.set_text(_('Installing'))
752 self.progressbar.show()
755 # make sure this file is a tarfile
756 if tarfile.is_tarfile(self.common.paths['file']['tarball']):
757 tf = tarfile.open(self.common.paths['file']['tarball'])
758 tf.extractall(self.common.paths['dir']['tbb'])
760 self.set_gui('task', _("Tor Browser Launcher doesn't understand the file format of {0}"), ['start_over'], False)
764 # installation is finished, so save installed_version
765 self.common.settings['installed_version'] = self.common.settings['latest_version']
766 self.common.save_settings()
770 def run(self, run_next_task = True):
771 subprocess.Popen([self.common.paths['file']['start']])
775 # make the progress bar pulse until process p (a Popen object) finishes
776 def pulse_until_process_exits(self, p):
777 while p.poll() == None:
779 self.progressbar.pulse()
782 # start over and download TBB again
783 def start_over(self):
784 self.label.set_text(_("Downloading Tor Browser Bundle over again."))
785 self.gui_tasks = ['download_tarball', 'download_tarball_sig', 'verify', 'extract', 'run']
790 def refresh_gtk(self):
791 while gtk.events_pending():
792 gtk.main_iteration(False)
795 def delete_event(self, widget, event, data=None):
797 def destroy(self, widget, data=None):
798 if hasattr(self, 'file_download'):
799 self.file_download.close()
803 if __name__ == "__main__":
804 tor_browser_launcher_version = '0.0.1'
806 print _('Tor Browser Launcher')
807 print _('By Micah Lee, licensed under GPLv3')
808 print _('version {0}').format(tor_browser_launcher_version)
809 print 'https://github.com/micahflee/torbrowser-launcher'
813 # is torbrowser-launcher already running?
814 tbl_pid = common.get_pid(common.paths['file']['tbl_bin'], True)
816 print _('Tor Browser Launcher is already running (pid {0}), bringing to front').format(tbl_pid)
817 common.bring_window_to_front(tbl_pid)
820 if '-settings' in sys.argv:
822 app = TBLSettings(common)
826 app = TBLLauncher(common)