mirror of
https://github.com/onionshare/onionshare.git
synced 2025-01-14 16:57:16 -05:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
c02263230a
3
git-hooks/README.md
Normal file
3
git-hooks/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
To use these hooks, cp any of them to onionshare's `.git/hooks`.
|
||||||
|
|
||||||
|
* `pre-push` runs the test suite, and will push if the tests pass.
|
6
git-hooks/pre-push
Executable file
6
git-hooks/pre-push
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Pre-push hook. If you want to test with a different version of firefox, put
|
||||||
|
# the path in the CFX_FIREFOX environment variable.
|
||||||
|
|
||||||
|
nosetests test
|
@ -6,14 +6,20 @@ from functools import wraps
|
|||||||
from stem.control import Controller
|
from stem.control import Controller
|
||||||
from stem import SocketError
|
from stem import SocketError
|
||||||
|
|
||||||
from flask import Flask, Markup, Response, request, make_response, send_from_directory, render_template_string
|
from flask import Flask, Markup, Response, request, make_response, send_from_directory, render_template_string, abort
|
||||||
|
|
||||||
|
# Flask depends on itsdangerous, which needs constant time string comparison
|
||||||
|
# for the HMAC values in secure cookies. Since we know itsdangerous is
|
||||||
|
# available, we just use its function.
|
||||||
|
from itsdangerous import constant_time_compare
|
||||||
|
|
||||||
class NoTor(Exception):
|
class NoTor(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def random_string(num_bytes):
|
def random_string(num_bytes):
|
||||||
b = os.urandom(num_bytes)
|
b = os.urandom(num_bytes)
|
||||||
return base64.b32encode(b).lower().replace('=','')
|
h = hashlib.sha256(b).digest()[:16]
|
||||||
|
return base64.b32encode(h).lower().replace('=','')
|
||||||
|
|
||||||
def get_platform():
|
def get_platform():
|
||||||
p = platform.system()
|
p = platform.system()
|
||||||
@ -37,6 +43,19 @@ def set_stay_open(new_stay_open):
|
|||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
def debug_mode():
|
||||||
|
import logging
|
||||||
|
global app
|
||||||
|
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
temp_dir = os.environ['Temp'].replace('\\', '/')
|
||||||
|
else:
|
||||||
|
temp_dir = '/tmp/'
|
||||||
|
|
||||||
|
log_handler = logging.FileHandler('{0}/onionshare_server.log'.format(temp_dir))
|
||||||
|
log_handler.setLevel(logging.WARNING)
|
||||||
|
app.logger.addHandler(log_handler)
|
||||||
|
|
||||||
# get path of onioshare directory
|
# get path of onioshare directory
|
||||||
if get_platform() == 'Darwin':
|
if get_platform() == 'Darwin':
|
||||||
onionshare_dir = os.path.dirname(__file__)
|
onionshare_dir = os.path.dirname(__file__)
|
||||||
@ -73,9 +92,13 @@ def human_readable_filesize(b):
|
|||||||
u += 1
|
u += 1
|
||||||
return '{0} {1}'.format(round(b, 1), units[u])
|
return '{0} {1}'.format(round(b, 1), units[u])
|
||||||
|
|
||||||
@app.route("/{0}".format(slug))
|
@app.route("/<slug_candidate>")
|
||||||
def index():
|
def index(slug_candidate):
|
||||||
global filename, filesize, filehash, slug, strings, REQUEST_LOAD, onionshare_dir
|
global filename, filesize, filehash, slug, strings, REQUEST_LOAD, onionshare_dir
|
||||||
|
|
||||||
|
if not constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
||||||
|
abort(404)
|
||||||
|
|
||||||
add_request(REQUEST_LOAD, request.path)
|
add_request(REQUEST_LOAD, request.path)
|
||||||
return render_template_string(
|
return render_template_string(
|
||||||
open('{0}/index.html'.format(onionshare_dir)).read(),
|
open('{0}/index.html'.format(onionshare_dir)).read(),
|
||||||
@ -87,11 +110,14 @@ def index():
|
|||||||
strings=strings
|
strings=strings
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.route("/{0}/download".format(slug))
|
@app.route("/<slug_candidate>/download")
|
||||||
def download():
|
def download(slug_candidate):
|
||||||
global filename, filesize, q, download_count
|
global filename, filesize, q, download_count
|
||||||
global REQUEST_DOWNLOAD, REQUEST_PROGRESS
|
global REQUEST_DOWNLOAD, REQUEST_PROGRESS
|
||||||
|
|
||||||
|
if not constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
||||||
|
abort(404)
|
||||||
|
|
||||||
# each download has a unique id
|
# each download has a unique id
|
||||||
download_id = download_count
|
download_id = download_count
|
||||||
download_count += 1
|
download_count += 1
|
||||||
@ -159,7 +185,7 @@ def tails_open_port(port):
|
|||||||
def tails_close_port(port):
|
def tails_close_port(port):
|
||||||
if get_platform() == 'Tails':
|
if get_platform() == 'Tails':
|
||||||
print translated("closing_hole")
|
print translated("closing_hole")
|
||||||
subprocess.call(['/sbin/iptables', '-I', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'REJECT'])
|
subprocess.call(['/sbin/iptables', '-D', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT'])
|
||||||
|
|
||||||
def load_strings(default="en"):
|
def load_strings(default="en"):
|
||||||
global strings
|
global strings
|
||||||
@ -260,14 +286,19 @@ def main():
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--local-only', action='store_true', dest='local_only', help='Do not attempt to use tor: for development only')
|
parser.add_argument('--local-only', action='store_true', dest='local_only', help='Do not attempt to use tor: for development only')
|
||||||
parser.add_argument('--stay-open', action='store_true', dest='stay_open', help='Keep hidden service running after download has finished')
|
parser.add_argument('--stay-open', action='store_true', dest='stay_open', help='Keep hidden service running after download has finished')
|
||||||
|
parser.add_argument('--debug', action='store_true', dest='debug', help='Log errors to disk')
|
||||||
parser.add_argument('filename', nargs=1, help='File to share')
|
parser.add_argument('filename', nargs=1, help='File to share')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
filename = os.path.abspath(args.filename[0])
|
filename = os.path.abspath(args.filename[0])
|
||||||
local_only = args.local_only
|
local_only = bool(args.local_only)
|
||||||
|
debug = bool(args.debug)
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
debug_mode()
|
||||||
|
|
||||||
global stay_open
|
global stay_open
|
||||||
stay_open = args.stay_open
|
stay_open = bool(args.stay_open)
|
||||||
|
|
||||||
if not (filename and os.path.isfile(filename)):
|
if not (filename and os.path.isfile(filename)):
|
||||||
sys.exit(translated("not_a_file").format(filename))
|
sys.exit(translated("not_a_file").format(filename))
|
||||||
|
@ -100,7 +100,17 @@
|
|||||||
"ctrlc_to_stop": "Druk Ctrl-C om de server te stoppen",
|
"ctrlc_to_stop": "Druk Ctrl-C om de server te stoppen",
|
||||||
"not_a_file": "{0} is geen bestand.",
|
"not_a_file": "{0} is geen bestand.",
|
||||||
"filesize": "Bestandsgrootte",
|
"filesize": "Bestandsgrootte",
|
||||||
"sha1_checksum": "SHA1 controlecijfer"
|
"sha1_checksum": "SHA1 controlecijfer",
|
||||||
|
"copied_url": "URL gekopieerd naar klembord",
|
||||||
|
"download_page_loaded": "Download pagina geladen",
|
||||||
|
"download_started": "Download gestart",
|
||||||
|
"download_finished": "Download voltooid",
|
||||||
|
"other_page_loaded": "Andere pagina is geladen",
|
||||||
|
"tails_requires_root": "Je moet OnionShare als root draaien in Tails",
|
||||||
|
"close_on_finish": "Sluit automatisch",
|
||||||
|
"close_countdown": "Sluit in {0} seconden...",
|
||||||
|
"choose_file": "Kies betsand om te delen",
|
||||||
|
"copy_url": "Kopieer URL"
|
||||||
}, "pt": {
|
}, "pt": {
|
||||||
"punching_a_hole": "Abrindo um buraco no firewall.",
|
"punching_a_hole": "Abrindo um buraco no firewall.",
|
||||||
"closing_hole": "Fechando buraco no firewall.",
|
"closing_hole": "Fechando buraco no firewall.",
|
||||||
|
@ -96,7 +96,7 @@ def main():
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
filename = args.filename
|
filename = args.filename
|
||||||
local_only = args.local_only
|
local_only = bool(args.local_only)
|
||||||
stay_open = bool(args.stay_open)
|
stay_open = bool(args.stay_open)
|
||||||
debug = bool(args.debug)
|
debug = bool(args.debug)
|
||||||
|
|
||||||
@ -134,6 +134,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
webapp.onion_host = local_host
|
webapp.onion_host = local_host
|
||||||
if debug:
|
if debug:
|
||||||
|
onionshare.debug_mode()
|
||||||
webapp.debug_mode()
|
webapp.debug_mode()
|
||||||
|
|
||||||
# run the web app in a new thread
|
# run the web app in a new thread
|
||||||
|
@ -9,3 +9,52 @@ function human_readable_filesize(bytes, si) {
|
|||||||
} while(bytes >= thresh);
|
} while(bytes >= thresh);
|
||||||
return bytes.toFixed(1)+' '+units[u];
|
return bytes.toFixed(1)+' '+units[u];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function htmlspecialchars(string, quote_style, charset, double_encode) {
|
||||||
|
var optTemp = 0,
|
||||||
|
i = 0,
|
||||||
|
noquotes = false;
|
||||||
|
if (typeof quote_style === 'undefined' || quote_style === null) {
|
||||||
|
quote_style = 2;
|
||||||
|
}
|
||||||
|
string = string.toString();
|
||||||
|
if (double_encode !== false) {
|
||||||
|
// Put this first to avoid double-encoding
|
||||||
|
string = string.replace(/&/g, '&');
|
||||||
|
}
|
||||||
|
string = string.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
|
||||||
|
var OPTS = {
|
||||||
|
'ENT_NOQUOTES': 0,
|
||||||
|
'ENT_HTML_QUOTE_SINGLE': 1,
|
||||||
|
'ENT_HTML_QUOTE_DOUBLE': 2,
|
||||||
|
'ENT_COMPAT': 2,
|
||||||
|
'ENT_QUOTES': 3,
|
||||||
|
'ENT_IGNORE': 4
|
||||||
|
};
|
||||||
|
if (quote_style === 0) {
|
||||||
|
noquotes = true;
|
||||||
|
}
|
||||||
|
if (typeof quote_style !== 'number') {
|
||||||
|
// Allow for a single string or an array of string flags
|
||||||
|
quote_style = [].concat(quote_style);
|
||||||
|
for (i = 0; i < quote_style.length; i++) {
|
||||||
|
// Resolve string input to bitwise e.g. 'ENT_IGNORE' becomes 4
|
||||||
|
if (OPTS[quote_style[i]] === 0) {
|
||||||
|
noquotes = true;
|
||||||
|
} else if (OPTS[quote_style[i]]) {
|
||||||
|
optTemp = optTemp | OPTS[quote_style[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quote_style = optTemp;
|
||||||
|
}
|
||||||
|
if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
|
||||||
|
string = string.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
if (!noquotes) {
|
||||||
|
string = string.replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
@ -65,7 +65,7 @@ $(function(){
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(r.path != '/favicon.ico')
|
if(r.path != '/favicon.ico')
|
||||||
update($('<span>').addClass('weblog-error').html(onionshare.strings['other_page_loaded']+': '+r.path));
|
update($('<span>').addClass('weblog-error').html(onionshare.strings['other_page_loaded']+': '+htmlspecialchars(r.path)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from flask import Flask, render_template
|
from flask import Flask, render_template, make_response
|
||||||
|
from functools import wraps
|
||||||
import threading, json, os, time, platform, sys
|
import threading, json, os, time, platform, sys
|
||||||
|
|
||||||
onionshare = None
|
onionshare = None
|
||||||
@ -22,15 +23,40 @@ def debug_mode():
|
|||||||
else:
|
else:
|
||||||
temp_dir = '/tmp/'
|
temp_dir = '/tmp/'
|
||||||
|
|
||||||
log_handler = logging.FileHandler('{0}/onionshare.web.log'.format(temp_dir))
|
log_handler = logging.FileHandler('{0}/onionshare_gui.log'.format(temp_dir))
|
||||||
log_handler.setLevel(logging.WARNING)
|
log_handler.setLevel(logging.WARNING)
|
||||||
app.logger.addHandler(log_handler)
|
app.logger.addHandler(log_handler)
|
||||||
|
|
||||||
|
def add_response_headers(headers={}):
|
||||||
|
def decorator(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
resp = make_response(f(*args, **kwargs))
|
||||||
|
h = resp.headers
|
||||||
|
for header, value in headers.items():
|
||||||
|
h[header] = value
|
||||||
|
return resp
|
||||||
|
return decorated_function
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def csp(f):
|
||||||
|
@wraps(f)
|
||||||
|
# disable inline js, external js
|
||||||
|
@add_response_headers({'Content-Security-Policy': "default-src 'self'; connect-src 'self'"})
|
||||||
|
# ugh, webkit embedded in Qt4 is stupid old
|
||||||
|
# TODO: remove webkit, build GUI with normal Qt widgets
|
||||||
|
@add_response_headers({'X-WebKit-CSP': "default-src 'self'; connect-src 'self'"})
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
@csp
|
||||||
def index():
|
def index():
|
||||||
return render_template('index.html')
|
return render_template('index.html')
|
||||||
|
|
||||||
@app.route("/init_info")
|
@app.route("/init_info")
|
||||||
|
@csp
|
||||||
def init_info():
|
def init_info():
|
||||||
global onionshare, filename, stay_open
|
global onionshare, filename, stay_open
|
||||||
basename = os.path.basename(filename)
|
basename = os.path.basename(filename)
|
||||||
@ -42,6 +68,7 @@ def init_info():
|
|||||||
})
|
})
|
||||||
|
|
||||||
@app.route("/start_onionshare")
|
@app.route("/start_onionshare")
|
||||||
|
@csp
|
||||||
def start_onionshare():
|
def start_onionshare():
|
||||||
global onionshare, onionshare_port, filename, onion_host, url
|
global onionshare, onionshare_port, filename, onion_host, url
|
||||||
|
|
||||||
@ -62,6 +89,7 @@ def start_onionshare():
|
|||||||
})
|
})
|
||||||
|
|
||||||
@app.route("/copy_url")
|
@app.route("/copy_url")
|
||||||
|
@csp
|
||||||
def copy_url():
|
def copy_url():
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
# Qt's QClipboard isn't working in Windows
|
# Qt's QClipboard isn't working in Windows
|
||||||
@ -82,16 +110,19 @@ def copy_url():
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
@app.route("/stay_open_true")
|
@app.route("/stay_open_true")
|
||||||
|
@csp
|
||||||
def stay_open_true():
|
def stay_open_true():
|
||||||
global onionshare
|
global onionshare
|
||||||
onionshare.set_stay_open(True)
|
onionshare.set_stay_open(True)
|
||||||
|
|
||||||
@app.route("/stay_open_false")
|
@app.route("/stay_open_false")
|
||||||
|
@csp
|
||||||
def stay_open_false():
|
def stay_open_false():
|
||||||
global onionshare
|
global onionshare
|
||||||
onionshare.set_stay_open(False)
|
onionshare.set_stay_open(False)
|
||||||
|
|
||||||
@app.route("/heartbeat")
|
@app.route("/heartbeat")
|
||||||
|
@csp
|
||||||
def check_for_requests():
|
def check_for_requests():
|
||||||
global onionshare
|
global onionshare
|
||||||
events = []
|
events = []
|
||||||
@ -107,6 +138,7 @@ def check_for_requests():
|
|||||||
return json.dumps(events)
|
return json.dumps(events)
|
||||||
|
|
||||||
@app.route("/close")
|
@app.route("/close")
|
||||||
|
@csp
|
||||||
def close():
|
def close():
|
||||||
global qtapp
|
global qtapp
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# import stuff for pyinstaller to find
|
# import stuff for pyinstaller to find
|
||||||
import os, sys, subprocess, time, hashlib, platform, json, locale, socket, argparse, Queue, inspect, base64, random, functools
|
import os, sys, subprocess, time, hashlib, platform, json, locale, socket, argparse, Queue, inspect, base64, random, functools, logging
|
||||||
import PyQt4.QtCore, PyQt4.QtGui, PyQt4.QtWebKit
|
from PyQt4 import QtCore, QtGui, QtWebKit
|
||||||
import stem, stem.control, flask
|
import stem, stem.control, flask
|
||||||
import onionshare, onionshare_gui
|
import onionshare, onionshare_gui
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
!define ABOUTURL "https://github.com/micahflee/onionshare"
|
!define ABOUTURL "https://github.com/micahflee/onionshare"
|
||||||
|
|
||||||
# change these with each release
|
# change these with each release
|
||||||
!define INSTALLSIZE 46094
|
!define INSTALLSIZE 46124
|
||||||
!define VERSIONMAJOR 0
|
!define VERSIONMAJOR 0
|
||||||
!define VERSIONMINOR 3
|
!define VERSIONMINOR 4
|
||||||
!define VERSIONSTRING "0.3dev"
|
!define VERSIONSTRING "0.4"
|
||||||
|
|
||||||
RequestExecutionLevel admin
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user