mirror of
https://github.com/onionshare/onionshare.git
synced 2024-12-27 08:19:41 -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 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):
|
||||
pass
|
||||
|
||||
def random_string(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():
|
||||
p = platform.system()
|
||||
@ -37,6 +43,19 @@ def set_stay_open(new_stay_open):
|
||||
|
||||
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
|
||||
if get_platform() == 'Darwin':
|
||||
onionshare_dir = os.path.dirname(__file__)
|
||||
@ -73,9 +92,13 @@ def human_readable_filesize(b):
|
||||
u += 1
|
||||
return '{0} {1}'.format(round(b, 1), units[u])
|
||||
|
||||
@app.route("/{0}".format(slug))
|
||||
def index():
|
||||
@app.route("/<slug_candidate>")
|
||||
def index(slug_candidate):
|
||||
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)
|
||||
return render_template_string(
|
||||
open('{0}/index.html'.format(onionshare_dir)).read(),
|
||||
@ -87,11 +110,14 @@ def index():
|
||||
strings=strings
|
||||
)
|
||||
|
||||
@app.route("/{0}/download".format(slug))
|
||||
def download():
|
||||
@app.route("/<slug_candidate>/download")
|
||||
def download(slug_candidate):
|
||||
global filename, filesize, q, download_count
|
||||
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
|
||||
download_id = download_count
|
||||
download_count += 1
|
||||
@ -159,7 +185,7 @@ def tails_open_port(port):
|
||||
def tails_close_port(port):
|
||||
if get_platform() == 'Tails':
|
||||
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"):
|
||||
global strings
|
||||
@ -260,14 +286,19 @@ def main():
|
||||
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('--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')
|
||||
args = parser.parse_args()
|
||||
|
||||
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
|
||||
stay_open = args.stay_open
|
||||
stay_open = bool(args.stay_open)
|
||||
|
||||
if not (filename and os.path.isfile(filename)):
|
||||
sys.exit(translated("not_a_file").format(filename))
|
||||
|
@ -100,7 +100,17 @@
|
||||
"ctrlc_to_stop": "Druk Ctrl-C om de server te stoppen",
|
||||
"not_a_file": "{0} is geen bestand.",
|
||||
"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": {
|
||||
"punching_a_hole": "Abrindo um buraco no firewall.",
|
||||
"closing_hole": "Fechando buraco no firewall.",
|
||||
|
@ -96,7 +96,7 @@ def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
filename = args.filename
|
||||
local_only = args.local_only
|
||||
local_only = bool(args.local_only)
|
||||
stay_open = bool(args.stay_open)
|
||||
debug = bool(args.debug)
|
||||
|
||||
@ -134,6 +134,7 @@ def main():
|
||||
else:
|
||||
webapp.onion_host = local_host
|
||||
if debug:
|
||||
onionshare.debug_mode()
|
||||
webapp.debug_mode()
|
||||
|
||||
# run the web app in a new thread
|
||||
|
@ -9,3 +9,52 @@ function human_readable_filesize(bytes, si) {
|
||||
} while(bytes >= thresh);
|
||||
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 {
|
||||
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
|
||||
|
||||
onionshare = None
|
||||
@ -22,15 +23,40 @@ def debug_mode():
|
||||
else:
|
||||
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)
|
||||
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("/")
|
||||
@csp
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route("/init_info")
|
||||
@csp
|
||||
def init_info():
|
||||
global onionshare, filename, stay_open
|
||||
basename = os.path.basename(filename)
|
||||
@ -42,6 +68,7 @@ def init_info():
|
||||
})
|
||||
|
||||
@app.route("/start_onionshare")
|
||||
@csp
|
||||
def start_onionshare():
|
||||
global onionshare, onionshare_port, filename, onion_host, url
|
||||
|
||||
@ -62,6 +89,7 @@ def start_onionshare():
|
||||
})
|
||||
|
||||
@app.route("/copy_url")
|
||||
@csp
|
||||
def copy_url():
|
||||
if platform.system() == 'Windows':
|
||||
# Qt's QClipboard isn't working in Windows
|
||||
@ -82,16 +110,19 @@ def copy_url():
|
||||
return ''
|
||||
|
||||
@app.route("/stay_open_true")
|
||||
@csp
|
||||
def stay_open_true():
|
||||
global onionshare
|
||||
onionshare.set_stay_open(True)
|
||||
|
||||
@app.route("/stay_open_false")
|
||||
@csp
|
||||
def stay_open_false():
|
||||
global onionshare
|
||||
onionshare.set_stay_open(False)
|
||||
|
||||
@app.route("/heartbeat")
|
||||
@csp
|
||||
def check_for_requests():
|
||||
global onionshare
|
||||
events = []
|
||||
@ -107,6 +138,7 @@ def check_for_requests():
|
||||
return json.dumps(events)
|
||||
|
||||
@app.route("/close")
|
||||
@csp
|
||||
def close():
|
||||
global qtapp
|
||||
time.sleep(1)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# import stuff for pyinstaller to find
|
||||
import os, sys, subprocess, time, hashlib, platform, json, locale, socket, argparse, Queue, inspect, base64, random, functools
|
||||
import PyQt4.QtCore, PyQt4.QtGui, PyQt4.QtWebKit
|
||||
import os, sys, subprocess, time, hashlib, platform, json, locale, socket, argparse, Queue, inspect, base64, random, functools, logging
|
||||
from PyQt4 import QtCore, QtGui, QtWebKit
|
||||
import stem, stem.control, flask
|
||||
import onionshare, onionshare_gui
|
||||
|
||||
|
@ -3,10 +3,10 @@
|
||||
!define ABOUTURL "https://github.com/micahflee/onionshare"
|
||||
|
||||
# change these with each release
|
||||
!define INSTALLSIZE 46094
|
||||
!define INSTALLSIZE 46124
|
||||
!define VERSIONMAJOR 0
|
||||
!define VERSIONMINOR 3
|
||||
!define VERSIONSTRING "0.3dev"
|
||||
!define VERSIONMINOR 4
|
||||
!define VERSIONSTRING "0.4"
|
||||
|
||||
RequestExecutionLevel admin
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user