mirror of
https://github.com/onionshare/onionshare.git
synced 2025-01-24 13:41:14 -05:00
Porting onionshare from python2 to python3 (#261). This commit only ports the CLI version, not the GUI. Has not been tested in Fedora, Windows, or OSX. Removed hack to make unicode filenames work because hack does not work in python3. Replaced constant_time_compare function with a new one that works in python3. Tweaked hidden service checking code because urllib is different in python3.
This commit is contained in:
parent
b2bda8294a
commit
170811f450
4
BUILD.md
4
BUILD.md
@ -11,10 +11,10 @@ cd onionshare
|
||||
|
||||
*For .deb-based distros (like Debian, Ubuntu, Linux Mint):*
|
||||
|
||||
Note that python-stem appears in Debian wheezy and newer, and it appears in Ubuntu 13.10 and newer. Older versions of Debian and Ubuntu aren't supported.
|
||||
Note that python3-stem appears in Debian wheezy and newer, and it appears in Ubuntu 13.10 and newer. Older versions of Debian and Ubuntu aren't supported.
|
||||
|
||||
```sh
|
||||
sudo apt-get install -y build-essential fakeroot python-all python-stdeb python-flask python-stem python-qt4 dh-python
|
||||
sudo apt-get install -y build-essential fakeroot python3-all python3-stdeb python3-flask python3-stem python3-pyqt5 dh-python
|
||||
./install/build_deb.sh
|
||||
sudo dpkg -i deb_dist/onionshare_*.deb
|
||||
```
|
||||
|
@ -9,7 +9,7 @@ VERSION=`cat version`
|
||||
rm -r deb_dist >/dev/null 2>&1
|
||||
|
||||
# build binary package
|
||||
python setup.py --command-packages=stdeb.command bdist_deb
|
||||
python3 setup.py --command-packages=stdeb.command bdist_deb
|
||||
|
||||
# return install instructions if onionshare builds properly
|
||||
echo ""
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
OnionShare | https://onionshare.org/
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
OnionShare | https://onionshare.org/
|
||||
|
@ -17,4 +17,4 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from onionshare import *
|
||||
from .onionshare import *
|
||||
|
@ -17,15 +17,7 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
import os, inspect, hashlib, base64, hmac, platform, zipfile, tempfile
|
||||
from itertools import izip
|
||||
import math
|
||||
import time
|
||||
|
||||
# hack to make unicode filenames work (#141)
|
||||
import sys
|
||||
reload(sys)
|
||||
sys.setdefaultencoding("utf-8")
|
||||
import sys, os, inspect, hashlib, base64, hmac, platform, zipfile, tempfile, math, time
|
||||
|
||||
|
||||
def get_platform():
|
||||
@ -82,20 +74,20 @@ def get_version():
|
||||
|
||||
def constant_time_compare(val1, val2):
|
||||
"""
|
||||
Compares two values in constant time.
|
||||
"""
|
||||
_builtin_constant_time_compare = getattr(hmac, 'compare_digest', None)
|
||||
if _builtin_constant_time_compare is not None:
|
||||
return _builtin_constant_time_compare(val1, val2)
|
||||
Returns True if the two strings are equal, False otherwise.
|
||||
|
||||
len_eq = len(val1) == len(val2)
|
||||
if len_eq:
|
||||
result = 0
|
||||
left = val1
|
||||
else:
|
||||
result = 1
|
||||
left = val2
|
||||
for x, y in izip(bytearray(left), bytearray(val2)):
|
||||
The time taken is independent of the number of characters that match.
|
||||
|
||||
For the sake of simplicity, this function executes in constant time only
|
||||
when the two strings have the same length. It short-circuits when they
|
||||
have different lengths.
|
||||
|
||||
From: http://www.levigross.com/2014/02/07/constant-time-comparison-functions-in...-python-haskell-clojure-and-java/
|
||||
"""
|
||||
if len(val1) != len(val2):
|
||||
return False
|
||||
result = 0
|
||||
for x, y in zip(val1, val2):
|
||||
result |= x ^ y
|
||||
return result == 0
|
||||
|
||||
@ -106,7 +98,7 @@ def random_string(num_bytes, output_len=None):
|
||||
"""
|
||||
b = os.urandom(num_bytes)
|
||||
h = hashlib.sha256(b).digest()[:16]
|
||||
s = base64.b32encode(h).lower().replace('=', '')
|
||||
s = base64.b32encode(h).lower().replace(b'=', b'').decode('utf-8')
|
||||
if not output_len:
|
||||
return s
|
||||
return s[:output_len]
|
||||
|
@ -19,10 +19,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from stem.control import Controller
|
||||
import os, sys, tempfile, shutil, urllib2, httplib
|
||||
import socks
|
||||
from stem import SocketError
|
||||
import os, sys, tempfile, shutil, urllib
|
||||
|
||||
import helpers, strings
|
||||
from . import socks
|
||||
from . import helpers, strings
|
||||
|
||||
class NoTor(Exception):
|
||||
"""
|
||||
@ -62,17 +63,19 @@ class HS(object):
|
||||
self.service_id = None
|
||||
|
||||
# connect to the tor controlport
|
||||
found_tor = False
|
||||
self.c = None
|
||||
ports = [9151, 9153, 9051]
|
||||
for port in ports:
|
||||
try:
|
||||
self.c = Controller.from_port(port=port)
|
||||
self.c.authenticate()
|
||||
found_tor = True
|
||||
break
|
||||
except:
|
||||
except SocketError:
|
||||
pass
|
||||
if not self.c:
|
||||
raise NoTor(strings._("cant_connect_ctrlport").format(ports))
|
||||
if not found_tor:
|
||||
raise NoTor(strings._("cant_connect_ctrlport").format(str(ports)))
|
||||
|
||||
# do the versions of stem and tor that I'm using support ephemeral hidden services?
|
||||
tor_version = self.c.get_version().version_str
|
||||
@ -84,9 +87,9 @@ class HS(object):
|
||||
Start a hidden service on port 80, pointing to the given port, and
|
||||
return the onion hostname.
|
||||
"""
|
||||
print strings._("connecting_ctrlport").format(int(port))
|
||||
print(strings._("connecting_ctrlport").format(int(port)))
|
||||
if self.supports_ephemeral:
|
||||
print strings._('using_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'
|
||||
@ -102,7 +105,7 @@ class HS(object):
|
||||
path = '/tmp/onionshare'
|
||||
try:
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path, 0700)
|
||||
os.makedirs(path, 0o700)
|
||||
except:
|
||||
raise HSDirError(strings._("error_hs_dir_cannot_create").format(path))
|
||||
if not os.access(path, os.W_OK):
|
||||
@ -139,7 +142,7 @@ class HS(object):
|
||||
successfully connects..
|
||||
"""
|
||||
# legacy only, this function is no longer required with ephemeral hidden services
|
||||
print strings._('wait_for_hs')
|
||||
print(strings._('wait_for_hs'))
|
||||
|
||||
ready = False
|
||||
while not ready:
|
||||
@ -149,7 +152,7 @@ class HS(object):
|
||||
|
||||
if self.transparent_torification:
|
||||
# no need to set the socks5 proxy
|
||||
urllib2.urlopen('http://{0:s}'.format(onion_host))
|
||||
urllib.request.urlopen('http://{0:s}'.format(onion_host))
|
||||
else:
|
||||
tor_exists = False
|
||||
ports = [9150, 9152, 9050]
|
||||
@ -164,17 +167,17 @@ class HS(object):
|
||||
except socks.ProxyConnectionError:
|
||||
pass
|
||||
if not tor_exists:
|
||||
raise NoTor(strings._("cant_connect_socksport").format(tor_socks_ports))
|
||||
raise NoTor(strings._("cant_connect_socksport").format(str(ports)))
|
||||
ready = True
|
||||
|
||||
sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_yup')))
|
||||
except socks.GeneralProxyError:
|
||||
sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope')))
|
||||
sys.stdout.flush()
|
||||
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
|
||||
except urllib.error.HTTPError: # torification error
|
||||
sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope')))
|
||||
sys.stdout.flush()
|
||||
except KeyboardInterrupt:
|
||||
|
@ -19,7 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os, sys, subprocess, time, argparse, inspect, shutil, socket, threading
|
||||
import strings, helpers, web, hs
|
||||
|
||||
from . import strings, helpers, web, hs
|
||||
|
||||
class OnionShare(object):
|
||||
"""
|
||||
@ -97,7 +98,7 @@ def main(cwd=None):
|
||||
onionshare uses.
|
||||
"""
|
||||
strings.load_strings()
|
||||
print strings._('version_string').format(helpers.get_version())
|
||||
print(strings._('version_string').format(helpers.get_version()))
|
||||
|
||||
# onionshare CLI in OSX needs to change current working directory (#132)
|
||||
if helpers.get_platform() == 'Darwin':
|
||||
@ -142,15 +143,15 @@ def main(cwd=None):
|
||||
sys.exit(e.args[0])
|
||||
|
||||
# prepare files to share
|
||||
print strings._("preparing_files")
|
||||
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 ''
|
||||
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))
|
||||
@ -164,10 +165,10 @@ def main(cwd=None):
|
||||
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")
|
||||
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():
|
||||
|
@ -18,7 +18,8 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
import json, locale, sys, os
|
||||
import helpers
|
||||
|
||||
from . import helpers
|
||||
|
||||
strings = {}
|
||||
|
||||
@ -62,9 +63,6 @@ def translated(k, gui=False):
|
||||
"""
|
||||
Returns a translated string.
|
||||
"""
|
||||
if gui:
|
||||
return strings[k].encode("utf-8").decode('utf-8', 'replace')
|
||||
else:
|
||||
return strings[k].encode("utf-8")
|
||||
return strings[k]
|
||||
|
||||
_ = translated
|
||||
|
@ -17,10 +17,12 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
import Queue, mimetypes, platform, os, sys, urllib2
|
||||
import queue, mimetypes, platform, os, sys
|
||||
from urllib.request import urlopen
|
||||
from flask import Flask, Response, request, render_template_string, abort
|
||||
from functools import wraps
|
||||
import strings, helpers
|
||||
|
||||
from . import strings, helpers
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@ -72,7 +74,7 @@ REQUEST_DOWNLOAD = 1
|
||||
REQUEST_PROGRESS = 2
|
||||
REQUEST_OTHER = 3
|
||||
REQUEST_CANCELED = 4
|
||||
q = Queue.Queue()
|
||||
q = queue.Queue()
|
||||
|
||||
|
||||
def add_request(request_type, path, data=None):
|
||||
@ -154,7 +156,7 @@ def index(slug_candidate):
|
||||
open(helpers.get_html_path('index.html')).read(),
|
||||
slug=slug,
|
||||
file_info=file_info,
|
||||
filename=os.path.basename(zip_filename).decode("utf-8"),
|
||||
filename=os.path.basename(zip_filename),
|
||||
filesize=zip_filesize,
|
||||
filesize_human=helpers.human_readable_filesize(zip_filesize)
|
||||
)
|
||||
@ -192,7 +194,7 @@ def download(slug_candidate):
|
||||
canceled = False
|
||||
while not done:
|
||||
chunk = fp.read(chunk_size)
|
||||
if chunk == '':
|
||||
if chunk == b'':
|
||||
done = True
|
||||
else:
|
||||
try:
|
||||
@ -224,7 +226,7 @@ def download(slug_candidate):
|
||||
|
||||
# download is finished, close the server
|
||||
if not stay_open and not canceled:
|
||||
print strings._("closing_automatically")
|
||||
print(strings._("closing_automatically"))
|
||||
if shutdown_func is None:
|
||||
raise RuntimeError('Not running with the Werkzeug Server')
|
||||
shutdown_func()
|
||||
@ -292,6 +294,6 @@ def stop(port):
|
||||
s.connect(('127.0.0.1', port))
|
||||
s.sendall('GET /{0:s}/shutdown HTTP/1.1\r\n\r\n'.format(shutdown_slug))
|
||||
else:
|
||||
urllib2.urlopen('http://127.0.0.1:{0:d}/{1:s}/shutdown'.format(port, shutdown_slug)).read()
|
||||
urlopen('http://127.0.0.1:{0:d}/{1:s}/shutdown'.format(port, shutdown_slug)).read()
|
||||
except:
|
||||
pass
|
||||
|
8
setup.py
8
setup.py
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
OnionShare | https://onionshare.org/
|
||||
@ -83,9 +83,11 @@ if system == 'Linux':
|
||||
url='https://github.com/micahflee/onionshare',
|
||||
license="GPL v3",
|
||||
keywords='onion, share, onionshare, tor, anonymous, web server',
|
||||
packages=['onionshare', 'onionshare_gui'],
|
||||
#packages=['onionshare', 'onionshare_gui'],
|
||||
packages=['onionshare'],
|
||||
include_package_data=True,
|
||||
scripts=['install/linux_scripts/onionshare', 'install/linux_scripts/onionshare-gui'],
|
||||
#scripts=['install/linux_scripts/onionshare', 'install/linux_scripts/onionshare-gui'],
|
||||
scripts=['install/linux_scripts/onionshare'],
|
||||
data_files=[
|
||||
(os.path.join(sys.prefix, 'share/applications'), ['install/onionshare.desktop']),
|
||||
(os.path.join(sys.prefix, 'share/appdata'), ['install/onionshare.appdata.xml']),
|
||||
|
Loading…
Reference in New Issue
Block a user