def load(self, supercipher_filename): self.infile = open(supercipher_filename, 'rb') # file must be at least 24 bytes, plus ciphertext if os.stat(supercipher_filename).st_size <= 24: raise InvalidSuperCipherFile # read header data magic_number = self.infile.read(4) version = self.infile.read(3) options = self.infile.read(1) salt = self.infile.read(16) # validate headers if magic_number != self.MAGIC_NUMBER: raise InvalidSuperCipherFile version = self.bytes_to_version(version) if version > self.version: raise FutureFileVersion self.options = options self.salt = salt # build the filename self.ciphertext_filename = os.path.join(self.tmp_dir, 'archive.tar.gz.cast5.blowfish.aes256') if bool(ord(self.options) & self.OPTIONS['pubkey']): self.ciphertext_filename += '.gpg' # write ciphertext file print strings._('scfile_writing_ciphertext') outfile = open(self.ciphertext_filename, 'wb') buf = None while buf != '': buf = self.infile.read(1048576) outfile.write(buf)
def generate(): chunk_size = 102400 # 100kb fp = open(zip_filename, 'rb') done = False while not done: chunk = fp.read(102400) if chunk == '': done = True else: yield chunk # tell GUI the progress downloaded_bytes = fp.tell() percent = round((1.0 * downloaded_bytes / zip_filesize) * 100, 2); sys.stdout.write("\r{0}, {1}% ".format(helpers.human_readable_filesize(downloaded_bytes), percent)) sys.stdout.flush() add_request(REQUEST_PROGRESS, path, { 'id':download_id, 'bytes':downloaded_bytes }) fp.close() sys.stdout.write("\n") # download is finished, close the server if not stay_open: print strings._("closing_automatically") if shutdown_func is None: raise RuntimeError('Not running with the Werkzeug Server') shutdown_func()
def tails_root(): # if running in Tails and as root, do only the things that require root if helpers.get_platform() == 'Tails' and helpers.is_root(): parser = argparse.ArgumentParser() parser.add_argument('port', nargs=1, help=strings._("help_tails_port")) args = parser.parse_args() try: port = int(args.port[0]) except ValueError: sys.stderr.write('{0}\n'.format(strings._("error_tails_invalid_port"))) sys.exit(-1) # open hole in firewall subprocess.call(['/sbin/iptables', '-I', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT']) # start hidden service app = OnionShare() app.choose_port() app.port = port app.start_hidden_service(False, True) sys.stdout.write(app.onion_host) sys.stdout.flush() # close hole in firewall on shutdown import signal def handler(signum = None, frame = None): subprocess.call(['/sbin/iptables', '-D', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT']) sys.exit() for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]: signal.signal(sig, handler) # stay open until killed while True: time.sleep(1)
def unlock(self, output_dir, input_filename, passphrase): # load the supercipher file self.load(input_filename) # derive keys from passphrase, using input file's salt keys = self.stretch_passphrase(passphrase, self.salt) # decrypt files self.decrypt(keys, output_dir) print strings._('decrypt_decrypted_to').format(output_dir)
def get_tmp_dir(): try: while True: random_string = base64.b32encode(Random.new().read(8)).lower().replace('=','') tmp_dir = os.path.join('/tmp', 'supercipher_{0}'.format(random_string)) if not os.path.exists(tmp_dir): os.makedirs(tmp_dir, 0700) return tmp_dir except: print strings._('mkdir_error').format(tmp_dir) return False
def encrypt(filenames, output_filename, pubkey=None, passphrase=None): print strings._('encrypt_encrypting_files').format(filenames) if not passphrase: passphrase = helpers.get_passphrase(True) scf = SuperCipherFile() try: scf.lock(output_filename, filenames, passphrase, pubkey) except KeyboardInterrupt: print strings._('cleanup')
def start_hidden_service(self, gui=False, tails_root=False): if not self.port: self.choose_port() if helpers.get_platform() == 'Tails' and not tails_root: # in Tails, start the hidden service in a root process if gui: args = ['/usr/bin/gksudo', '-D', 'OnionShare', '--', '/usr/bin/onionshare'] else: args = ['/usr/bin/sudo', '--', '/usr/bin/onionshare'] p = subprocess.Popen(args+[str(self.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdout = p.stdout.read(22) # .onion URLs are 22 chars long if stdout: self.onion_host = stdout else: if p.poll() == -1: raise TailsError(o.stderr.read()) else: raise TailsError(strings._("error_tails_unknown_root")) else: if self.local_only: self.onion_host = '127.0.0.1:{0}'.format(self.port) else: # come up with a hidden service directory name hidserv_dir = '{0}/onionshare_{1}'.format(helpers.get_tmp_dir(), helpers.random_string(8)) self.cleanup_filenames.append(hidserv_dir) # connect to the tor controlport controller = False tor_control_ports = [9051, 9151] for tor_control_port in tor_control_ports: try: controller = Controller.from_port(port=tor_control_port) break except SocketError: pass if not controller: raise NoTor(strings._("cant_connect_ctrlport").format(tor_control_ports)) controller.authenticate() # set up hidden service controller.set_options([ ('HiddenServiceDir', hidserv_dir), ('HiddenServicePort', '80 127.0.0.1:{0}'.format(self.port)) ]) # figure out the .onion hostname hostname_file = '{0}/hostname'.format(hidserv_dir) self.onion_host = open(hostname_file, 'r').read().strip()
def mistake(client, message): if player.is_currently_playing(): message.reply_text( _("song_1").format( "<a href=\"{}\">{}</a>".format( player.currently_playing["url"], player.currently_playing["title"]), "<a href=\"tg://user?id{}\">{}</a>".format( player.currently_playing["sent_by_id"], player.currently_playing["sent_by_name"]), player.currently_playing["dur"])) else: message.reply_text(_("song_2"))
def f10(): first_10 = player.q_list[:10] res = (_("listing") + "\n\n").format(len(first_10), len(player.q_list)) if first_10: for i in range(len(first_10)): item = first_10[i] res += _("list_item").format( i + 1, "<a href=\"{}\">{}</a> ({})".format( item["url"], item["title"], item["dur"]), "<a href=\"tg://user?id={}\">{}</a>".format( item["sent_by_id"], item["sent_by_name"])) + "\n" return res
def get_passphrase(ask_twice=False): if ask_twice: valid_passphrase = False while not valid_passphrase: passphrase = getpass.getpass(strings._('get_passphrase')) passphrase2 = getpass.getpass(strings._('get_passphrase2')) if passphrase == passphrase2: valid_passphrase = True else: print strings._('passphrase_mismatch') else: passphrase = getpass.getpass(strings._('get_passphrase')) return passphrase
def mistake(client, message): if message.from_user.id in BANNED_USERS: message.reply_text(_("ban_9")) return if player.is_currently_playing(): message.reply_text( _("song_1").format( '<a href="{}">{}</a>'.format( player.currently_playing["url"], player.currently_playing["title"]), player.currently_playing["duration"], )) else: message.reply_text(_("song_2"))
def playlist(client, query): cp = player.currently_playing if query.data.startswith("add_to"): if db.add_to_playlist( cp["title"], cp["url"], ): query.message.edit_reply_markup( InlineKeyboardMarkup([ [ InlineKeyboardButton(_("playlist_6"), "rm_from_playlist"), InlineKeyboardButton(_("play_4"), "close"), ], ])) query.answer(_("playlist_4")) else: query.answer(_("playlist_5")) elif query.data.startswith("rm_from"): if db.remove_from_playlist(cp["url"]): query.message.edit_reply_markup( InlineKeyboardMarkup([ [ InlineKeyboardButton(_("playlist_3"), "add_to_playlist"), InlineKeyboardButton(_("play_4"), "close"), ], ])) query.answer(_("playlist_7")) else: query.answer(_("playlist_8"))
async def callback(client, query): if query.from_user.id not in SUDO_USERS: await query.answer() return current_volume = int(query.message.text.split()[-1].replace("%", "")) if query.data == "decrease_volume": volume = current_volume - 1 if volume < 0: volume = 0 volume = f"{volume}%" await asyncio.create_subprocess_exec( ["pactl", "set-sink-volume", "MySink", volume]).wait() await query.message.reply_text( _("current_volume").format(volume), reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("➖", callback_data="decrease_volume"), InlineKeyboardButton("➕", callback_data="increase_volume"), ]]), quote=False, ) await query.message.delete() await query.answer() elif query.data == "increase_volume": volume = current_volume + 1 if volume > 100: volume = 100 volume = f"{volume}%" await asyncio.create_subprocess_exec( ["pactl", "set-sink-volume", "MySink", volume]).wait() await query.message.reply_text( _("current_volume").format(volume), reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("➖", callback_data="decrease_volume"), InlineKeyboardButton("➕", callback_data="increase_volume"), ]]), quote=False, ) await query.message.delete() await query.answer()
def mistake(client, message): if player.is_currently_playing(): message.reply_text( _("song_1").format( '<a href="{}">{}</a>'.format( player.currently_playing["url"], player.currently_playing["title"]), player.currently_playing["duration"], '<a href="tg://user?id{}">{}</a>'.format( player.currently_playing["sent_by_id"], player.currently_playing["sent_by_name"], ), )) else: message.reply_text(_("song_2"))
def tts(client, message): if message.text.replace("/tts", "") == "": message.reply_text(_("tts_1")) else: try: gTTS(message.text.replace("/tts ", ""), lang="en-US").save("downloads/tts.mp3") m = message.reply_text(_("tts_2")) _thread.start_new_thread( subprocess.Popen(["mplayer", "downloads/tts.mp3"]).wait, () ) m.edit(_("tts_3")) except: message.reply_text(_("error"))
def seekb(client, message): if message.from_user.id in BANNED_USERS: message.reply_text(_("ban_9")) return if message.from_user.id not in SUDO_FILTER: message.reply_text(_("n4u")) return if player.mpv.filename or player.mpv.pause: try: player.mpv.seek(-int(message.command[1])) message.reply_text(_("seek_1")) except: message.reply_text(_("seek_2")) else: message.reply_text(_("seek_3"))
def generate(): chunk_size = 102400 # 100kb fp = open(zip_filename, 'rb') done = False canceled = False while not done: chunk = fp.read(chunk_size) if chunk == '': done = True else: try: yield chunk # tell GUI the progress downloaded_bytes = fp.tell() percent = (1.0 * downloaded_bytes / zip_filesize) * 100 # suppress stdout platform on OSX (#203) if helpers.get_platform() != 'Darwin': sys.stdout.write("\r{0:s}, {1:.2f}% ".format( helpers.human_readable_filesize(downloaded_bytes), percent)) sys.stdout.flush() add_request(REQUEST_PROGRESS, path, { 'id': download_id, 'bytes': downloaded_bytes }) except: # looks like the download was canceled done = True canceled = True # tell the GUI the download has canceled add_request(REQUEST_CANCELED, path, {'id': download_id}) fp.close() if helpers.get_platform() != 'Darwin': sys.stdout.write("\n") # download is finished, close the server if not stay_open and not canceled: print strings._("closing_automatically") if shutdown_func is None: raise RuntimeError('Not running with the Werkzeug Server') shutdown_func()
def tails_root(): # if running in Tails and as root, do only the things that require root if helpers.get_platform() == 'Tails' and helpers.is_root(): parser = argparse.ArgumentParser() parser.add_argument('port', nargs=1, help=strings._("help_tails_port")) args = parser.parse_args() try: port = int(args.port[0]) except ValueError: sys.stderr.write('{0}\n'.format( strings._("error_tails_invalid_port"))) sys.exit(-1) # open hole in firewall subprocess.call([ '/sbin/iptables', '-I', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT' ]) # start hidden service app = OnionShare() app.choose_port() app.port = port app.start_hidden_service(False, True) sys.stdout.write(app.onion_host) sys.stdout.flush() # close hole in firewall on shutdown import signal def handler(signum=None, frame=None): subprocess.call([ '/sbin/iptables', '-D', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT' ]) sys.exit() for sig in [ signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT ]: signal.signal(sig, handler) # stay open until killed while True: time.sleep(1)
async def resume(client, message): if player.STATE == State.Paused: player.STATE = State.Playing player.pause_resume() m = await message.reply_text(_("resumed")) else: m = await message.reply_text(_("cant_resume")) if m and message.chat.type != "private": await sleep(5) await m.delete() try: await message.delete() except: pass
def stretch_passphrase(self, passphrase, salt): keys = {} sys.stdout.write(strings._('deriving_keys')) sys.stdout.flush() # start with the passphrase key = passphrase # derive a key for each cipher for cipher in common.ciphers: sys.stdout.write(' {0}'.format(cipher)) sys.stdout.flush() # rounds of pbkdf2 and scrypt key = str(PBKDF2.crypt(key, salt.encode('hex'), 100000)) key = scrypt.hash(key, salt, N=2**14, r=8, p=1) if cipher == 'aes256': # AES256 needs 256-bit (32-byte) key keys[cipher] = key[:32] elif cipher == 'blowfish': # Blowfish keys very from 32-448 bits, but we'll use 256-bit (32-byte) key keys[cipher] = key[:32] elif cipher == 'cast5': # CAST5 needs 128-bit (16-byte) key keys[cipher] = key[:16] sys.stdout.write('\n') return keys
def resume(client, message): if player.STATE == State.Paused: player.STATE = State.Playing player.pause_resume() m = message.reply_text(_("pause_4")) else: m = message.reply_text(_("pause_5")) if m and message.chat.type != "private": sleep(5) m.delete() try: message.delete() except: pass
def __init__(self, transparent_torification=False): self.transparent_torification = transparent_torification # files and dirs to delete on shutdown self.cleanup_filenames = [] self.service_id = None # connect to the tor controlport self.c = None ports = [9151, 9153, 9051] for port in ports: try: self.c = Controller.from_port(port=port) self.c.authenticate() break except: pass if not self.c: raise NoTor(strings._("cant_connect_ctrlport").format(ports)) # do the versions of stem and tor that I'm using support ephemeral hidden services? tor_version = self.c.get_version().version_str list_ephemeral_hidden_services = getattr( self.c, "list_ephemeral_hidden_services", None) self.supports_ephemeral = callable( list_ephemeral_hidden_services) and tor_version >= '0.2.7.1'
def on_browse(self): initial_dir = self.dir_edit.text().strip() dir_name = QFileDialog.getExistingDirectory(self, _('select_dir_title'), initial_dir) if dir_name: self.dir_edit.setText(dir_name)
async def skip(client, message): if player.STATE in (State.Playing, State.Streaming, State.Paused): player.STATE = State.Skipped player.abort() m = await message.reply_text(_("skip_1")) else: m = await message.reply_text(_("skip_2")) if m and message.chat.type != "private": await sleep(5) await m.delete() try: await message.delete() except: pass
def volume(client, message): if len(message.text.split()) == 2 and message.from_user.id in SUDO_USERS: try: volume = int(message.text.split()[1]) if volume in range(1, 101): volume = f"{volume}%" subprocess.Popen( [ "pactl", "set-sink-volume", "MySink", volume ] ).wait() message.reply_text( _("volume_2").format(volume) ) return except: pass current_volume = "".join(re.search(r"Volume\:(.+)\n", subprocess.check_output( ["pactl", "list", "sinks"]).decode()).group(0).split()).split("/")[1] if message.from_user.id in SUDO_USERS: message.reply_text( _("volume_1").format(current_volume), reply_markup=InlineKeyboardMarkup( [ [ InlineKeyboardButton( "➖", callback_data="decrease_volume" ), InlineKeyboardButton( "➕", callback_data="increase_volume" ) ] ] ), quote=True ) else: message.reply_text( _("volume_1").format(current_volume), )
def clear_downloads(client, message): if message.from_user.id in BANNED_USERS: message.reply_text(_("ban_9")) return if message.from_user.id not in SUDO_FILTER: message.reply_text(_("n4u")) return try: for file in os.listdir("downloads"): try: os.remove("downloads/" + file) except: pass message.reply_text(_("cleardownloads")) except: message.reply_text(_("error"))
def process_dir(self, dir_name): print('process dir:', dir_name) for sub_dir in enum_directory(dir_name, dirs=True): self.process_dir(sub_dir) self.delete_files(dir_name) if is_empty_dir(dir_name): self.dir_found += 1 try: delete_dir(dir_name) self.delete_success += 1 self.notify.emit(True, _('msg_delete_dir_success').format(dir_name)) except Exception as e: self.delete_failed += 1 self.notify.emit( False, _('msg_delete_dir_failed').format(dir_name, e))
def stream(client, message): if player.STATE in (State.Playing, State.Paused): message.reply_text(_("stream_3")) else: args = message.text.split() if len(args) == 1: message.reply_text(_("stream_1")) elif len(args) != 2: message.reply_text(_("stream_2")) else: player.stream( args[1], func(client.send_message, LOG_GROUP, _("group_2").format(args[1])) if LOG_GROUP else None) message.reply_text(_("stream_4"))
def wait_for_hs(self, onion_host): """ This function is only required when using non-ephemeral hidden services. After creating a hidden service, continually attempt to connect to it until it successfully connects.. """ # legacy only, this function is no longer required with ephemeral hidden services print strings._('wait_for_hs') ready = False while not ready: try: sys.stdout.write('{0:s} '.format( strings._('wait_for_hs_trying'))) sys.stdout.flush() if self.transparent_torification: # no need to set the socks5 proxy urllib2.urlopen('http://{0:s}'.format(onion_host)) else: tor_exists = False ports = [9150, 9152, 9050] for port in ports: try: s = socks.socksocket() s.setproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', port) s.connect((onion_host, 80)) s.close() tor_exists = True break except socks.ProxyConnectionError: pass if not tor_exists: raise NoTor( strings._("cant_connect_socksport").format( tor_socks_ports)) ready = True sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_yup'))) except socks.SOCKS5Error: sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_nope'))) sys.stdout.flush() except urllib2.HTTPError: # torification error sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_nope'))) sys.stdout.flush() except httplib.BadStatusLine: # torification (with bridge) error sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_nope'))) sys.stdout.flush() except KeyboardInterrupt: return False return True
def removeuser(client, message): if message.from_user.id not in OWNER: message.reply_text(_("n4u")) else: if len(message.text.split(" ")) != 2: message.reply_text(_("admin_6")) return try: remadmin = int(message.text.split()[1]) if remadmin not in SUDO_FILTER: message.reply_text(_("admin_4")) return if remadmin in SUDO_FILTER: SUDO_FILTER.remove(remadmin) message.reply_text(_("admin_2")) except: message.reply_text(_("admin_6"))
def unbanuser(client, message): if message.from_user.id not in SUDO_FILTER: message.reply_text(_("n4u")) return if len(message.text.split(" ")) != 2: message.reply_text(_("admin_6")) return try: newunban = int(message.text.split()[1]) if newunban not in BANNED_USERS: message.reply_text(_("ban_3")) return if newunban in BANNED_USERS: BANNED_USERS.remove(newunban) message.reply_text(_("ban_4")) except: message.reply_text(_("admin_6"))
def __init__(self): super(MainWindow, self).__init__() self.setWindowIcon(QIcon(":/images/app.png")) self.setWindowTitle(_('app_title')) self.setMinimumSize(800, 600) self.setFont(QFont('MS Shell Dlg 2', 10)) self.setupUi()
async def pause(client, message): if player.STATE in State.Playing: player.STATE = State.Paused player.pause_resume() m = await message.reply_text(_("pause_1")) elif player.STATE == State.Paused: m = await message.reply_text(_("pause_2")) else: m = await message.reply_text(_("pause_3")) if m and message.chat.type != "private": await sleep(5) await m.delete() try: await message.delete() except: pass
def delete_files(self, dir_name): for file_path in enum_directory(dir_name, files=True): if self.is_deletable(file_path): try: delete_file(file_path) except Exception as e: self.notify.emit( False, _('msg_delete_file_failed').format(file_path, e))
def wait_for_hs(self): if self.local_only: return True print strings._('wait_for_hs') ready = False while not ready: try: sys.stdout.write('{0:s} '.format( strings._('wait_for_hs_trying'))) sys.stdout.flush() if helpers.get_platform() == 'Tails': # in Tails everything is proxies over Tor already # so no need to set the socks5 proxy urllib2.urlopen('http://{0:s}'.format(self.onion_host)) else: tor_exists = False tor_socks_ports = [9050, 9150] for tor_socks_port in tor_socks_ports: try: s = socks.socksocket() s.setproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', tor_socks_port) s.connect((self.onion_host, 80)) s.close() tor_exists = True break except socks.ProxyConnectionError: pass if not tor_exists: raise NoTor( strings._("cant_connect_socksport").format( tor_socks_ports)) ready = True sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_yup'))) except socks.SOCKS5Error: # non-Tails error sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_nope'))) sys.stdout.flush() except urllib2.HTTPError: # Tails error sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_nope'))) sys.stdout.flush() except httplib.BadStatusLine: # Tails (with bridge) error sys.stdout.write('{0:s}\n'.format( strings._('wait_for_hs_nope'))) sys.stdout.flush() except KeyboardInterrupt: return False return True
def generate(): chunk_size = 102400 # 100kb fp = open(zip_filename, 'rb') done = False canceled = False while not done: chunk = fp.read(chunk_size) if chunk == '': done = True else: try: yield chunk # tell GUI the progress downloaded_bytes = fp.tell() percent = (1.0 * downloaded_bytes / zip_filesize) * 100 # suppress stdout platform on OSX (#203) if helpers.get_platform() != 'Darwin': sys.stdout.write( "\r{0:s}, {1:.2f}% ".format(helpers.human_readable_filesize(downloaded_bytes), percent)) sys.stdout.flush() add_request(REQUEST_PROGRESS, path, {'id': download_id, 'bytes': downloaded_bytes}) except: # looks like the download was canceled done = True canceled = True # tell the GUI the download has canceled add_request(REQUEST_CANCELED, path, {'id': download_id}) fp.close() if helpers.get_platform() != 'Darwin': sys.stdout.write("\n") # download is finished, close the server if not stay_open and not canceled: print strings._("closing_automatically") if shutdown_func is None: raise RuntimeError('Not running with the Werkzeug Server') shutdown_func()
def callback(client, query): current_volume = int(query.message.text.split()[-1].replace("%", "")) if query.data == "decrease_volume": volume = current_volume - 1 if volume < 0: volume = 0 volume = f"{volume}%" subprocess.Popen(["pactl", "set-sink-volume", "MySink", volume]).wait() query.message.reply_text( _("volume_1").format(volume), reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("➖", callback_data="decrease_volume"), InlineKeyboardButton("➕", callback_data="increase_volume"), ]]), quote=False, ) query.message.delete() query.answer() elif query.data == "increase_volume": volume = current_volume + 1 if volume > 100: volume = 100 volume = f"{volume}%" subprocess.Popen(["pactl", "set-sink-volume", "MySink", volume]).wait() query.message.reply_text( _("volume_1").format(volume), reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton("➖", callback_data="decrease_volume"), InlineKeyboardButton("➕", callback_data="increase_volume"), ]]), quote=False, ) query.message.delete() query.answer()
def setupUi(self): file_label = QLabel(_('file')) self.file_edit = QLineEdit() self.file_edit.setReadOnly(True) self.browse_btn = QPushButton('...') method_label = QLabel(_('method')) self.method_md5 = QCheckBox('MD5') self.method_md5.setChecked(True) self.method_sha1 = QCheckBox('SHA1') self.method_sha1.setChecked(True) self.method_sha256 = QCheckBox('SHA256') self.method_sha256.setChecked(True) self.check_btn = QPushButton(_('check')) file_box = QHBoxLayout() file_box.addWidget(self.file_edit, 1) file_box.addWidget(self.browse_btn) result_label = QLabel(_('result')) self.result_edit = QTextBrowser() self.result_edit.setReadOnly(True) self.result_edit.setFixedHeight(300) method_box = QHBoxLayout() method_box.addWidget(self.method_md5) method_box.addWidget(self.method_sha1) method_box.addWidget(self.method_sha256) method_box.addStretch() method_box.addWidget(self.check_btn) form = QFormLayout() form.addRow(file_label, file_box) form.addRow(method_label, method_box) form.addRow(result_label, self.result_edit) vbox = QVBoxLayout() vbox.addLayout(form) vbox.addStretch() self.setLayout(vbox) self.browse_btn.clicked.connect(self.on_browse) self.check_btn.clicked.connect(self.on_check)
def on_check(self): try: file_path = self.file_edit.text().strip() if not os.path.exists(file_path): raise ValueError(_('msg_file_not_exist').format(file_path)) methods = [] if self.method_md5.isChecked(): methods.append('md5') if self.method_sha1.isChecked(): methods.append('sha1') if self.method_sha256.isChecked(): methods.append('sha256') self.on_thread_running(True) self.result_edit.setText(_('msg_checking_file_hash')) thread = FileHashThread(file_path, methods, self) thread.finished.connect(self.on_thread_finished) thread.start() except Exception as e: self.setColoredText(self.result_edit, str(e), False)
def search(client, query): answers = [] string = query.query.lower().strip().rstrip() if string == "": client.answer_inline_query( query.id, results=answers, switch_pm_text=_("inline_1"), switch_pm_parameter="help", cache_time=0 ) return else: videosSearch = VideosSearch(string.lower(), limit=50) for v in videosSearch.result()["result"]: answers.append( InlineQueryResultArticle( title=v["title"], description=_("inline_2").format( v["duration"], v["viewCount"]["short"] ), input_message_content=InputTextMessageContent( "https://www.youtube.com/watch?v={}".format( v["id"] ) ), thumb_url=v["thumbnails"][0]["url"] ) ) try: query.answer( results=answers, cache_time=0 ) except errors.QueryIdInvalid: query.answer( results=answers, cache_time=0, switch_pm_text=_("inline_3"), switch_pm_parameter="", )
def decrypt(self, keys, output_dir): if not self.infile: raise DecryptBeforeLoading # if there's a pubkey wrapper, decrypt that first if bool(ord(self.options) & self.OPTIONS['pubkey']): print strings._('scfile_decrypting_pubkey') common.gpg.pubkey_decrypt(self.ciphertext_filename) # delete the .gpg file os.remove(self.ciphertext_filename) self.ciphertext_filename = self.ciphertext_filename.rstrip('.gpg') # reverse the order of ciphers list reversed_ciphers = common.ciphers[:] reversed_ciphers.reverse() # decrypt all the layers of symmetric encryption ciphertext = open(self.ciphertext_filename, 'r').read() for cipher in reversed_ciphers: try: print strings._('scfile_decrypting_symmetric').format(cipher) if cipher == 'aes256': bs = AES.block_size eiv = ciphertext[:bs] ciphertext = ciphertext[bs:] cipher = AES.new(keys[cipher], AES.MODE_CFB, eiv) if cipher == 'blowfish': bs = Blowfish.block_size eiv = ciphertext[:bs] ciphertext = ciphertext[bs:] cipher = Blowfish.new(keys[cipher], Blowfish.MODE_CBC, eiv) if cipher == 'cast5': bs = CAST.block_size eiv = ciphertext[:bs+2] ciphertext = ciphertext[bs+2:] cipher = CAST.new(keys[cipher], CAST.MODE_OPENPGP, eiv) plaintext = cipher.decrypt(ciphertext) except ValueError: raise InvalidDecryptionPassphrase ciphertext = plaintext # delete the .cast5.blowfish.aes256 file os.unlink(self.ciphertext_filename) # write archive to disk archive_filename = self.ciphertext_filename.rstrip('.cast5.blowfish.aes256') open(archive_filename, 'w').write(plaintext) # extract print strings._('scfile_extracting') if not tarfile.is_tarfile(archive_filename): raise InvalidArchive tar = tarfile.open(archive_filename, 'r:gz') names = tar.getnames() tar.extractall(output_dir)
def wait_for_hs(self, onion_host): """ This function is only required when using non-ephemeral hidden services. After creating a hidden service, continually attempt to connect to it until it successfully connects.. """ # legacy only, this function is no longer required with ephemeral hidden services print strings._('wait_for_hs') ready = False while not ready: try: sys.stdout.write('{0:s} '.format(strings._('wait_for_hs_trying'))) sys.stdout.flush() if self.transparent_torification: # no need to set the socks5 proxy urllib2.urlopen('http://{0:s}'.format(onion_host)) else: tor_exists = False ports = [9150, 9152, 9050] for port in ports: try: s = socks.socksocket() s.setproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', port) s.connect((onion_host, 80)) s.close() tor_exists = True break except socks.ProxyConnectionError: pass if not tor_exists: raise NoTor(strings._("cant_connect_socksport").format(tor_socks_ports)) ready = True sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_yup'))) except socks.SOCKS5Error: sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() except urllib2.HTTPError: # torification error sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() except httplib.BadStatusLine: # torification (with bridge) error sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() except KeyboardInterrupt: return False return True
def compress(filenames, archive_filename): print strings._('compressing') def reset(tarinfo): strip_dir = False absfilename = '/{0}'.format(tarinfo.name) for filename in filenames: if os.path.isdir(filename) and absfilename.startswith(filename): strip_dir = True tarinfo.name = tarinfo.name[len(os.path.dirname(filename)):] if not strip_dir: tarinfo.name = os.path.basename(tarinfo.name) #print strings._('adding').format(tarinfo.name) tarinfo.uid = tarinfo.gid = 0 tarinfo.uname = tarinfo.gname = "root" return tarinfo with tarfile.open(archive_filename, 'w:gz') as tar: for filename in filenames: tar.add(filename, recursive=True, filter=reset)
def start(self, port): """ Start a hidden service on port 80, pointing to the given port, and return the onion hostname. """ print strings._("connecting_ctrlport").format(int(port)) if self.supports_ephemeral: print strings._('using_ephemeral') res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication = False) self.service_id = res.content()[0][2].split('=')[1] onion_host = res.content()[0][2].split('=')[1] + '.onion' return onion_host else: # come up with a hidden service directory name if helpers.get_platform() == 'Windows': self.hidserv_dir = tempfile.mkdtemp() self.hidserv_dir = self.hidserv_dir.replace('\\', '/') else: path = '/tmp/onionshare' try: if not os.path.exists(path): os.makedirs(path, 0700) except: raise HSDirError(strings._("error_hs_dir_cannot_create").format(path)) if not os.access(path, os.W_OK): raise HSDirError(strings._("error_hs_dir_not_writable").format(path)) self.hidserv_dir = tempfile.mkdtemp(dir=path) self.cleanup_filenames.append(self.hidserv_dir) # set up hidden service hsdic = self.c.get_conf_map('HiddenServiceOptions') or { 'HiddenServiceDir': [], 'HiddenServicePort': [] } if self.hidserv_dir in hsdic.get('HiddenServiceDir', []): # Maybe a stale service with the wrong local port dropme = hsdic['HiddenServiceDir'].index(self.hidserv_dir) del hsdic['HiddenServiceDir'][dropme] del hsdic['HiddenServicePort'][dropme] hsdic['HiddenServiceDir'] = hsdic.get('HiddenServiceDir', [])+[self.hidserv_dir] hsdic['HiddenServicePort'] = hsdic.get('HiddenServicePort', [])+[ '80 127.0.0.1:{0:d}'.format(port)] self.c.set_options(self._hsdic2list(hsdic)) # figure out the .onion hostname hostname_file = '{0:s}/hostname'.format(self.hidserv_dir) onion_host = open(hostname_file, 'r').read().strip() return onion_host
def wait_for_hs(self): if self.local_only: return True print strings._('wait_for_hs') ready = False while not ready: try: sys.stdout.write('{0} '.format(strings._('wait_for_hs_trying'))) sys.stdout.flush() if helpers.get_platform() == 'Tails': # in Tails everything is proxies over Tor already # so no need to set the socks5 proxy urllib2.urlopen('http://{0}'.format(self.onion_host)) else: tor_exists = False tor_socks_ports = [9050, 9150] for tor_socks_port in tor_socks_ports: try: s = socks.socksocket() s.setproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', tor_socks_port) s.connect((self.onion_host, 80)) s.close() tor_exists = True break except socks.ProxyConnectionError: pass if not tor_exists: raise NoTor(strings._("cant_connect_socksport").format(tor_socks_ports)) ready = True sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_yup'))) except socks.SOCKS5Error: # non-Tails error sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() except urllib2.HTTPError: # Tails error sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() except KeyboardInterrupt: return False return True
def __init__(self, transparent_torification=False): self.transparent_torification = transparent_torification # files and dirs to delete on shutdown self.cleanup_filenames = [] self.service_id = None # connect to the tor controlport self.c = None ports = [9151, 9153, 9051] for port in ports: try: self.c = Controller.from_port(port=port) self.c.authenticate() break except: pass if not self.c: raise NoTor(strings._("cant_connect_ctrlport").format(ports)) # do the versions of stem and tor that I'm using support ephemeral hidden services? tor_version = self.c.get_version().version_str list_ephemeral_hidden_services = getattr(self.c, "list_ephemeral_hidden_services", None) self.supports_ephemeral = callable(list_ephemeral_hidden_services) and tor_version >= '0.2.7.1'
def start(self, port): """ Start a hidden service on port 80, pointing to the given port, and return the onion hostname. """ print strings._("connecting_ctrlport").format(int(port)) if self.supports_ephemeral: print strings._("using_ephemeral") res = self.c.create_ephemeral_hidden_service({80: port}, await_publication=True) self.service_id = res.content()[0][2].split("=")[1] onion_host = res.content()[0][2].split("=")[1] + ".onion" return onion_host else: # come up with a hidden service directory name if helpers.get_platform() == "Windows": path = "{0:s}/onionshare".format(os.environ["Temp"].replace("\\", "/")) else: path = "/tmp/onionshare" try: if not os.path.exists(path): os.makedirs(path, 0700) except: raise HSDirError(strings._("error_hs_dir_cannot_create").format(path)) if not os.access(path, os.W_OK): raise HSDirError(strings._("error_hs_dir_not_writable").format(path)) self.hidserv_dir = tempfile.mkdtemp(dir=path) self.cleanup_filenames.append(self.hidserv_dir) # set up hidden service hs_conf = self.c.get_hidden_service_conf() if self.hidserv_dir in hs_conf: del hs_conf[self.hidserv_dir] hs_conf[self.hidserv_dir] = {"HiddenServicePort": [(80, "127.0.0.1", port)]} self.c.set_hidden_service_conf(hs_conf) # figure out the .onion hostname hostname_file = "{0:s}/hostname".format(self.hidserv_dir) onion_host = open(hostname_file, "r").read().strip() return onion_host
def decrypt(filename, output_dir, passphrase=None): print strings._('decrypt_decrypting_file').format(filename) if not passphrase: passphrase = helpers.get_passphrase() scf = SuperCipherFile() try: scf.unlock(output_dir, filename, passphrase) except InvalidSuperCipherFile: print strings._('decrypt_error_invalid_file').format(filename) except FutureFileVersion: print strings._('decrypt_error_future_version') except InvalidArchive: print strings._('decrypt_error_invalid_archive') except MissingSeckey: print strings._('decrypt_error_missing_seckey') except InvalidDecryptionPassphrase: print strings._('decrypt_error_invalid_passphrase') except KeyboardInterrupt: print strings._('cleanup')
def lock(self, output_filename, filenames, passphrase, pubkey=None): timer = helpers.Timer() # random salt salt = Random.new().read(16) # compress files into archive timer.start() archive_filename = os.path.join(self.tmp_dir, 'archive.tar.gz') helpers.compress(filenames, archive_filename) print strings._('time_compression').format(timer.stop()) # derive keys from passphrase timer.start() keys = self.stretch_passphrase(passphrase, salt) print strings._('time_stretching').format(timer.stop()) # encrypt with symmetric ciphers timer.start() sys.stdout.write(strings._('encrypt_encrypting_cipher')) sys.stdout.flush() plaintext = open(archive_filename, 'r').read() os.unlink(archive_filename) for cipher in common.ciphers: sys.stdout.write(' {0}'.format(cipher)) sys.stdout.flush() if cipher == 'aes256': # https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.AES-module.html iv = Random.new().read(AES.block_size) cipher = AES.new(keys[cipher], AES.MODE_CFB, iv) ciphertext = iv + cipher.encrypt(plaintext) elif cipher == 'blowfish': # https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.Blowfish-module.html bs = Blowfish.block_size iv = Random.new().read(bs) cipher = Blowfish.new(keys[cipher], Blowfish.MODE_CBC, iv) plen = bs - divmod(len(plaintext),bs)[1] padding = [plen]*plen padding = pack('b'*plen, *padding) ciphertext = iv + cipher.encrypt(plaintext + padding) elif cipher == 'cast5': # https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.CAST-module.html iv = Random.new().read(CAST.block_size) cipher = CAST.new(keys[cipher], CAST.MODE_OPENPGP, iv) ciphertext = cipher.encrypt(plaintext) # today's plaintext is yesterday's ciphertext plaintext = ciphertext sys.stdout.write('\n') # save the new super-enciphered ciphertext current_filename = '{0}.cast5.blowfish.aes256'.format(archive_filename) open(current_filename, 'w').write(plaintext) # encrypt with pubkey if pubkey: common.gpg.pubkey_encrypt(current_filename, pubkey) os.remove(current_filename) current_filename += '.gpg' # write the output file self.save(salt, current_filename, output_filename, bool(pubkey)) print strings._('time_encryption').format(timer.stop()) print strings._('encrypt_encrypted_to').format(output_filename)
def main(): strings.load_strings(common.supercipher_dir) # parse arguments parser = argparse.ArgumentParser() parser.add_argument('-e', '--encrypt', metavar='filename', nargs='+', help=strings._('arg_help_encrypt')) parser.add_argument('-d', '--decrypt', metavar='filename', dest='decrypt', help=strings._('arg_help_decrypt')) parser.add_argument('-p', '--pubkey', metavar='public_key', dest='pubkey', help=strings._('arg_help_pubkey')) parser.add_argument('-o', '--output', metavar='filename', dest='output', required=True, help=strings._('arg_help_output')) args = parser.parse_args() encrypt_filenames = args.encrypt decrypt_filename = args.decrypt pubkey = args.pubkey output_filename = args.output # convert filenames to absolute paths if encrypt_filenames: for i in range(len(encrypt_filenames)): encrypt_filenames[i] = os.path.abspath(encrypt_filenames[i]) if decrypt_filename: decrypt_filename = os.path.abspath(decrypt_filename) output_filename = os.path.abspath(output_filename) # validation if not encrypt_filenames and not decrypt_filename: parser.print_help() print '' print strings._('validation_choose_one') sys.exit(0) if encrypt_filenames and decrypt_filename: print strings._('validation_dont_choose_two') sys.exit(0) if encrypt_filenames: action = 'encrypt' else: action = 'decrypt' # encrypt validation if action == 'encrypt': # make sure encrypt_filenames is a list of valid files/folders valid = True for filename in encrypt_filenames: if not os.path.exists(filename): print strings._('validation_doesnt_exist').format(filename) valid = False if not valid: print strings._('validation_invalid_file') sys.exit(0) # if pubkey is passed, make sure the fingerprint is valid if pubkey: try: common.gpg.valid_pubkey(pubkey) except InvalidPubkeyLength: print strings._('validation_pubkey_length') sys.exit(0) except InvalidPubkeyNotHex: print strings._('validation_pubkey_not_hex') sys.exit(0) except MissingPubkey: print strings._('validation_missing_pubkey') sys.exit(0) # make sure output_filename doesn't already exist if os.path.exists(output_filename): print strings._('validation_output_exists').format(output_filename) sys.exit(0) elif action == 'decrypt': # make sure decrypt_filename is a valid file if not os.path.isfile(decrypt_filename): print strings._('validation_not_file').format(decrypt_filename) sys.exit(0) # make sure output_filename either doesn't exist or is a writable folder if os.path.exists(output_filename): if os.path.isdir(output_filename): if not os.access(output_filename, os.W_OK): print strings._('validation_not_writable').format(output_filename) sys.exit(0) else: print strings._('validation_already_exists').format(output_filename) sys.exit(0) # execute the action if action == 'encrypt': encrypt(encrypt_filenames, output_filename, pubkey) else: decrypt(decrypt_filename, output_filename)
def start_hidden_service(self, gui=False, tails_root=False): if not self.port: self.choose_port() if helpers.get_platform() == 'Tails' and not tails_root: # in Tails, start the hidden service in a root process if gui: args = ['/usr/bin/gksudo', '-D', 'OnionShare', '--', '/usr/bin/onionshare'] else: args = ['/usr/bin/sudo', '--', '/usr/bin/onionshare'] print "Executing: {0}".format(args+[str(self.port)]) p = subprocess.Popen(args+[str(self.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdout = p.stdout.read(22) # .onion URLs are 22 chars long if stdout: self.onion_host = stdout print 'Got onion_host: {0}'.format(self.onion_host) else: if p.poll() == -1: raise TailsError(o.stderr.read()) else: raise TailsError(strings._("error_tails_unknown_root")) else: if self.local_only: self.onion_host = '127.0.0.1:{0}'.format(self.port) else: # come up with a hidden service directory name if helpers.get_platform() == 'Tails': # need to create HS directory in /var/lib/tor because of AppArmor rules included in Tails self.hidserv_dir = tempfile.mkdtemp(dir='/var/lib/tor') # change owner to debian-tor import pwd import grp uid = pwd.getpwnam("debian-tor").pw_uid gid = grp.getgrnam("debian-tor").gr_gid os.chown(self.hidserv_dir, uid, gid) else: self.hidserv_dir = tempfile.mkdtemp() self.cleanup_filenames.append(self.hidserv_dir) # connect to the tor controlport self.controller = None tor_control_ports = [9051, 9151] for tor_control_port in tor_control_ports: try: self.controller = Controller.from_port(port=tor_control_port) break except SocketError: pass if not self.controller: raise NoTor(strings._("cant_connect_ctrlport").format(tor_control_ports)) self.controller.authenticate() # set up hidden service if helpers.get_platform() == 'Windows': self.hidserv_dir = self.hidserv_dir.replace('\\', '/') hsdic = self.controller.get_conf_map('HiddenServiceOptions') or { 'HiddenServiceDir': [], 'HiddenServicePort': [] } if self.hidserv_dir in hsdic.get('HiddenServiceDir', []): # Maybe a stale service with the wrong local port dropme = hsdic['HiddenServiceDir'].index(self.hidserv_dir) del hsdic['HiddenServiceDir'][dropme] del hsdic['HiddenServicePort'][dropme] hsdic['HiddenServiceDir'] = hsdic.get('HiddenServiceDir', [])+[self.hidserv_dir] hsdic['HiddenServicePort'] = hsdic.get('HiddenServicePort', [])+[ '80 127.0.0.1:{0}'.format(self.port)] self.controller.set_options(hsdic2list(hsdic)) # figure out the .onion hostname hostname_file = '{0}/hostname'.format(self.hidserv_dir) self.onion_host = open(hostname_file, 'r').read().strip()
def main(): strings.load_strings() tails_root() # parse arguments parser = argparse.ArgumentParser() parser.add_argument('--local-only', action='store_true', dest='local_only', help=strings._("help_local_only")) parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=strings._("help_stay_open")) parser.add_argument('--debug', action='store_true', dest='debug', help=strings._("help_debug")) parser.add_argument('filename', metavar='filename', nargs='+', help=strings._('help_filename')) args = parser.parse_args() filenames = args.filename for i in range(len(filenames)): filenames[i] = os.path.abspath(filenames[i]) local_only = bool(args.local_only) debug = bool(args.debug) stay_open = bool(args.stay_open) # validation valid = True for filename in filenames: if not os.path.exists(filename): print(strings._("not_a_file").format(filename)) valid = False if not valid: sys.exit() # start the onionshare app try: app = OnionShare(debug, local_only, stay_open) app.choose_port() print strings._("connecting_ctrlport").format(app.port) app.start_hidden_service() except NoTor as e: sys.exit(e.args[0]) except TailsError as e: sys.exit(e.args[0]) # prepare files to share print strings._("preparing_files") web.set_file_info(filenames) app.cleanup_filenames.append(web.zip_filename) # start onionshare service in new thread t = threading.Thread(target=web.start, args=(app.port, app.stay_open)) t.daemon = True t.start() # wait for hs ready = app.wait_for_hs() if not ready: sys.exit() print strings._("give_this_url") print 'http://{0}/{1}'.format(app.onion_host, web.slug) print '' print strings._("ctrlc_to_stop") # wait for app to close running = True while running: try: time.sleep(0.5) except KeyboardInterrupt: running = False web.stop() # shutdown app.cleanup()
def main(cwd=None): """ The main() function implements all of the logic that the command-line version of onionshare uses. """ strings.load_strings() # onionshare CLI in OSX needs to change current working directory (#132) if helpers.get_platform() == 'Darwin': if cwd: os.chdir(cwd) # parse arguments parser = argparse.ArgumentParser() parser.add_argument('--local-only', action='store_true', dest='local_only', help=strings._("help_local_only")) parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=strings._("help_stay_open")) parser.add_argument('--transparent', action='store_true', dest='transparent_torification', help=strings._("help_transparent_torification")) parser.add_argument('--debug', action='store_true', dest='debug', help=strings._("help_debug")) parser.add_argument('filename', metavar='filename', nargs='+', help=strings._('help_filename')) args = parser.parse_args() filenames = args.filename for i in range(len(filenames)): filenames[i] = os.path.abspath(filenames[i]) local_only = bool(args.local_only) debug = bool(args.debug) stay_open = bool(args.stay_open) transparent_torification = bool(args.transparent_torification) # validation valid = True for filename in filenames: if not os.path.exists(filename): print(strings._("not_a_file").format(filename)) valid = False if not valid: sys.exit() # start the onionshare app try: app = OnionShare(debug, local_only, stay_open, transparent_torification) app.choose_port() app.start_hidden_service() except hs.NoTor as e: sys.exit(e.args[0]) except hs.HSDirError as e: sys.exit(e.args[0]) # prepare files to share print strings._("preparing_files") web.set_file_info(filenames) app.cleanup_filenames.append(web.zip_filename) # warn about sending large files over Tor if web.zip_filesize >= 157286400: # 150mb print '' print strings._("large_filesize") print '' # start onionshare service in new thread t = threading.Thread(target=web.start, args=(app.port, app.stay_open, app.transparent_torification)) t.daemon = True t.start() try: # Trap Ctrl-C # wait for hs, only if using old version of tor if not app.hs.supports_ephemeral: if not app.local_only: ready = app.hs.wait_for_hs(app.onion_host) if not ready: sys.exit() print strings._("give_this_url") print 'http://{0:s}/{1:s}'.format(app.onion_host, web.slug) print '' print strings._("ctrlc_to_stop") # wait for app to close while t.is_alive(): # t.join() can't catch KeyboardInterrupt in such as Ubuntu t.join(0.5) except KeyboardInterrupt: web.stop(app.port) finally: # shutdown app.cleanup()