Compare commits
51 commits
py3-latest
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bf771eda5f | ||
![]() |
a9b5561c49 | ||
![]() |
a121c23973 | ||
![]() |
27a67d9753 | ||
![]() |
67b78ca12d | ||
![]() |
77a5d88ec9 | ||
![]() |
861e085597 | ||
![]() |
32e9839f23 | ||
![]() |
028d491294 | ||
![]() |
bdca28844c | ||
![]() |
9e7ae55068 | ||
![]() |
7418400b52 | ||
![]() |
c165d21d95 | ||
![]() |
f08bea7f90 | ||
![]() |
9b274415e0 | ||
![]() |
8dd3a8495b | ||
![]() |
fd7f724e2b | ||
![]() |
3366edc244 | ||
![]() |
129aff2c0c | ||
![]() |
7e78fbc16e | ||
![]() |
719df4ac88 | ||
![]() |
7a217a3741 | ||
![]() |
85fd08774f | ||
![]() |
c0d81021df | ||
![]() |
5d81467083 | ||
![]() |
7a59a19df1 | ||
![]() |
171b591798 | ||
![]() |
27c47bb3bd | ||
![]() |
80f3f9d511 | ||
![]() |
ba6a75f8d7 | ||
![]() |
b3f677f806 | ||
![]() |
c51dfe728f | ||
![]() |
eb88dbbec8 | ||
![]() |
350adeb52d | ||
![]() |
5ab20317d0 | ||
![]() |
cdd0f9cda3 | ||
![]() |
d504cdf501 | ||
![]() |
e333b47c27 | ||
![]() |
91b2f6a8a7 | ||
![]() |
f66cfc9a5e | ||
![]() |
911733955b | ||
![]() |
abb566e35f | ||
![]() |
06be430b74 | ||
![]() |
ef892e91da | ||
![]() |
c198237938 | ||
![]() |
74ce0c50ff | ||
![]() |
b8b8ce21fa | ||
![]() |
5716b7505f | ||
![]() |
b4ceb6957c | ||
![]() |
d51e9c68f4 | ||
![]() |
4d25a02cd1 |
27 changed files with 254 additions and 133 deletions
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
custom: https://zeronet.io/docs/help_zeronet/donate/
|
|
@ -4,7 +4,7 @@ FROM alpine:3.8
|
|||
ENV HOME /root
|
||||
|
||||
#Install ZeroNet
|
||||
RUN apk --no-cache --no-progress add musl-dev gcc python python-dev py2-pip tor \
|
||||
RUN apk --no-cache --no-progress add musl-dev gcc python python-dev py2-pip tor openssl \
|
||||
&& pip install --no-cache-dir gevent msgpack \
|
||||
&& apk del musl-dev gcc python-dev py2-pip \
|
||||
&& echo "ControlPort 9051" >> /etc/tor/torrc \
|
||||
|
|
|
@ -4,6 +4,7 @@ import subprocess
|
|||
import shutil
|
||||
import collections
|
||||
import math
|
||||
import json
|
||||
|
||||
import msgpack
|
||||
import gevent
|
||||
|
@ -96,12 +97,12 @@ class UiRequestPlugin(object):
|
|||
|
||||
site.content_manager.contents.loadItem(file_info["content_inner_path"]) # reload cache
|
||||
|
||||
return {
|
||||
return json.dumps({
|
||||
"merkle_root": merkle_root,
|
||||
"piece_num": len(piecemap_info["sha512_pieces"]),
|
||||
"piece_size": piece_size,
|
||||
"inner_path": inner_path
|
||||
}
|
||||
})
|
||||
|
||||
def readMultipartHeaders(self, wsgi_input):
|
||||
for i in range(100):
|
||||
|
@ -604,6 +605,7 @@ class FileRequestPlugin(object):
|
|||
if file.read(10) == "\0" * 10:
|
||||
# Looks empty, but makes sures we don't have that piece
|
||||
file_info = site.content_manager.getFileInfo(inner_path)
|
||||
if "piece_size" in file_info:
|
||||
piece_i = pos / file_info["piece_size"]
|
||||
if not site.storage.piecefields[file_info["sha512"]][piece_i]:
|
||||
return False
|
||||
|
|
|
@ -491,3 +491,32 @@ class TestBigfile:
|
|||
|
||||
site_temp.needFile("%s|%s-%s" % (inner_path, 9 * 1024 * 1024, 10 * 1024 * 1024))
|
||||
assert site_temp.storage.getSize(inner_path) == site.storage.getSize(inner_path)
|
||||
|
||||
@pytest.mark.parametrize("size", [1024 * 3, 1024 * 1024 * 3, 1024 * 1024 * 30])
|
||||
def testNullFileRead(self, file_server, site, site_temp, size):
|
||||
inner_path = "data/optional.iso"
|
||||
|
||||
f = site.storage.open(inner_path, "w")
|
||||
f.write("\0" * size)
|
||||
f.close()
|
||||
assert site.content_manager.sign("content.json", self.privatekey)
|
||||
|
||||
# Init source server
|
||||
site.connection_server = file_server
|
||||
file_server.sites[site.address] = site
|
||||
|
||||
# Init client server
|
||||
site_temp.connection_server = FileServer(file_server.ip, 1545)
|
||||
site_temp.connection_server.sites[site_temp.address] = site_temp
|
||||
site_temp.addPeer(file_server.ip, 1544)
|
||||
|
||||
# Download site
|
||||
site_temp.download(blind_includes=True).join(timeout=5)
|
||||
|
||||
if "piecemap" in site.content_manager.getFileInfo(inner_path): # Bigfile
|
||||
site_temp.needFile(inner_path + "|all")
|
||||
else:
|
||||
site_temp.needFile(inner_path)
|
||||
|
||||
|
||||
assert site_temp.storage.getSize(inner_path) == size
|
||||
|
|
|
@ -145,7 +145,14 @@ class UiWebsocketPlugin(object):
|
|||
include_site = filter_storage.site_manager.get(include["address"])
|
||||
if not include_site:
|
||||
continue
|
||||
try:
|
||||
content = include_site.storage.loadJson(include["inner_path"])
|
||||
include["error"] = None
|
||||
except Exception as err:
|
||||
if include_site.settings["own"]:
|
||||
include_site.log.warning("Error loading filter %s: %s" % (include["inner_path"], err))
|
||||
content = {}
|
||||
include["error"] = str(err)
|
||||
include["mutes"] = content.get("mutes", {})
|
||||
include["siteblocks"] = content.get("siteblocks", {})
|
||||
back.append(include)
|
||||
|
|
|
@ -4,6 +4,7 @@ import re
|
|||
from Plugin import PluginManager
|
||||
from Db import DbQuery
|
||||
from Debug import Debug
|
||||
from util import helper
|
||||
|
||||
|
||||
@PluginManager.registerTo("UiWebsocket")
|
||||
|
@ -66,14 +67,14 @@ class UiWebsocketPlugin(object):
|
|||
query = " UNION ".join(query_parts)
|
||||
|
||||
if ":params" in query:
|
||||
query = query.replace(":params", ",".join(["?"] * len(params)))
|
||||
res = site.storage.query(query + " ORDER BY date_added DESC LIMIT %s" % limit, params * query_raw.count(":params"))
|
||||
else:
|
||||
query_params = map(helper.sqlquote, params)
|
||||
query = query.replace(":params", ",".join(query_params))
|
||||
|
||||
res = site.storage.query(query + " ORDER BY date_added DESC LIMIT %s" % limit)
|
||||
|
||||
except Exception as err: # Log error
|
||||
self.log.error("%s feed query %s error: %s" % (address, name, Debug.formatException(err)))
|
||||
stats.append({"site": site.address, "feed_name": name, "error": str(err), "query": query})
|
||||
stats.append({"site": site.address, "feed_name": name, "error": str(err)})
|
||||
continue
|
||||
|
||||
for row in res:
|
||||
|
|
|
@ -132,8 +132,12 @@ class UiWebsocketPlugin(object):
|
|||
wheres_raw = []
|
||||
if "bigfile" in filter:
|
||||
wheres["size >"] = 1024 * 1024 * 10
|
||||
if "downloaded" in filter:
|
||||
|
||||
if "not_downloaded" in filter:
|
||||
wheres["is_downloaded"] = 0
|
||||
elif "downloaded" in filter:
|
||||
wheres_raw.append("(is_downloaded = 1 OR is_pinned = 1)")
|
||||
|
||||
if "pinned" in filter:
|
||||
wheres["is_pinned"] = 1
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@ import cStringIO as StringIO
|
|||
import os
|
||||
import zipfile
|
||||
|
||||
|
||||
class ZipStream(file):
|
||||
def __init__(self, dir_path):
|
||||
self.dir_path = dir_path
|
||||
self.pos = 0
|
||||
self.zf = zipfile.ZipFile(self, 'w', zipfile.ZIP_DEFLATED, allowZip64 = True)
|
||||
self.buff_pos = 0
|
||||
self.zf = zipfile.ZipFile(self, 'w', zipfile.ZIP_DEFLATED, allowZip64=True)
|
||||
self.buff = StringIO.StringIO()
|
||||
self.file_list = self.getFileList()
|
||||
|
||||
|
@ -27,6 +27,8 @@ class ZipStream(file):
|
|||
self.buff.seek(0)
|
||||
back = self.buff.read()
|
||||
self.buff.truncate(0)
|
||||
self.buff.seek(0)
|
||||
self.buff_pos += len(back)
|
||||
return back
|
||||
|
||||
def write(self, data):
|
||||
|
@ -36,8 +38,22 @@ class ZipStream(file):
|
|||
def tell(self):
|
||||
return self.pos
|
||||
|
||||
def seek(self, pos, type):
|
||||
pass
|
||||
def seek(self, pos, whence=0):
|
||||
if pos >= self.buff_pos:
|
||||
self.buff.seek(pos - self.buff_pos, whence)
|
||||
self.pos = pos
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
zs = ZipStream(".")
|
||||
out = open("out.zip", "wb")
|
||||
while 1:
|
||||
data = zs.read()
|
||||
print("Write %s" % len(data))
|
||||
if not data:
|
||||
break
|
||||
out.write(data)
|
||||
out.close()
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
}
|
||||
|
||||
.drag-bg { width: 100%; height: 100%; position: fixed; }
|
||||
.fixbutton.dragging { cursor: -webkit-grabbing; }
|
||||
.fixbutton-bg:active { cursor: -webkit-grabbing; }
|
||||
.fixbutton.dragging { cursor: -webkit-grabbing; cusor: grabbing; }
|
||||
.fixbutton-bg:active { cursor: -webkit-grabbing; cusor: grabbing; }
|
||||
|
||||
|
||||
.body-sidebar, .body-internals { background-color: #666 !important; }
|
||||
|
|
|
@ -7,11 +7,18 @@ from Translate import translate
|
|||
@PluginManager.registerTo("UiRequest")
|
||||
class UiRequestPlugin(object):
|
||||
def actionSiteMedia(self, path, **kwargs):
|
||||
file_name = path.split("/")[-1]
|
||||
file_name = path.split("/")[-1].lower()
|
||||
if not file_name: # Path ends with /
|
||||
file_name = "index.html"
|
||||
extension = file_name.split(".")[-1]
|
||||
if translate.lang != "en" and extension in ["js", "html"]:
|
||||
if extension == "html":
|
||||
should_translate = True
|
||||
elif extension == "js" and translate.lang != "en":
|
||||
should_translate = True
|
||||
else:
|
||||
should_translate = False
|
||||
|
||||
if should_translate:
|
||||
path_parts = self.parsePath(path)
|
||||
kwargs["header_length"] = False
|
||||
file_generator = super(UiRequestPlugin, self).actionSiteMedia(path, **kwargs)
|
||||
|
|
|
@ -96,7 +96,7 @@ class UiRequestPlugin(object):
|
|||
class UiWebsocketPlugin(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.multiuser_denied_cmds = (
|
||||
"siteDelete", "configSet", "serverShutdown", "serverUpdate", "siteClone",
|
||||
"sitePause", "siteResume", "siteDelete", "configSet", "serverShutdown", "serverUpdate", "siteClone",
|
||||
"siteSetOwned", "siteSetAutodownloadoptional", "dbReload", "dbRebuild",
|
||||
"mergerSiteDelete", "siteSetLimit", "siteSetAutodownloadBigfileLimit",
|
||||
"optionalLimitSet", "optionalHelp", "optionalHelpRemove", "optionalHelpAll", "optionalFilePin", "optionalFileUnpin", "optionalFileDelete",
|
||||
|
|
|
@ -13,7 +13,7 @@ class Config(object):
|
|||
|
||||
def __init__(self, argv):
|
||||
self.version = "0.6.5"
|
||||
self.rev = 3853
|
||||
self.rev = 3870
|
||||
self.argv = argv
|
||||
self.action = None
|
||||
self.pending_changes = {}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import socket
|
||||
import time
|
||||
import random
|
||||
|
||||
import gevent
|
||||
import msgpack
|
||||
|
@ -172,7 +173,7 @@ class Connection(object):
|
|||
self.sock.connect(sock_address)
|
||||
|
||||
# Detect protocol
|
||||
self.send({"cmd": "handshake", "req_id": 0, "params": self.getHandshakeInfo()})
|
||||
self.send({"cmd": "handshake", "req_id": 0, "params": self.getHandshakeInfo(), "random": "A" * random.randint(0, 1024)})
|
||||
event_connected = self.event_connected
|
||||
gevent.spawn(self.messageLoop)
|
||||
connect_res = event_connected.get() # Wait for handshake
|
||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
|||
import os
|
||||
import ssl
|
||||
import hashlib
|
||||
import random
|
||||
|
||||
from Config import config
|
||||
from util import SslPatch
|
||||
|
@ -20,6 +21,12 @@ class CryptConnectionManager:
|
|||
|
||||
self.crypt_supported = [] # Supported cryptos
|
||||
|
||||
self.cacert_pem = config.data_dir + "/cacert-rsa.pem"
|
||||
self.cakey_pem = config.data_dir + "/cakey-rsa.pem"
|
||||
self.cert_pem = config.data_dir + "/cert-rsa.pem"
|
||||
self.cert_csr = config.data_dir + "/cert-rsa.csr"
|
||||
self.key_pem = config.data_dir + "/key-rsa.pem"
|
||||
|
||||
# Select crypt that supported by both sides
|
||||
# Return: Name of the crypto
|
||||
def selectCrypt(self, client_supported):
|
||||
|
@ -32,12 +39,13 @@ class CryptConnectionManager:
|
|||
# Return: wrapped socket
|
||||
def wrapSocket(self, sock, crypt, server=False, cert_pin=None):
|
||||
if crypt == "tls-rsa":
|
||||
ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:AES128-GCM-SHA256:AES128-SHA256:HIGH:"
|
||||
ciphers = "ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:AES128-SHA256:AES256-SHA:"
|
||||
ciphers += "!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK"
|
||||
if server:
|
||||
sock_wrapped = ssl.wrap_socket(
|
||||
sock, server_side=server, keyfile='%s/key-rsa.pem' % config.data_dir,
|
||||
certfile='%s/cert-rsa.pem' % config.data_dir, ciphers=ciphers)
|
||||
sock, server_side=server, keyfile=self.key_pem,
|
||||
certfile=self.cert_pem, ciphers=ciphers
|
||||
)
|
||||
else:
|
||||
sock_wrapped = ssl.wrap_socket(sock, ciphers=ciphers)
|
||||
if cert_pin:
|
||||
|
@ -50,7 +58,7 @@ class CryptConnectionManager:
|
|||
def removeCerts(self):
|
||||
if config.keep_ssl_cert:
|
||||
return False
|
||||
for file_name in ["cert-rsa.pem", "key-rsa.pem"]:
|
||||
for file_name in ["cert-rsa.pem", "key-rsa.pem", "cacert-rsa.pem", "cakey-rsa.pem", "cacert-rsa.srl", "cert-rsa.csr"]:
|
||||
file_path = "%s/%s" % (config.data_dir, file_name)
|
||||
if os.path.isfile(file_path):
|
||||
os.unlink(file_path)
|
||||
|
@ -66,15 +74,33 @@ class CryptConnectionManager:
|
|||
# Try to create RSA server cert + sign for connection encryption
|
||||
# Return: True on success
|
||||
def createSslRsaCert(self):
|
||||
if os.path.isfile("%s/cert-rsa.pem" % config.data_dir) and os.path.isfile("%s/key-rsa.pem" % config.data_dir):
|
||||
casubjects = [
|
||||
"/C=US/O=Amazon/OU=Server CA 1B/CN=Amazon",
|
||||
"/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
|
||||
"/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA",
|
||||
"/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA"
|
||||
]
|
||||
fakedomains = [
|
||||
"yahoo.com", "amazon.com", "live.com", "microsoft.com", "mail.ru", "csdn.net", "bing.com",
|
||||
"amazon.co.jp", "office.com", "imdb.com", "msn.com", "samsung.com", "huawei.com", "ztedevices.com",
|
||||
"godaddy.com", "w3.org", "gravatar.com", "creativecommons.org", "hatena.ne.jp",
|
||||
"adobe.com", "opera.com", "apache.org", "rambler.ru", "one.com", "nationalgeographic.com",
|
||||
"networksolutions.com", "php.net", "python.org", "phoca.cz", "debian.org", "ubuntu.com",
|
||||
"nazwa.pl", "symantec.com"
|
||||
]
|
||||
self.openssl_env['CN'] = random.choice(fakedomains)
|
||||
|
||||
if os.path.isfile(self.cert_pem) and os.path.isfile(self.key_pem):
|
||||
return True # Files already exits
|
||||
|
||||
import subprocess
|
||||
cmd = "%s req -x509 -newkey rsa:2048 -sha256 -batch -keyout %s -out %s -nodes -config %s" % helper.shellquote(
|
||||
# Generate CAcert and CAkey
|
||||
cmd = "%s req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj %s -keyout %s -out %s -batch -config %s" % helper.shellquote(
|
||||
self.openssl_bin,
|
||||
config.data_dir+"/key-rsa.pem",
|
||||
config.data_dir+"/cert-rsa.pem",
|
||||
self.openssl_env["OPENSSL_CONF"]
|
||||
random.choice(casubjects),
|
||||
self.cakey_pem,
|
||||
self.cacert_pem,
|
||||
self.openssl_env["OPENSSL_CONF"],
|
||||
)
|
||||
proc = subprocess.Popen(
|
||||
cmd.encode(sys.getfilesystemencoding()),
|
||||
|
@ -82,47 +108,50 @@ class CryptConnectionManager:
|
|||
)
|
||||
back = proc.stdout.read().strip()
|
||||
proc.wait()
|
||||
logging.debug("Generating RSA cert and key PEM files...%s" % back)
|
||||
logging.debug("Generating RSA CAcert and CAkey PEM files...%s" % back)
|
||||
|
||||
if os.path.isfile("%s/cert-rsa.pem" % config.data_dir) and os.path.isfile("%s/key-rsa.pem" % config.data_dir):
|
||||
if not (os.path.isfile(self.cacert_pem) and os.path.isfile(self.cakey_pem)):
|
||||
logging.error("RSA ECC SSL CAcert generation failed, CAcert or CAkey files not exist.")
|
||||
return False
|
||||
|
||||
# Generate certificate key and signing request
|
||||
cmd = "%s req -new -newkey rsa:2048 -keyout %s -out %s -subj %s -sha256 -nodes -batch -config %s" % helper.shellquote(
|
||||
self.openssl_bin,
|
||||
self.key_pem,
|
||||
self.cert_csr,
|
||||
"/CN=" + self.openssl_env['CN'],
|
||||
self.openssl_env["OPENSSL_CONF"],
|
||||
)
|
||||
proc = subprocess.Popen(
|
||||
cmd.encode(sys.getfilesystemencoding()),
|
||||
shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env
|
||||
)
|
||||
back = proc.stdout.read().strip()
|
||||
proc.wait()
|
||||
logging.debug("Generating certificate key and signing request...%s" % back)
|
||||
|
||||
# Sign request and generate certificate
|
||||
cmd = "%s x509 -req -in %s -CA %s -CAkey %s -CAcreateserial -out %s -days 730 -sha256 -extensions x509_ext -extfile %s" % helper.shellquote(
|
||||
self.openssl_bin,
|
||||
self.cert_csr,
|
||||
self.cacert_pem,
|
||||
self.cakey_pem,
|
||||
self.cert_pem,
|
||||
self.openssl_env["OPENSSL_CONF"],
|
||||
)
|
||||
proc = subprocess.Popen(
|
||||
cmd.encode(sys.getfilesystemencoding()),
|
||||
shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env
|
||||
)
|
||||
back = proc.stdout.read().strip()
|
||||
proc.wait()
|
||||
logging.debug("Generating RSA cert...%s" % back)
|
||||
|
||||
if os.path.isfile(self.cert_pem) and os.path.isfile(self.key_pem):
|
||||
return True
|
||||
else:
|
||||
logging.error("RSA ECC SSL cert generation failed, cert or key files not exist.")
|
||||
return False
|
||||
|
||||
# Not used yet: Missing on some platform
|
||||
"""def createSslEccCert(self):
|
||||
return False
|
||||
import subprocess
|
||||
|
||||
# Create ECC privatekey
|
||||
proc = subprocess.Popen(
|
||||
"%s ecparam -name prime256v1 -genkey -out %s/key-ecc.pem" % (self.openssl_bin, config.data_dir),
|
||||
shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env
|
||||
)
|
||||
back = proc.stdout.read().strip()
|
||||
proc.wait()
|
||||
self.log.debug("Generating ECC privatekey PEM file...%s" % back)
|
||||
|
||||
# Create ECC cert
|
||||
proc = subprocess.Popen(
|
||||
"%s req -new -key %s -x509 -nodes -out %s -config %s" % helper.shellquote(
|
||||
self.openssl_bin,
|
||||
config.data_dir+"/key-ecc.pem",
|
||||
config.data_dir+"/cert-ecc.pem",
|
||||
self.openssl_env["OPENSSL_CONF"]
|
||||
),
|
||||
shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env
|
||||
)
|
||||
back = proc.stdout.read().strip()
|
||||
proc.wait()
|
||||
self.log.debug("Generating ECC cert PEM file...%s" % back)
|
||||
|
||||
if os.path.isfile("%s/cert-ecc.pem" % config.data_dir) and os.path.isfile("%s/key-ecc.pem" % config.data_dir):
|
||||
return True
|
||||
else:
|
||||
self.logging.error("ECC SSL cert generation failed, cert or key files not exits.")
|
||||
return False
|
||||
"""
|
||||
|
||||
manager = CryptConnectionManager()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import time
|
||||
import re
|
||||
from util import helper
|
||||
|
||||
|
||||
# Special sqlite cursor
|
||||
|
||||
|
@ -12,12 +14,6 @@ class DbCursor:
|
|||
self.cursor = conn.cursor()
|
||||
self.logging = False
|
||||
|
||||
def quoteValue(self, value):
|
||||
if type(value) is int:
|
||||
return str(value)
|
||||
else:
|
||||
return "'%s'" % value.replace("'", "''")
|
||||
|
||||
def execute(self, query, params=None):
|
||||
self.db.last_query_time = time.time()
|
||||
if isinstance(params, dict) and "?" in query: # Make easier select and insert by allowing dict params
|
||||
|
@ -35,7 +31,7 @@ class DbCursor:
|
|||
operator = "IN"
|
||||
if len(value) > 100:
|
||||
# Embed values in query to avoid "too many SQL variables" error
|
||||
query_values = ",".join(map(self.quoteValue, value))
|
||||
query_values = ",".join(map(helper.sqlquote, value))
|
||||
else:
|
||||
query_values = ",".join(["?"] * len(value))
|
||||
values += value
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import sys
|
||||
import logging
|
||||
import signal
|
||||
|
||||
import gevent
|
||||
import gevent.hub
|
||||
|
@ -8,22 +9,22 @@ from Config import config
|
|||
|
||||
last_error = None
|
||||
|
||||
def shutdown():
|
||||
print "Shutting down..."
|
||||
def shutdown(reason="Unknown"):
|
||||
logging.info("Shutting down (reason: %s)..." % reason)
|
||||
if "file_server" in dir(sys.modules["main"]) and sys.modules["main"].file_server.running:
|
||||
try:
|
||||
if "file_server" in dir(sys.modules["main"]):
|
||||
gevent.spawn(sys.modules["main"].file_server.stop)
|
||||
if "ui_server" in dir(sys.modules["main"]):
|
||||
gevent.spawn(sys.modules["main"].ui_server.stop)
|
||||
except Exception, err:
|
||||
except Exception as err:
|
||||
print "Proper shutdown error: %s" % err
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
# Store last error, ignore notify, allow manual error logging
|
||||
def handleError(*args):
|
||||
def handleError(*args, **kwargs):
|
||||
global last_error
|
||||
if not args: # Manual called
|
||||
args = sys.exc_info()
|
||||
|
@ -32,22 +33,23 @@ def handleError(*args):
|
|||
silent = False
|
||||
if args[0].__name__ != "Notify":
|
||||
last_error = args
|
||||
|
||||
if args[0].__name__ == "KeyboardInterrupt":
|
||||
shutdown()
|
||||
return
|
||||
if not silent and args[0].__name__ != "Notify":
|
||||
shutdown("Keyboard interrupt")
|
||||
elif not silent and args[0].__name__ != "Notify":
|
||||
logging.exception("Unhandled exception")
|
||||
if "greenlet.py" not in args[2].tb_frame.f_code.co_filename: # Don't display error twice
|
||||
sys.__excepthook__(*args)
|
||||
sys.__excepthook__(*args, **kwargs)
|
||||
|
||||
|
||||
# Ignore notify errors
|
||||
def handleErrorNotify(*args):
|
||||
if args[0].__name__ == "KeyboardInterrupt":
|
||||
shutdown()
|
||||
if args[0].__name__ != "Notify":
|
||||
logging.exception("Unhandled exception")
|
||||
sys.__excepthook__(*args)
|
||||
def handleErrorNotify(*args, **kwargs):
|
||||
err = args[0]
|
||||
if err.__name__ == "KeyboardInterrupt":
|
||||
shutdown("Keyboard interrupt")
|
||||
elif err.__name__ != "Notify":
|
||||
logging.error("Unhandled exception: %s" % [args])
|
||||
sys.__excepthook__(*args, **kwargs)
|
||||
|
||||
|
||||
if config.debug: # Keep last error for /Debug
|
||||
|
@ -79,6 +81,12 @@ def handleGreenletError(self, context, type, value, tb):
|
|||
|
||||
gevent.hub.Hub.handle_error = handleGreenletError
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, lambda signum, stack_frame: shutdown("SIGTERM"))
|
||||
except Exception as err:
|
||||
logging.debug("Error setting up SIGTERM watcher: %s" % err)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import time
|
||||
from gevent import monkey
|
||||
|
|
|
@ -146,7 +146,7 @@ class Peer(object):
|
|||
|
||||
self.log("Send request: %s %s %s %s" % (params.get("site", ""), cmd, params.get("inner_path", ""), params.get("location", "")))
|
||||
|
||||
for retry in range(1, 4): # Retry 3 times
|
||||
for retry in range(1, 2): # Retry 1 times
|
||||
try:
|
||||
if not self.connection:
|
||||
raise Exception("No connection found")
|
||||
|
|
|
@ -90,7 +90,7 @@ class Site(object):
|
|||
self.settings = settings
|
||||
if "cache" not in settings:
|
||||
settings["cache"] = {}
|
||||
if "size_files_optional" not in settings:
|
||||
if "size_optional" not in settings:
|
||||
settings["size_optional"] = 0
|
||||
if "optional_downloaded" not in settings:
|
||||
settings["optional_downloaded"] = 0
|
||||
|
|
|
@ -362,7 +362,7 @@ class SiteStorage(object):
|
|||
if not inner_path:
|
||||
return self.directory
|
||||
|
||||
if ".." in inner_path:
|
||||
if "../" in inner_path:
|
||||
raise Exception(u"File not allowed: %s" % inner_path)
|
||||
|
||||
return u"%s/%s" % (self.directory, inner_path)
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
" files needs to be downloaded": " i file devono essere scaricati",
|
||||
" downloaded": " scaricati",
|
||||
" download failed": " scaricamento fallito",
|
||||
"Peers found: ": "Peer trovati: ",
|
||||
"Peers found: ": "Peers trovati: ",
|
||||
"No peers found": "Nessun peer trovato",
|
||||
"Running out of size limit (": "Superato il limite di spazio (",
|
||||
"Set limit to \" + site_info.next_size_limit + \"MB": "Imposta il limite a \" + site_info.next_size_limit + \"MB",
|
||||
|
|
|
@ -103,7 +103,7 @@ class UiRequest(object):
|
|||
|
||||
extra_headers = {"Access-Control-Allow-Origin": "null"}
|
||||
|
||||
self.sendHeader(content_type=content_type, extra_headers=extra_headers)
|
||||
self.sendHeader(content_type=content_type, extra_headers=extra_headers, noscript=True)
|
||||
return ""
|
||||
|
||||
if path == "/":
|
||||
|
@ -246,7 +246,13 @@ class UiRequest(object):
|
|||
headers["Connection"] = "Keep-Alive"
|
||||
headers["Keep-Alive"] = "max=25, timeout=30"
|
||||
headers["X-Frame-Options"] = "SAMEORIGIN"
|
||||
if content_type != "text/html" and self.env.get("HTTP_REFERER") and self.isSameOrigin(self.getReferer(), self.getRequestUrl()):
|
||||
is_referer_allowed = False
|
||||
if self.env.get("HTTP_REFERER"):
|
||||
if self.isSameOrigin(self.getReferer(), self.getRequestUrl()):
|
||||
is_referer_allowed = True
|
||||
elif self.getReferer() == "%s://%s/" % (self.env["wsgi.url_scheme"], self.env["HTTP_HOST"]): # Origin-only referer
|
||||
is_referer_allowed = True
|
||||
if content_type != "text/html" and is_referer_allowed:
|
||||
headers["Access-Control-Allow-Origin"] = "*" # Allow load font files from css
|
||||
|
||||
if noscript:
|
||||
|
@ -287,9 +293,12 @@ class UiRequest(object):
|
|||
# Renders a template
|
||||
def render(self, template_path, *args, **kwargs):
|
||||
template = open(template_path).read()
|
||||
for key, val in kwargs.items():
|
||||
template = template.replace("{%s}" % key, "%s" % val)
|
||||
return template.encode("utf8")
|
||||
def renderReplacer(m):
|
||||
return "%s" % kwargs.get(m.group(1), "")
|
||||
|
||||
template_rendered = re.sub("{(.*?)}", renderReplacer, template)
|
||||
|
||||
return template_rendered.encode("utf8")
|
||||
|
||||
# - Actions -
|
||||
|
||||
|
@ -410,6 +419,9 @@ class UiRequest(object):
|
|||
file_url = "/" + address + "/" + inner_path
|
||||
root_url = "/" + address + "/"
|
||||
|
||||
if self.isProxyRequest():
|
||||
self.server.allowed_ws_origins.add(self.env["HTTP_HOST"])
|
||||
|
||||
# Wrapper variable inits
|
||||
body_style = ""
|
||||
meta_tags = ""
|
||||
|
@ -520,7 +532,7 @@ class UiRequest(object):
|
|||
if path.endswith("/"):
|
||||
path = path + "index.html"
|
||||
|
||||
if ".." in path or "./" in path:
|
||||
if "../" in path or "./" in path:
|
||||
raise SecurityError("Invalid path")
|
||||
|
||||
match = re.match("/media/(?P<address>[A-Za-z0-9]+[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
|
||||
|
@ -704,9 +716,20 @@ class UiRequest(object):
|
|||
# On websocket connection
|
||||
def actionWebsocket(self):
|
||||
ws = self.env.get("wsgi.websocket")
|
||||
|
||||
if ws:
|
||||
wrapper_key = self.get["wrapper_key"]
|
||||
# Allow only same-origin websocket requests
|
||||
origin = self.env.get("HTTP_ORIGIN")
|
||||
host = self.env.get("HTTP_HOST")
|
||||
# Allow only same-origin websocket requests
|
||||
if origin:
|
||||
origin_host = origin.split("://", 1)[-1]
|
||||
if origin_host != host and origin_host not in self.server.allowed_ws_origins:
|
||||
ws.send(json.dumps({"error": "Invalid origin: %s" % origin}))
|
||||
return self.error403("Invalid origin: %s" % origin)
|
||||
|
||||
# Find site by wrapper_key
|
||||
wrapper_key = self.get["wrapper_key"]
|
||||
site = None
|
||||
for site_check in self.server.sites.values():
|
||||
if site_check.settings["wrapper_key"] == wrapper_key:
|
||||
|
@ -781,30 +804,30 @@ class UiRequest(object):
|
|||
|
||||
# Send bad request error
|
||||
def error400(self, message=""):
|
||||
self.sendHeader(400)
|
||||
self.sendHeader(400, noscript=True)
|
||||
return self.formatError("Bad Request", message)
|
||||
|
||||
# You are not allowed to access this
|
||||
def error403(self, message="", details=True):
|
||||
self.sendHeader(403)
|
||||
self.sendHeader(403, noscript=True)
|
||||
self.log.error("Error 403: %s" % message)
|
||||
return self.formatError("Forbidden", message, details=details)
|
||||
|
||||
# Send file not found error
|
||||
def error404(self, path=""):
|
||||
self.sendHeader(404)
|
||||
return self.formatError("Not Found", cgi.escape(path.encode("utf8")), details=False)
|
||||
self.sendHeader(404, noscript=True)
|
||||
return self.formatError("Not Found", path.encode("utf8"), details=False)
|
||||
|
||||
# Internal server error
|
||||
def error500(self, message=":("):
|
||||
self.sendHeader(500)
|
||||
return self.formatError("Server error", cgi.escape(message))
|
||||
self.sendHeader(500, noscript=True)
|
||||
return self.formatError("Server error", message)
|
||||
|
||||
def formatError(self, title, message, details=True):
|
||||
import sys
|
||||
import gevent
|
||||
|
||||
if details:
|
||||
if details and config.debug:
|
||||
details = {key: val for key, val in self.env.items() if hasattr(val, "endswith") and "COOKIE" not in key}
|
||||
details["version_zeronet"] = "%s r%s" % (config.version, config.rev)
|
||||
details["version_python"] = sys.version
|
||||
|
@ -819,10 +842,10 @@ class UiRequest(object):
|
|||
</style>
|
||||
<h1>%s</h1>
|
||||
<h2>%s</h3>
|
||||
<h3>Please <a href="https://github.com/HelloZeroNet/ZeroNet/issues" target="_blank">report it</a> if you think this an error.</h3>
|
||||
<h3>Please <a href="https://github.com/HelloZeroNet/ZeroNet/issues" target="_top">report it</a> if you think this an error.</h3>
|
||||
<h4>Details:</h4>
|
||||
<pre>%s</pre>
|
||||
""" % (title, message, json.dumps(details, indent=4, sort_keys=True))
|
||||
""" % (title, cgi.escape(message), cgi.escape(json.dumps(details, indent=4, sort_keys=True)))
|
||||
else:
|
||||
return """
|
||||
<h1>%s</h1>
|
||||
|
|
|
@ -75,6 +75,7 @@ class UiServer:
|
|||
else:
|
||||
self.allowed_hosts = set([])
|
||||
self.allow_trans_proxy = config.ui_trans_proxy
|
||||
self.allowed_ws_origins = set()
|
||||
|
||||
self.wrapper_nonces = []
|
||||
self.add_nonces = []
|
||||
|
|
|
@ -1120,6 +1120,11 @@ class UiWebsocket(object):
|
|||
self.response(to, {"error": "Forbidden you cannot set this config key"})
|
||||
return
|
||||
|
||||
if key == "open_browser":
|
||||
if value not in ["default_browser", "False"]:
|
||||
self.response(to, {"error": "Forbidden: Invalid value"})
|
||||
return
|
||||
|
||||
# Remove empty lines from lists
|
||||
if type(value) is list:
|
||||
value = [line for line in value if line]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[ req ]
|
||||
prompt = no
|
||||
prompt = yes
|
||||
default_bits = 2048
|
||||
default_keyfile = server-key.pem
|
||||
distinguished_name = subject
|
||||
|
@ -32,8 +32,8 @@ authorityKeyIdentifier = keyid,issuer
|
|||
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alternate_names
|
||||
nsComment = "OpenSSL Generated Certificate"
|
||||
|
||||
# RFC 5280, Section 4.2.1.12 makes EKU optional
|
||||
# CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
|
||||
|
@ -46,8 +46,8 @@ subjectKeyIdentifier = hash
|
|||
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alternate_names
|
||||
nsComment = "OpenSSL Generated Certificate"
|
||||
|
||||
# RFC 5280, Section 4.2.1.12 makes EKU optional
|
||||
# CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
|
||||
|
@ -55,16 +55,5 @@ nsComment = "OpenSSL Generated Certificate"
|
|||
|
||||
[ alternate_names ]
|
||||
|
||||
DNS.1 = example.com
|
||||
DNS.2 = www.example.com
|
||||
DNS.3 = mail.example.com
|
||||
DNS.4 = ftp.example.com
|
||||
|
||||
# Add these if you need them. But usually you don't want them or
|
||||
# need them in production. You may need them for development.
|
||||
# DNS.5 = localhost
|
||||
# DNS.6 = localhost.localdomain
|
||||
# DNS.7 = 127.0.0.1
|
||||
|
||||
# IPv6 localhost
|
||||
# DNS.8 = ::1
|
||||
DNS.1 = $ENV::CN
|
||||
DNS.2 = www.$ENV::CN
|
||||
|
|
|
@ -72,6 +72,13 @@ def getFreeSpace():
|
|||
return free_space
|
||||
|
||||
|
||||
def sqlquote(value):
|
||||
if type(value) is int:
|
||||
return str(value)
|
||||
else:
|
||||
return "'%s'" % value.replace("'", "''")
|
||||
|
||||
|
||||
def shellquote(*args):
|
||||
if len(args) == 1:
|
||||
return '"%s"' % args[0].replace('"', "")
|
||||
|
|
3
start.py
3
start.py
|
@ -9,7 +9,8 @@ import zeronet
|
|||
|
||||
|
||||
def main():
|
||||
sys.argv = [sys.argv[0]]+["--open_browser", "default_browser"]+sys.argv[1:]
|
||||
if "--open_browser" not in sys.argv:
|
||||
sys.argv = [sys.argv[0]] + ["--open_browser", "default_browser"] + sys.argv[1:]
|
||||
zeronet.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -11,12 +11,6 @@ def main():
|
|||
|
||||
main = None
|
||||
try:
|
||||
import signal
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(0))
|
||||
except Exception as err:
|
||||
print("Error setting up SIGTERM watcher: %s" % err)
|
||||
|
||||
app_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
os.chdir(app_dir) # Change working dir to zeronet.py dir
|
||||
sys.path.insert(0, os.path.join(app_dir, "src/lib")) # External liblary directory
|
||||
|
|
Loading…
Reference in a new issue