mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 12:03:32 +00:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9c03c65e07 | ||
|  | d8ed006b9b | ||
|  | 63c0623a5e | ||
|  | fd84506db0 | ||
|  | d8bcb44e44 | ||
|  | 56a26b0916 | ||
|  | efcf1d6b90 | ||
|  | 9f578bfec6 | ||
|  | 1f170d7d28 | ||
|  | 5ae14cf9be | ||
|  | aaf9d53be9 | ||
|  | 75c73f7ba7 | ||
|  | b6dba8beee | ||
|  | 94521cdc1a | ||
|  | 3365b1c355 | ||
|  | 6c957c4923 | ||
|  | 833997f04c | ||
|  | 68d51e4037 | ||
|  | ce274d2011 | ||
|  | 280778ed43 | ||
|  | 0f558ecbbf | 
							
								
								
									
										7
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -20,6 +20,13 @@ | ||||
|                 "srv::r:aed:cnodupe" | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "No debug", | ||||
|             "preLaunchTask": "no_dbg", | ||||
|             "type": "python", | ||||
|             //"request": "attach", "port": 42069 | ||||
|             // fork: nc -l 42069 </dev/null | ||||
|         }, | ||||
|         { | ||||
|             "name": "Run active unit test", | ||||
|             "type": "python", | ||||
|   | ||||
							
								
								
									
										12
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -50,11 +50,9 @@ | ||||
|     "files.associations": { | ||||
|         "*.makefile": "makefile" | ||||
|     }, | ||||
|     "editor.codeActionsOnSaveTimeout": 9001, | ||||
|     "editor.formatOnSaveTimeout": 9001, | ||||
|     // | ||||
|     //  things you may wanna edit: | ||||
|     // | ||||
|     "python.pythonPath": "/usr/bin/python3", | ||||
|     //"python.linting.enabled": true, | ||||
|     "python.formatting.blackArgs": [ | ||||
|         "-t", | ||||
|         "py27" | ||||
|     ], | ||||
|     "python.linting.enabled": true, | ||||
| } | ||||
							
								
								
									
										5
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							| @@ -5,6 +5,11 @@ | ||||
|             "label": "pre", | ||||
|             "command": "true;rm -rf inc/* inc/.hist/;mkdir -p inc;", | ||||
|             "type": "shell" | ||||
|         }, | ||||
|         { | ||||
|             "label": "no_dbg", | ||||
|             "command": "${config:python.pythonPath} -m copyparty -ed -emp -e2d -e2s -a ed:wark -v srv::r:aed:cnodupe ;exit 1", | ||||
|             "type": "shell" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @@ -8,7 +8,7 @@ | ||||
|  | ||||
| turn your phone or raspi into a portable file server with resumable uploads/downloads using IE6 or any other browser | ||||
|  | ||||
| * server runs on anything with `py2.7` or `py3.2+` | ||||
| * server runs on anything with `py2.7` or `py3.3+` | ||||
| * *resumable* uploads need `firefox 12+` / `chrome 6+` / `safari 6+` / `IE 10+` | ||||
| * code standard: `black` | ||||
|  | ||||
| @@ -68,17 +68,16 @@ summary: it works! you can use it! (but technically not even close to beta) | ||||
| # dependencies | ||||
|  | ||||
| * `jinja2` | ||||
|   * pulls in `markupsafe` as of v2.7; use jinja 2.6 on py3.2 | ||||
|  | ||||
| optional, enables thumbnails: | ||||
| optional, will eventually enable thumbnails: | ||||
| * `Pillow` (requires py2.7 or py3.5+) | ||||
|  | ||||
|  | ||||
| # sfx | ||||
|  | ||||
| currently there are two self-contained binaries: | ||||
| * `copyparty-sfx.sh` for unix (linux and osx) -- smaller, more robust | ||||
| * `copyparty-sfx.py` for windows (unix too) -- crossplatform, beta | ||||
| * [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) -- pure python, works everywhere | ||||
| * [copyparty-sfx.sh](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.sh) -- smaller, but only for linux and macos | ||||
|  | ||||
| launch either of them (**use sfx.py on systemd**) and it'll unpack and run copyparty, assuming you have python installed of course | ||||
|  | ||||
|   | ||||
| @@ -1067,7 +1067,7 @@ def main(): | ||||
|         dbg = null_log | ||||
|  | ||||
|     if WINDOWS: | ||||
|         os.system("") | ||||
|         os.system("rem") | ||||
|  | ||||
|         for ch in '<>:"\\|?*': | ||||
|             # microsoft maps illegal characters to f0xx | ||||
|   | ||||
| @@ -980,7 +980,7 @@ def main(): | ||||
|         dbg = null_log | ||||
|  | ||||
|     if WINDOWS: | ||||
|         os.system("") | ||||
|         os.system("rem") | ||||
|  | ||||
|         for ch in '<>:"\\|?*': | ||||
|             # microsoft maps illegal characters to f0xx | ||||
|   | ||||
| @@ -10,7 +10,12 @@ | ||||
| * modify `10.13.1.1` as necessary if you wish to support browsers without javascript | ||||
|  | ||||
| ### [`explorer-nothumbs-nofoldertypes.reg`](explorer-nothumbs-nofoldertypes.reg) | ||||
| disables thumbnails and folder-type detection in windows explorer, makes it way faster (especially for slow/networked locations (such as copyparty-fuse)) | ||||
| * disables thumbnails and folder-type detection in windows explorer | ||||
| * makes it way faster (especially for slow/networked locations (such as copyparty-fuse)) | ||||
|  | ||||
| ### [`cfssl.sh`](cfssl.sh) | ||||
| * creates CA and server certificates using cfssl | ||||
| * give a 3rd argument to install it to your copyparty config | ||||
|  | ||||
| # OS integration | ||||
| init-scripts to start copyparty as a service | ||||
|   | ||||
							
								
								
									
										72
									
								
								contrib/cfssl.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										72
									
								
								contrib/cfssl.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
|  | ||||
| # ca-name and server-name | ||||
| ca_name="$1" | ||||
| srv_name="$2" | ||||
|  | ||||
| [ -z "$srv_name" ] && { | ||||
| 	echo "need arg 1: ca name" | ||||
| 	echo "need arg 2: server name" | ||||
| 	exit 1 | ||||
| } | ||||
|  | ||||
|  | ||||
| gen_ca() { | ||||
| 	(tee /dev/stderr <<EOF | ||||
| {"CN": "$ca_name ca", | ||||
| "CA": {"expiry":"87600h", "pathlen":0}, | ||||
| "key": {"algo":"rsa", "size":4096}, | ||||
| "names": [{"O":"$ca_name ca"}]} | ||||
| EOF | ||||
| 	)| | ||||
| 	cfssl gencert -initca - | | ||||
| 	cfssljson -bare ca | ||||
| 	 | ||||
| 	mv ca-key.pem ca.key | ||||
| 	rm ca.csr | ||||
| } | ||||
|  | ||||
|  | ||||
| gen_srv() { | ||||
| 	(tee /dev/stderr <<EOF | ||||
| {"key": {"algo":"rsa", "size":4096}, | ||||
| "names": [{"O":"$ca_name - $srv_name"}]} | ||||
| EOF | ||||
| 	)| | ||||
| 	cfssl gencert -ca ca.pem -ca-key ca.key \ | ||||
| 		-profile=www -hostname="$srv_name.$ca_name" - | | ||||
| 	cfssljson -bare "$srv_name" | ||||
|  | ||||
| 	mv "$srv_name-key.pem" "$srv_name.key" | ||||
| 	rm "$srv_name.csr" | ||||
| } | ||||
|  | ||||
|  | ||||
| # create ca if not exist | ||||
| [ -e ca.key ] || | ||||
| 	gen_ca | ||||
|  | ||||
| # always create server cert | ||||
| gen_srv | ||||
|  | ||||
|  | ||||
| # dump cert info | ||||
| show() { | ||||
| 	openssl x509 -text -noout -in $1 | | ||||
| 	awk '!o; {o=0} /[0-9a-f:]{16}/{o=1}' | ||||
| } | ||||
| show ca.pem | ||||
| show "$srv_name.pem" | ||||
|  | ||||
|  | ||||
| # write cert into copyparty config | ||||
| [ -z "$3" ] || { | ||||
| 	mkdir -p ~/.config/copyparty | ||||
| 	cat "$srv_name".{key,pem} ca.pem >~/.config/copyparty/cert.pem  | ||||
| } | ||||
|  | ||||
|  | ||||
| # rm *.key *.pem | ||||
| # cfssl print-defaults config | ||||
| # cfssl print-defaults csr | ||||
| @@ -8,7 +8,9 @@ __copyright__ = 2019 | ||||
| __license__ = "MIT" | ||||
| __url__ = "https://github.com/9001/copyparty/" | ||||
|  | ||||
| import re | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
| import shutil | ||||
| import filecmp | ||||
| @@ -19,7 +21,13 @@ from textwrap import dedent | ||||
| from .__init__ import E, WINDOWS, VT100 | ||||
| from .__version__ import S_VERSION, S_BUILD_DT, CODENAME | ||||
| from .svchub import SvcHub | ||||
| from .util import py_desc | ||||
| from .util import py_desc, align_tab | ||||
|  | ||||
| HAVE_SSL = True | ||||
| try: | ||||
|     import ssl | ||||
| except: | ||||
|     HAVE_SSL = False | ||||
|  | ||||
|  | ||||
| class RiceFormatter(argparse.HelpFormatter): | ||||
| @@ -85,10 +93,77 @@ def ensure_cert(): | ||||
|     # printf 'NO\n.\n.\n.\n.\ncopyparty-insecure\n.\n' | faketime '2000-01-01 00:00:00' openssl req -x509 -sha256 -newkey rsa:2048 -keyout insecure.pem -out insecure.pem -days $((($(printf %d 0x7fffffff)-$(date +%s --date=2000-01-01T00:00:00Z))/(60*60*24))) -nodes && ls -al insecure.pem && openssl x509 -in insecure.pem -text -noout | ||||
|  | ||||
|  | ||||
| def configure_ssl_ver(al): | ||||
|     def terse_sslver(txt): | ||||
|         txt = txt.lower() | ||||
|         for c in ["_", "v", "."]: | ||||
|             txt = txt.replace(c, "") | ||||
|  | ||||
|         return txt.replace("tls10", "tls1") | ||||
|  | ||||
|     # oh man i love openssl | ||||
|     # check this out | ||||
|     # hold my beer | ||||
|     ptn = re.compile(r"^OP_NO_(TLS|SSL)v") | ||||
|     sslver = terse_sslver(al.ssl_ver).split(",") | ||||
|     flags = [k for k in ssl.__dict__ if ptn.match(k)] | ||||
|     # SSLv2 SSLv3 TLSv1 TLSv1_1 TLSv1_2 TLSv1_3 | ||||
|     if "help" in sslver: | ||||
|         avail = [terse_sslver(x[6:]) for x in flags] | ||||
|         avail = " ".join(sorted(avail) + ["all"]) | ||||
|         print("\navailable ssl/tls versions:\n  " + avail) | ||||
|         sys.exit(0) | ||||
|  | ||||
|     al.ssl_flags_en = 0 | ||||
|     al.ssl_flags_de = 0 | ||||
|     for flag in sorted(flags): | ||||
|         ver = terse_sslver(flag[6:]) | ||||
|         num = getattr(ssl, flag) | ||||
|         if ver in sslver: | ||||
|             al.ssl_flags_en |= num | ||||
|         else: | ||||
|             al.ssl_flags_de |= num | ||||
|  | ||||
|     if sslver == ["all"]: | ||||
|         x = al.ssl_flags_en | ||||
|         al.ssl_flags_en = al.ssl_flags_de | ||||
|         al.ssl_flags_de = x | ||||
|  | ||||
|     for k in ["ssl_flags_en", "ssl_flags_de"]: | ||||
|         num = getattr(al, k) | ||||
|         print("{}: {:8x} ({})".format(k, num, num)) | ||||
|  | ||||
|     # think i need that beer now | ||||
|  | ||||
|  | ||||
| def configure_ssl_ciphers(al): | ||||
|     ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) | ||||
|     if al.ssl_ver: | ||||
|         ctx.options &= ~al.ssl_flags_en | ||||
|         ctx.options |= al.ssl_flags_de | ||||
|  | ||||
|     is_help = al.ciphers == "help" | ||||
|  | ||||
|     if al.ciphers and not is_help: | ||||
|         try: | ||||
|             ctx.set_ciphers(al.ciphers) | ||||
|         except: | ||||
|             print("\n\033[1;31mfailed to set ciphers\033[0m\n") | ||||
|  | ||||
|     if not hasattr(ctx, "get_ciphers"): | ||||
|         print("cannot read cipher list: openssl or python too old") | ||||
|     else: | ||||
|         ciphers = [x["description"] for x in ctx.get_ciphers()] | ||||
|         print("\n  ".join(["\nenabled ciphers:"] + align_tab(ciphers) + [""])) | ||||
|  | ||||
|     if is_help: | ||||
|         sys.exit(0) | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     time.strptime("19970815", "%Y%m%d")  # python#7980 | ||||
|     if WINDOWS: | ||||
|         os.system("")  # enables colors | ||||
|         os.system("rem")  # enables colors | ||||
|  | ||||
|     desc = py_desc().replace("[", "\033[1;30m[") | ||||
|  | ||||
| @@ -96,7 +171,8 @@ def main(): | ||||
|     print(f.format(S_VERSION, CODENAME, S_BUILD_DT, desc)) | ||||
|  | ||||
|     ensure_locale() | ||||
|     ensure_cert() | ||||
|     if HAVE_SSL: | ||||
|         ensure_cert() | ||||
|  | ||||
|     ap = argparse.ArgumentParser( | ||||
|         formatter_class=RiceFormatter, | ||||
| @@ -127,6 +203,16 @@ def main(): | ||||
|  | ||||
|             consider the config file for more flexible account/volume management, | ||||
|             including dynamic reload at runtime (and being more readable w) | ||||
|  | ||||
|             values for --urlform: | ||||
|               "stash" dumps the data to file and returns length + checksum | ||||
|               "save,get" dumps to file and returns the page like a GET | ||||
|               "print,get" prints the data in the log and returns GET | ||||
|               (leave out the ",get" to return an error instead) | ||||
|  | ||||
|             --ciphers help = available ssl/tls ciphers, | ||||
|             --ssl-ver help = available ssl/tls versions, | ||||
|               default is what python considers safe, usually >= TLS1 | ||||
|             """ | ||||
|         ), | ||||
|     ) | ||||
| @@ -148,6 +234,15 @@ def main(): | ||||
|     ap.add_argument("-nih", action="store_true", help="no info hostname") | ||||
|     ap.add_argument("-nid", action="store_true", help="no info disk-usage") | ||||
|     ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile") | ||||
|     ap.add_argument("--urlform", type=str, default="print,get", help="how to handle url-forms") | ||||
|  | ||||
|     ap2 = ap.add_argument_group('SSL/TLS options') | ||||
|     ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls") | ||||
|     ap2.add_argument("--https-only", action="store_true", help="disable plaintext") | ||||
|     ap2.add_argument("--ssl-ver", type=str, help="ssl/tls versions to allow") | ||||
|     ap2.add_argument("--ciphers", metavar="LIST", help="set allowed ciphers") | ||||
|     ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info") | ||||
|     ap2.add_argument("--ssl-log", metavar="PATH", help="log master secrets") | ||||
|     al = ap.parse_args() | ||||
|     # fmt: on | ||||
|  | ||||
| @@ -161,6 +256,15 @@ def main(): | ||||
|     except: | ||||
|         raise Exception("invalid value for -p") | ||||
|  | ||||
|     if HAVE_SSL: | ||||
|         if al.ssl_ver: | ||||
|             configure_ssl_ver(al) | ||||
|  | ||||
|         if al.ciphers: | ||||
|             configure_ssl_ciphers(al) | ||||
|     else: | ||||
|         print("\033[33m  ssl module does not exist; cannot enable https\033[0m\n") | ||||
|  | ||||
|     SvcHub(al).run() | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| # coding: utf-8 | ||||
|  | ||||
| VERSION = (0, 7, 3) | ||||
| VERSION = (0, 7, 6) | ||||
| CODENAME = "keeping track" | ||||
| BUILD_DT = (2021, 2, 3) | ||||
| BUILD_DT = (2021, 2, 12) | ||||
|  | ||||
| S_VERSION = ".".join(map(str, VERSION)) | ||||
| S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) | ||||
|   | ||||
| @@ -134,6 +134,16 @@ class HttpCli(object): | ||||
|             uparam["raw"] = True | ||||
|             uparam["dots"] = True | ||||
|  | ||||
|         if hasattr(self.s, "cipher"): | ||||
|             self.ssl_suf = "".join( | ||||
|                 [ | ||||
|                     " \033[3{}m{}".format(c, s) | ||||
|                     for c, s in zip([6, 3, 6], self.s.cipher()) | ||||
|                 ] | ||||
|             ) | ||||
|         else: | ||||
|             self.ssl_suf = "" | ||||
|  | ||||
|         try: | ||||
|             if self.mode in ["GET", "HEAD"]: | ||||
|                 return self.handle_get() and self.keepalive | ||||
| @@ -211,7 +221,7 @@ class HttpCli(object): | ||||
|  | ||||
|             logmsg += " [\033[36m" + rval + "\033[0m]" | ||||
|  | ||||
|         self.log(logmsg) | ||||
|         self.log(logmsg + self.ssl_suf) | ||||
|  | ||||
|         # "embedded" resources | ||||
|         if self.vpath.startswith(".cpr"): | ||||
| @@ -245,7 +255,7 @@ class HttpCli(object): | ||||
|         return self.tx_browser() | ||||
|  | ||||
|     def handle_options(self): | ||||
|         self.log("OPTIONS " + self.req) | ||||
|         self.log("OPTIONS " + self.req + self.ssl_suf) | ||||
|         self.send_headers( | ||||
|             None, | ||||
|             204, | ||||
| @@ -258,7 +268,7 @@ class HttpCli(object): | ||||
|         return True | ||||
|  | ||||
|     def handle_put(self): | ||||
|         self.log("PUT " + self.req) | ||||
|         self.log("PUT " + self.req + self.ssl_suf) | ||||
|  | ||||
|         if self.headers.get("expect", "").lower() == "100-continue": | ||||
|             try: | ||||
| @@ -269,7 +279,7 @@ class HttpCli(object): | ||||
|         return self.handle_stash() | ||||
|  | ||||
|     def handle_post(self): | ||||
|         self.log("POST " + self.req) | ||||
|         self.log("POST " + self.req + self.ssl_suf) | ||||
|  | ||||
|         if self.headers.get("expect", "").lower() == "100-continue": | ||||
|             try: | ||||
| @@ -294,16 +304,37 @@ class HttpCli(object): | ||||
|         if "application/octet-stream" in ctype: | ||||
|             return self.handle_post_binary() | ||||
|  | ||||
|         raise Pebkac(405, "don't know how to handle {} POST".format(ctype)) | ||||
|         if "application/x-www-form-urlencoded" in ctype: | ||||
|             opt = self.args.urlform | ||||
|             if "stash" in opt: | ||||
|                 return self.handle_stash() | ||||
|  | ||||
|     def handle_stash(self): | ||||
|             if "save" in opt: | ||||
|                 post_sz, _, _, path = self.dump_to_file() | ||||
|                 self.log("urlform: {} bytes, {}".format(post_sz, path)) | ||||
|             elif "print" in opt: | ||||
|                 reader, _ = self.get_body_reader() | ||||
|                 for buf in reader: | ||||
|                     buf = buf.decode("utf-8", "replace") | ||||
|                     self.log("urlform:\n  {}\n".format(buf)) | ||||
|  | ||||
|             if "get" in opt: | ||||
|                 return self.handle_get() | ||||
|  | ||||
|             raise Pebkac(405, "POST({}) is disabled".format(ctype)) | ||||
|  | ||||
|         raise Pebkac(405, "don't know how to handle POST({})".format(ctype)) | ||||
|  | ||||
|     def get_body_reader(self): | ||||
|         remains = int(self.headers.get("content-length", None)) | ||||
|         if remains is None: | ||||
|             reader = read_socket_unbounded(self.sr) | ||||
|             self.keepalive = False | ||||
|             return read_socket_unbounded(self.sr), remains | ||||
|         else: | ||||
|             reader = read_socket(self.sr, remains) | ||||
|             return read_socket(self.sr, remains), remains | ||||
|  | ||||
|     def dump_to_file(self): | ||||
|         reader, remains = self.get_body_reader() | ||||
|         vfs, rem = self.conn.auth.vfs.get(self.vpath, self.uname, False, True) | ||||
|         fdir = os.path.join(vfs.realpath, rem) | ||||
|  | ||||
| @@ -314,6 +345,10 @@ class HttpCli(object): | ||||
|         with open(path, "wb", 512 * 1024) as f: | ||||
|             post_sz, _, sha_b64 = hashcopy(self.conn, reader, f) | ||||
|  | ||||
|         return post_sz, sha_b64, remains, path | ||||
|  | ||||
|     def handle_stash(self): | ||||
|         post_sz, sha_b64, remains, path = self.dump_to_file() | ||||
|         spd = self._spd(post_sz) | ||||
|         self.log("{} wrote {}/{} bytes to {}".format(spd, post_sz, remains, path)) | ||||
|         self.reply("{}\n{}\n".format(post_sz, sha_b64).encode("utf-8")) | ||||
| @@ -517,10 +552,9 @@ class HttpCli(object): | ||||
|                 raise Pebkac(500, "mkdir failed, check the logs") | ||||
|  | ||||
|         vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/") | ||||
|         esc_paths = [quotep(vpath), html_escape(vpath)] | ||||
|         html = self.conn.tpl_msg.render( | ||||
|             h2='<a href="/{}">go to /{}</a>'.format( | ||||
|                 quotep(vpath), html_escape(vpath) | ||||
|             ), | ||||
|             h2='<a href="/{}">go to /{}</a>'.format(*esc_paths), | ||||
|             pre="aight", | ||||
|             click=True, | ||||
|         ) | ||||
| @@ -903,8 +937,11 @@ class HttpCli(object): | ||||
|             open_func = open | ||||
|             # 512 kB is optimal for huge files, use 64k | ||||
|             open_args = [fsenc(fs_path), "rb", 64 * 1024] | ||||
|             if hasattr(os, "sendfile"): | ||||
|                 use_sendfile = not self.args.no_sendfile | ||||
|             use_sendfile = ( | ||||
|                 not self.ssl_suf | ||||
|                 and not self.args.no_sendfile | ||||
|                 and hasattr(os, "sendfile") | ||||
|             ) | ||||
|  | ||||
|         # | ||||
|         # send reply | ||||
| @@ -1058,6 +1095,10 @@ class HttpCli(object): | ||||
|         if not self.args.ed or "dots" not in self.uparam: | ||||
|             vfs_ls = exclude_dotfiles(vfs_ls) | ||||
|  | ||||
|         hidden = [] | ||||
|         if fsroot.endswith(str(os.sep) + ".hist"): | ||||
|             hidden = ["up2k.db", "up2k.snap"] | ||||
|  | ||||
|         dirs = [] | ||||
|         files = [] | ||||
|         for fn in vfs_ls: | ||||
| @@ -1069,6 +1110,8 @@ class HttpCli(object): | ||||
|  | ||||
|             if fn in vfs_virt: | ||||
|                 fspath = vfs_virt[fn].realpath | ||||
|             elif fn in hidden: | ||||
|                 continue | ||||
|             else: | ||||
|                 fspath = fsroot + "/" + fn | ||||
|  | ||||
|   | ||||
| @@ -3,10 +3,15 @@ from __future__ import print_function, unicode_literals | ||||
|  | ||||
| import os | ||||
| import sys | ||||
| import ssl | ||||
| import time | ||||
| import socket | ||||
|  | ||||
| HAVE_SSL = True | ||||
| try: | ||||
|     import ssl | ||||
| except: | ||||
|     HAVE_SSL = False | ||||
|  | ||||
| try: | ||||
|     import jinja2 | ||||
| except ImportError: | ||||
| @@ -75,9 +80,8 @@ class HttpConn(object): | ||||
|     def log(self, msg): | ||||
|         self.log_func(self.log_src, msg) | ||||
|  | ||||
|     def run(self): | ||||
|     def _detect_https(self): | ||||
|         method = None | ||||
|         self.sr = None | ||||
|         if self.cert_path: | ||||
|             try: | ||||
|                 method = self.s.recv(4, socket.MSG_PEEK) | ||||
| @@ -102,16 +106,52 @@ class HttpConn(object): | ||||
|                 self.s.send(b"HTTP/1.1 400 Bad Request\r\n\r\n" + err.encode("utf-8")) | ||||
|                 return | ||||
|  | ||||
|         if method not in [None, b"GET ", b"HEAD", b"POST", b"PUT ", b"OPTI"]: | ||||
|         return method not in [None, b"GET ", b"HEAD", b"POST", b"PUT ", b"OPTI"] | ||||
|  | ||||
|     def run(self): | ||||
|         self.sr = None | ||||
|         if self.args.https_only: | ||||
|             is_https = True | ||||
|         elif self.args.http_only or not HAVE_SSL: | ||||
|             is_https = False | ||||
|         else: | ||||
|             is_https = self._detect_https() | ||||
|  | ||||
|         if is_https: | ||||
|             if self.sr: | ||||
|                 self.log("\033[1;31mTODO: cannot do https in jython\033[0m") | ||||
|                 return | ||||
|  | ||||
|             self.log_src = self.log_src.replace("[36m", "[35m") | ||||
|             try: | ||||
|                 self.s = ssl.wrap_socket( | ||||
|                     self.s, server_side=True, certfile=self.cert_path | ||||
|                 ) | ||||
|                 ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) | ||||
|                 ctx.load_cert_chain(self.cert_path) | ||||
|                 if self.args.ssl_ver: | ||||
|                     ctx.options &= ~self.args.ssl_flags_en | ||||
|                     ctx.options |= self.args.ssl_flags_de | ||||
|                     # print(repr(ctx.options)) | ||||
|  | ||||
|                 if self.args.ssl_log: | ||||
|                     try: | ||||
|                         ctx.keylog_filename = self.args.ssl_log | ||||
|                     except: | ||||
|                         self.log("keylog failed; openssl or python too old") | ||||
|  | ||||
|                 if self.args.ciphers: | ||||
|                     ctx.set_ciphers(self.args.ciphers) | ||||
|  | ||||
|                 self.s = ctx.wrap_socket(self.s, server_side=True) | ||||
|                 if self.args.ssl_dbg and hasattr(self.s, "shared_ciphers"): | ||||
|                     overlap = [y[::-1] for y in self.s.shared_ciphers()] | ||||
|                     lines = [str(x) for x in (["TLS cipher overlap:"] + overlap)] | ||||
|                     self.log("\n".join(lines)) | ||||
|                     for k, v in [ | ||||
|                         ["compression", self.s.compression()], | ||||
|                         ["ALPN proto", self.s.selected_alpn_protocol()], | ||||
|                         ["NPN proto", self.s.selected_npn_protocol()], | ||||
|                     ]: | ||||
|                         self.log("TLS {}: {}".format(k, v or "nah")) | ||||
|  | ||||
|             except Exception as ex: | ||||
|                 em = str(ex) | ||||
|  | ||||
|   | ||||
| @@ -53,15 +53,13 @@ class TcpSrv(object): | ||||
|             srv.bind((ip, port)) | ||||
|             return srv | ||||
|         except (OSError, socket.error) as ex: | ||||
|             if ex.errno == 98: | ||||
|                 raise Exception( | ||||
|                     "\033[1;31mport {} is busy on interface {}\033[0m".format(port, ip) | ||||
|                 ) | ||||
|  | ||||
|             if ex.errno == 99: | ||||
|                 raise Exception( | ||||
|                     "\033[1;31minterface {} does not exist\033[0m".format(ip) | ||||
|                 ) | ||||
|             if ex.errno in [98, 48]: | ||||
|                 e = "\033[1;31mport {} is busy on interface {}\033[0m".format(port, ip) | ||||
|             elif ex.errno in [99, 49]: | ||||
|                 e = "\033[1;31minterface {} does not exist\033[0m".format(ip) | ||||
|             else: | ||||
|                 raise | ||||
|             raise Exception(e) | ||||
|  | ||||
|     def run(self): | ||||
|         for srv in self.srv: | ||||
|   | ||||
| @@ -130,15 +130,19 @@ class Up2k(object): | ||||
|             if db: | ||||
|                 # can be symlink so don't `and d.startswith(top)`` | ||||
|                 excl = set([d for d in tops if d != top]) | ||||
|                 self._build_dir([db, 0], top, excl, top) | ||||
|                 dbw = [db, 0, time.time()] | ||||
|                 self._build_dir(dbw, top, excl, top) | ||||
|                 self._drop_lost(db, top) | ||||
|                 if dbw[1]: | ||||
|                     self.log("up2k", "commit {} new files".format(dbw[1])) | ||||
|  | ||||
|                 db.commit() | ||||
|  | ||||
|     def _build_dir(self, dbw, top, excl, cdir): | ||||
|         try: | ||||
|             inodes = [fsdec(x) for x in os.listdir(fsenc(cdir))] | ||||
|         except Exception as ex: | ||||
|             self.log("up2k", "listdir: " + repr(ex)) | ||||
|             self.log("up2k", "listdir: {} @ [{}]".format(repr(ex), cdir)) | ||||
|             return | ||||
|  | ||||
|         histdir = os.path.join(top, ".hist") | ||||
| @@ -147,7 +151,7 @@ class Up2k(object): | ||||
|             try: | ||||
|                 inf = os.stat(fsenc(abspath)) | ||||
|             except Exception as ex: | ||||
|                 self.log("up2k", "stat: " + repr(ex)) | ||||
|                 self.log("up2k", "stat: {} @ [{}]".format(repr(ex), abspath)) | ||||
|                 continue | ||||
|  | ||||
|             if stat.S_ISDIR(inf.st_mode): | ||||
| @@ -182,15 +186,18 @@ class Up2k(object): | ||||
|                 try: | ||||
|                     hashes = self._hashlist_from_file(abspath) | ||||
|                 except Exception as ex: | ||||
|                     self.log("up2k", "hash: " + repr(ex)) | ||||
|                     self.log("up2k", "hash: {} @ [{}]".format(repr(ex), abspath)) | ||||
|                     continue | ||||
|  | ||||
|                 wark = self._wark_from_hashlist(inf.st_size, hashes) | ||||
|                 self.db_add(dbw[0], wark, rp, inf.st_mtime, inf.st_size) | ||||
|                 dbw[1] += 1 | ||||
|                 if dbw[1] > 1024: | ||||
|                 td = time.time() - dbw[2] | ||||
|                 if dbw[1] > 1024 or td > 60: | ||||
|                     self.log("up2k", "commit {} new files".format(dbw[1])) | ||||
|                     dbw[0].commit() | ||||
|                     dbw[1] = 0 | ||||
|                     dbw[2] = time.time() | ||||
|  | ||||
|     def _drop_lost(self, db, top): | ||||
|         rm = [] | ||||
| @@ -201,7 +208,7 @@ class Up2k(object): | ||||
|                 if not os.path.exists(fsenc(abspath)): | ||||
|                     rm.append(drp) | ||||
|             except Exception as ex: | ||||
|                 self.log("up2k", "stat-rm: " + repr(ex)) | ||||
|                 self.log("up2k", "stat-rm: {} @ [{}]".format(repr(ex), abspath)) | ||||
|  | ||||
|         if not rm: | ||||
|             return | ||||
| @@ -512,8 +519,15 @@ class Up2k(object): | ||||
|         fsz = os.path.getsize(path) | ||||
|         csz = self._get_chunksize(fsz) | ||||
|         ret = [] | ||||
|         last_print = time.time() | ||||
|         with open(path, "rb", 512 * 1024) as f: | ||||
|             while fsz > 0: | ||||
|                 now = time.time() | ||||
|                 td = now - last_print | ||||
|                 if td >= 0.3: | ||||
|                     last_print = now | ||||
|                     print(" {}    \n\033[A".format(fsz), end="") | ||||
|  | ||||
|                 hashobj = hashlib.sha512() | ||||
|                 rem = min(csz, fsz) | ||||
|                 fsz -= rem | ||||
|   | ||||
| @@ -108,7 +108,7 @@ def ren_open(fname, *args, **kwargs): | ||||
|         with open(fname, *args, **kwargs) as f: | ||||
|             yield {"orz": [f, fname]} | ||||
|             return | ||||
|      | ||||
|  | ||||
|     orig_name = fname | ||||
|     bname = fname | ||||
|     ext = "" | ||||
| @@ -632,10 +632,10 @@ def sendfile_kern(lower, upper, f, s): | ||||
|         except Exception as ex: | ||||
|             # print("sendfile: " + repr(ex)) | ||||
|             n = 0 | ||||
|          | ||||
|  | ||||
|         if n <= 0: | ||||
|             return upper - ofs | ||||
|          | ||||
|  | ||||
|         ofs += n | ||||
|         # print("sendfile: ok, sent {} now, {} total, {} remains".format(n, ofs - lower, upper - ofs)) | ||||
|  | ||||
| @@ -718,6 +718,22 @@ def py_desc(): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def align_tab(lines): | ||||
|     rows = [] | ||||
|     ncols = 0 | ||||
|     for ln in lines: | ||||
|         row = [x for x in ln.split(" ") if x] | ||||
|         ncols = max(ncols, len(row)) | ||||
|         rows.append(row) | ||||
|  | ||||
|     lens = [0] * ncols | ||||
|     for row in rows: | ||||
|         for n, col in enumerate(row): | ||||
|             lens[n] = max(lens[n], len(col)) | ||||
|  | ||||
|     return ["".join(x.ljust(y + 2) for x, y in zip(row, lens)) for row in rows] | ||||
|  | ||||
|  | ||||
| class Pebkac(Exception): | ||||
|     def __init__(self, code, msg=None): | ||||
|         super(Pebkac, self).__init__(msg or HTTPCODE[code]) | ||||
|   | ||||
| @@ -219,6 +219,10 @@ function up2k_init(have_crypto) { | ||||
|             "hash": [], | ||||
|             "handshake": [], | ||||
|             "upload": [] | ||||
|         }, | ||||
|         "bytes": { | ||||
|             "hashed": 0, | ||||
|             "uploaded": 0 | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @@ -357,8 +361,11 @@ function up2k_init(have_crypto) { | ||||
|     } | ||||
|  | ||||
|     function hashing_permitted() { | ||||
|         var lim = multitask ? 1 : 0; | ||||
|         return handshakes_permitted() && lim >= | ||||
|         if (multitask) { | ||||
|             var ahead = st.bytes.hashed - st.bytes.uploaded; | ||||
|             return ahead < 1024 * 1024 * 128; | ||||
|         } | ||||
|         return handshakes_permitted() && 0 == | ||||
|             st.todo.handshake.length + | ||||
|             st.busy.handshake.length; | ||||
|     } | ||||
| @@ -512,6 +519,7 @@ function up2k_init(have_crypto) { | ||||
|  | ||||
|         var t = st.todo.hash.shift(); | ||||
|         st.busy.hash.push(t); | ||||
|         st.bytes.hashed += t.size; | ||||
|         t.t1 = new Date().getTime(); | ||||
|  | ||||
|         var nchunk = 0; | ||||
| @@ -752,6 +760,7 @@ function up2k_init(have_crypto) { | ||||
|             xhr.onload = function (xev) { | ||||
|                 if (xhr.status == 200) { | ||||
|                     prog(t.n, npart, col_uploaded); | ||||
|                     st.bytes.uploaded += cdr - car; | ||||
|                     st.busy.upload.splice(st.busy.upload.indexOf(upt), 1); | ||||
|                     t.postlist.splice(t.postlist.indexOf(npart), 1); | ||||
|                     if (t.postlist.length == 0) { | ||||
|   | ||||
| @@ -3,7 +3,8 @@ | ||||
|         href="#" data-dest="up2k">up2k</a><i></i><a | ||||
|         href="#" data-dest="bup">bup</a><i></i><a | ||||
|         href="#" data-dest="mkdir">mkdir</a><i></i><a | ||||
|         href="#" data-dest="new_md">new.md</a></div> | ||||
|         href="#" data-dest="new_md">new.md</a><i></i><a | ||||
|         href="#" data-dest="msg">msg</a></div> | ||||
|  | ||||
|     <div id="op_bup" class="opview opbox act"> | ||||
|         <div id="u2err"></div> | ||||
| @@ -30,6 +31,13 @@ | ||||
|         </form> | ||||
|     </div> | ||||
|  | ||||
|     <div id="op_msg" class="opview opbox"> | ||||
|         <form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="/{{ vdir }}"> | ||||
|             <input type="text" name="msg" size="30"> | ||||
|             <input type="submit" value="send"> | ||||
|         </form> | ||||
|     </div> | ||||
|  | ||||
|     <div id="op_up2k" class="opview"> | ||||
|         <form id="u2form" method="post" enctype="multipart/form-data" onsubmit="return false;"></form> | ||||
|  | ||||
|   | ||||
| @@ -62,28 +62,32 @@ cd sfx | ||||
| 	)/pe-copyparty" | ||||
|  | ||||
| 	echo "repack of files in $old" | ||||
| 	cp -pR "$old/"*{jinja2,copyparty} . | ||||
| 	mv {x.,}jinja2 2>/dev/null || true | ||||
| 	cp -pR "$old/"*{dep-j2,copyparty} . | ||||
| } | ||||
|  | ||||
| [ $repack ] || { | ||||
| 	echo collecting jinja2 | ||||
| 	f="../build/Jinja2-2.6.tar.gz" | ||||
| 	f="../build/Jinja2-2.11.3.tar.gz" | ||||
| 	[ -e "$f" ] || | ||||
| 		(url=https://files.pythonhosted.org/packages/25/c8/212b1c2fd6df9eaf536384b6c6619c4e70a3afd2dffdd00e5296ffbae940/Jinja2-2.6.tar.gz; | ||||
| 		(url=https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz; | ||||
| 		wget -O$f "$url" || curl -L "$url" >$f) | ||||
|  | ||||
| 	tar -zxf $f | ||||
| 	mv Jinja2-*/jinja2 . | ||||
| 	rm -rf Jinja2-* jinja2/testsuite jinja2/_markupsafe/tests.py jinja2/_stringdefs.py | ||||
| 	mv Jinja2-*/src/jinja2 . | ||||
| 	rm -rf Jinja2-* | ||||
| 	 | ||||
| 	f=jinja2/lexer.py | ||||
| 	sed -r '/.*föö.*/    raise SyntaxError/' <$f >t | ||||
| 	tmv $f | ||||
| 	 | ||||
| 	f=jinja2/_markupsafe/_constants.py | ||||
| 	awk '!/: [0-9]+,?$/ || /(amp|gt|lt|quot|apos|nbsp).:/' <$f >t | ||||
| 	tmv $f | ||||
| 	echo collecting markupsafe | ||||
| 	f="../build/MarkupSafe-1.1.1.tar.gz" | ||||
| 	[ -e "$f" ] || | ||||
| 		(url=https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz; | ||||
| 		wget -O$f "$url" || curl -L "$url" >$f) | ||||
|  | ||||
| 	tar -zxf $f | ||||
| 	mv MarkupSafe-*/src/markupsafe . | ||||
| 	rm -rf MarkupSafe-* markupsafe/_speedups.c | ||||
|  | ||||
| 	mkdir dep-j2/ | ||||
| 	mv {markupsafe,jinja2} dep-j2/ | ||||
|  | ||||
| 	# msys2 tar is bad, make the best of it | ||||
| 	echo collecting source | ||||
| @@ -165,6 +169,15 @@ done | ||||
| 	sed -r '/edit2">edit \(fancy/d' <$f >t && tmv "$f" | ||||
| } | ||||
|  | ||||
| find | grep -E '\.py$' | | ||||
|   grep -vE '__version__' | | ||||
|   tr '\n' '\0' | | ||||
|   xargs -0 python ../scripts/uncomment.py | ||||
|  | ||||
| f=dep-j2/jinja2/constants.py | ||||
| awk '/^LOREM_IPSUM_WORDS/{o=1;print "LOREM_IPSUM_WORDS = u\"a\"";next} !o; /"""/{o=0}' <$f >t | ||||
| tmv "$f" | ||||
|  | ||||
| # up2k goes from 28k to 22k laff | ||||
| echo entabbening | ||||
| find | grep -E '\.(js|css|html|py)$' | while IFS= read -r f; do | ||||
| @@ -177,7 +190,7 @@ args=(--owner=1000 --group=1000) | ||||
| [ "$OSTYPE" = msys ] && | ||||
| 	args=() | ||||
|  | ||||
| tar -cf tar "${args[@]}" --numeric-owner copyparty jinja2 | ||||
| tar -cf tar "${args[@]}" --numeric-owner copyparty dep-j2 | ||||
|  | ||||
| echo compressing tar | ||||
| # detect best level; bzip2 -7 is usually better than -9 | ||||
|   | ||||
							
								
								
									
										153
									
								
								scripts/sfx.py
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								scripts/sfx.py
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import print_function, unicode_literals | ||||
|  | ||||
| import re, os, sys, time, shutil, signal, tarfile, hashlib, platform, tempfile | ||||
| import os, sys, time, shutil, signal, tarfile, hashlib, platform, tempfile | ||||
| import subprocess as sp | ||||
|  | ||||
| """ | ||||
| @@ -202,93 +202,6 @@ def u8(gen): | ||||
|             yield s | ||||
|  | ||||
|  | ||||
| def get_py_win(ret): | ||||
|     tops = [] | ||||
|     p = str(os.getenv("LocalAppdata")) | ||||
|     if p: | ||||
|         tops.append(os.path.join(p, "Programs", "Python")) | ||||
|  | ||||
|     progfiles = {} | ||||
|     for p in ["ProgramFiles", "ProgramFiles(x86)"]: | ||||
|         p = str(os.getenv(p)) | ||||
|         if p: | ||||
|             progfiles[p] = 1 | ||||
|             # 32bit apps get x86 for both | ||||
|             if p.endswith(" (x86)"): | ||||
|                 progfiles[p[:-6]] = 1 | ||||
|  | ||||
|     tops += list(progfiles.keys()) | ||||
|  | ||||
|     for sysroot in [me, sys.executable]: | ||||
|         sysroot = sysroot[:3].upper() | ||||
|         if sysroot[1] == ":" and sysroot not in tops: | ||||
|             tops.append(sysroot) | ||||
|  | ||||
|     # $WIRESHARK_SLOGAN | ||||
|     for top in tops: | ||||
|         try: | ||||
|             for name1 in u8(sorted(os.listdir(top), reverse=True)): | ||||
|                 if name1.lower().startswith("python"): | ||||
|                     path1 = os.path.join(top, name1) | ||||
|                     try: | ||||
|                         for name2 in u8(os.listdir(path1)): | ||||
|                             if name2.lower() == "python.exe": | ||||
|                                 path2 = os.path.join(path1, name2) | ||||
|                                 ret[path2.lower()] = path2 | ||||
|                     except: | ||||
|                         pass | ||||
|         except: | ||||
|             pass | ||||
|  | ||||
|  | ||||
| def get_py_nix(ret): | ||||
|     ptn = re.compile(r"^(python|pypy)[0-9\.-]*$") | ||||
|     for bindir in os.getenv("PATH").split(":"): | ||||
|         if not bindir: | ||||
|             next | ||||
|  | ||||
|         try: | ||||
|             for fn in u8(os.listdir(bindir)): | ||||
|                 if ptn.match(fn): | ||||
|                     fn = os.path.join(bindir, fn) | ||||
|                     ret[fn.lower()] = fn | ||||
|         except: | ||||
|             pass | ||||
|  | ||||
|  | ||||
| def read_py(binp): | ||||
|     cmd = [ | ||||
|         binp, | ||||
|         "-c", | ||||
|         "import sys; sys.stdout.write(' '.join(str(x) for x in sys.version_info)); import jinja2", | ||||
|     ] | ||||
|     p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE) | ||||
|     ver, _ = p.communicate() | ||||
|     ver = ver.decode("utf-8").split(" ")[:3] | ||||
|     ver = [int(x) if x.isdigit() else 0 for x in ver] | ||||
|     return ver, p.returncode == 0 | ||||
|  | ||||
|  | ||||
| def get_pys(): | ||||
|     ver, chk = read_py(sys.executable) | ||||
|     if chk or PY2: | ||||
|         return [[chk, ver, sys.executable]] | ||||
|  | ||||
|     hits = {sys.executable.lower(): sys.executable} | ||||
|     if platform.system() == "Windows": | ||||
|         get_py_win(hits) | ||||
|     else: | ||||
|         get_py_nix(hits) | ||||
|  | ||||
|     ret = [] | ||||
|     for binp in hits.values(): | ||||
|         ver, chk = read_py(binp) | ||||
|         ret.append([chk, ver, binp]) | ||||
|         msg("\t".join(str(x) for x in ret[-1])) | ||||
|  | ||||
|     return ret | ||||
|  | ||||
|  | ||||
| def yieldfile(fn): | ||||
|     with open(fn, "rb") as f: | ||||
|         for block in iter(lambda: f.read(64 * 1024), b""): | ||||
| @@ -440,12 +353,11 @@ def confirm(): | ||||
|         pass | ||||
|  | ||||
|  | ||||
| def run(tmp, py): | ||||
| def run(tmp, j2ver): | ||||
|     global cpp | ||||
|  | ||||
|     msg("OK") | ||||
|     msg("will use:", py) | ||||
|     msg("bound to:", tmp) | ||||
|     msg("jinja2:", j2ver or "bundled") | ||||
|     msg("sfxdir:", tmp) | ||||
|  | ||||
|     # "systemd-tmpfiles-clean.timer"?? HOW do you even come up with this shit | ||||
|     try: | ||||
| @@ -457,24 +369,20 @@ def run(tmp, py): | ||||
|     except: | ||||
|         pass | ||||
|  | ||||
|     fp_py = os.path.join(tmp, "py") | ||||
|     try: | ||||
|         with open(fp_py, "wb") as f: | ||||
|             f.write(py.encode("utf-8") + b"\n") | ||||
|     except: | ||||
|         pass | ||||
|     ld = [tmp, os.path.join(tmp, "dep-j2")] | ||||
|     if j2ver: | ||||
|         del ld[-1] | ||||
|  | ||||
|     # avoid loading ./copyparty.py | ||||
|     cmd = [ | ||||
|         py, | ||||
|         "-c", | ||||
|         'import sys, runpy; sys.path.insert(0, r"' | ||||
|         + tmp | ||||
|         + '"); runpy.run_module("copyparty", run_name="__main__")', | ||||
|     ] + list(sys.argv[1:]) | ||||
|     cmd = ( | ||||
|         "import sys, runpy; " | ||||
|         + "".join(['sys.path.insert(0, r"' + x + '"); ' for x in ld]) | ||||
|         + 'runpy.run_module("copyparty", run_name="__main__")' | ||||
|     ) | ||||
|     cmd = [sys.executable, "-c", cmd] + list(sys.argv[1:]) | ||||
|  | ||||
|     cmd = [str(x) for x in cmd] | ||||
|     msg("\n", cmd, "\n") | ||||
|     cpp = sp.Popen(str(x) for x in cmd) | ||||
|     cpp = sp.Popen(cmd) | ||||
|     try: | ||||
|         cpp.wait() | ||||
|     except: | ||||
| @@ -494,7 +402,6 @@ def bye(sig, frame): | ||||
| def main(): | ||||
|     sysver = str(sys.version).replace("\n", "\n" + " " * 18) | ||||
|     pktime = time.strftime("%Y-%m-%d, %H:%M:%S", time.gmtime(STAMP)) | ||||
|     os.system("") | ||||
|     msg() | ||||
|     msg("   this is: copyparty", VER) | ||||
|     msg(" packed at:", pktime, "UTC,", STAMP) | ||||
| @@ -526,33 +433,13 @@ def main(): | ||||
|     signal.signal(signal.SIGTERM, bye) | ||||
|  | ||||
|     tmp = unpack() | ||||
|     fp_py = os.path.join(tmp, "py") | ||||
|     if os.path.exists(fp_py): | ||||
|         with open(fp_py, "rb") as f: | ||||
|             py = f.read().decode("utf-8").rstrip() | ||||
|  | ||||
|         return run(tmp, py) | ||||
|     try: | ||||
|         from jinja2 import __version__ as j2ver | ||||
|     except: | ||||
|         j2ver = None | ||||
|  | ||||
|     pys = get_pys() | ||||
|     pys.sort(reverse=True) | ||||
|     j2, ver, py = pys[0] | ||||
|     if j2: | ||||
|         try: | ||||
|             os.rename(os.path.join(tmp, "jinja2"), os.path.join(tmp, "x.jinja2")) | ||||
|         except: | ||||
|             pass | ||||
|  | ||||
|         return run(tmp, py) | ||||
|  | ||||
|     msg("\n  could not find jinja2; will use py2 + the bundled version\n") | ||||
|     for _, ver, py in pys: | ||||
|         if ver > [2, 7] and ver < [3, 0]: | ||||
|             return run(tmp, py) | ||||
|  | ||||
|     m = "\033[1;31m\n\n\ncould not find a python with jinja2 installed; please do one of these:\n\n  pip install --user jinja2\n\n  install python2\n\n\033[0m" | ||||
|     msg(m) | ||||
|     confirm() | ||||
|     sys.exit(1) | ||||
|     return run(tmp, j2ver) | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|   | ||||
							
								
								
									
										77
									
								
								scripts/uncomment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								scripts/uncomment.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| #!/usr/bin/env python | ||||
| # coding: utf-8 | ||||
| from __future__ import print_function, unicode_literals | ||||
|  | ||||
| import io | ||||
| import sys | ||||
| import tokenize | ||||
|  | ||||
|  | ||||
| def uncomment(fpath): | ||||
|     """ modified https://stackoverflow.com/a/62074206 """ | ||||
|  | ||||
|     with open(fpath, "rb") as f: | ||||
|         orig = f.read().decode("utf-8") | ||||
|  | ||||
|     out = "" | ||||
|     for ln in orig.split("\n"): | ||||
|         if not ln.startswith("#"): | ||||
|             break | ||||
|  | ||||
|         out += ln + "\n" | ||||
|  | ||||
|     io_obj = io.StringIO(orig) | ||||
|     prev_toktype = tokenize.INDENT | ||||
|     last_lineno = -1 | ||||
|     last_col = 0 | ||||
|     for tok in tokenize.generate_tokens(io_obj.readline): | ||||
|         # print(repr(tok)) | ||||
|         token_type = tok[0] | ||||
|         token_string = tok[1] | ||||
|         start_line, start_col = tok[2] | ||||
|         end_line, end_col = tok[3] | ||||
|  | ||||
|         if start_line > last_lineno: | ||||
|             last_col = 0 | ||||
|  | ||||
|         if start_col > last_col: | ||||
|             out += " " * (start_col - last_col) | ||||
|  | ||||
|         is_legalese = ( | ||||
|             "copyright" in token_string.lower() or "license" in token_string.lower() | ||||
|         ) | ||||
|  | ||||
|         if token_type == tokenize.STRING: | ||||
|             if ( | ||||
|                 prev_toktype != tokenize.INDENT | ||||
|                 and prev_toktype != tokenize.NEWLINE | ||||
|                 and start_col > 0 | ||||
|                 or is_legalese | ||||
|             ): | ||||
|                 out += token_string | ||||
|             else: | ||||
|                 out += '"a"' | ||||
|         elif token_type != tokenize.COMMENT or is_legalese: | ||||
|             out += token_string | ||||
|  | ||||
|         prev_toktype = token_type | ||||
|         last_lineno = end_line | ||||
|         last_col = end_col | ||||
|  | ||||
|     # out = "\n".join(x for x in out.splitlines() if x.strip()) | ||||
|  | ||||
|     with open(fpath, "wb") as f: | ||||
|         f.write(out.encode("utf-8")) | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     print("uncommenting", end="") | ||||
|     for f in sys.argv[1:]: | ||||
|         print(".", end="") | ||||
|         uncomment(f) | ||||
|  | ||||
|     print("k") | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
							
								
								
									
										6
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								setup.py
									
									
									
									
									
								
							| @@ -2,10 +2,8 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import print_function | ||||
|  | ||||
| import io | ||||
| import os | ||||
| import sys | ||||
| from glob import glob | ||||
| from shutil import rmtree | ||||
|  | ||||
| setuptools_available = True | ||||
| @@ -49,7 +47,7 @@ with open(here + "/README.md", "rb") as f: | ||||
| about = {} | ||||
| if not VERSION: | ||||
|     with open(os.path.join(here, NAME, "__version__.py"), "rb") as f: | ||||
|         exec(f.read().decode("utf-8").split("\n\n", 1)[1], about) | ||||
|         exec (f.read().decode("utf-8").split("\n\n", 1)[1], about) | ||||
| else: | ||||
|     about["__version__"] = VERSION | ||||
|  | ||||
| @@ -110,13 +108,13 @@ args = { | ||||
|         "Programming Language :: Python :: 2", | ||||
|         "Programming Language :: Python :: 2.7", | ||||
|         "Programming Language :: Python :: 3", | ||||
|         "Programming Language :: Python :: 3.2", | ||||
|         "Programming Language :: Python :: 3.3", | ||||
|         "Programming Language :: Python :: 3.4", | ||||
|         "Programming Language :: Python :: 3.5", | ||||
|         "Programming Language :: Python :: 3.6", | ||||
|         "Programming Language :: Python :: 3.7", | ||||
|         "Programming Language :: Python :: 3.8", | ||||
|         "Programming Language :: Python :: 3.9", | ||||
|         "Programming Language :: Python :: Implementation :: CPython", | ||||
|         "Programming Language :: Python :: Implementation :: PyPy", | ||||
|         "Environment :: Console", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user