share:基于 HTTP 协议的文件分享工具
OneFile
源码
#!/bin/env python3
import sys
import signal
import os
import argparse
import functools
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler, HTTPStatus
from http import cookies
from urllib import parse
import html
import mimetypes
import shutil
import base64
import time
import io
import stat
import re
import socket
class ShareServer(ThreadingHTTPServer):
    def __init__(self, *args, **kwargs):
        if is_windows():
            self._print_error = self._print_error_windows
        else:
            self._print_error = self._print_error_unix
        super().__init__(*args, **kwargs)
    def handle_error(self, request, client_address):
        year, month, day, hh, mm, ss, x, y, z = time.localtime()
        t, value, traceback = sys.exc_info()
        self._print_error(f'{year:04}/{month:02}/{day:02} {hh:02}:{mm:02}:{ss:02} - {client_address[0]}:{client_address[1]} - {t.__name__}: {value}')
    def _print_error_windows(self, msg):
        sys.stderr.write(f'{msg}\n')
    def _print_error_unix(self, msg):
        sys.stderr.write(f'\033[33m{msg}\033[0m\n')
class BaseHandler(BaseHTTPRequestHandler):
    protocol_version = 'HTTP/1.1'
    ico = base64.b64decode('AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAAAABgAAAAAQAgAAAAAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPAFxszwhcbM8U3GzPGZxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8ZnGzPFNxszwhcbM8AQAAAAAAAAAAAAAAAAAAAAAAAAAAcbM8B3GzPGxxszzccbM8/HGzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPPxxszzccbM8bHGzPAcAAAAAAAAAAAAAAAAAAAAAcbM8bHGzPPZxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM89nGzPGwAAAAAAAAAAAAAAABxszwhcbM823GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPNtxszwhAAAAAAAAAABxszxTcbM8+3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPPtxszxTAAAAAAAAAABxszxmcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxmAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8+3GzPOFxszzHcbM8x3GzPOFxszz8cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszzccbM8bHGzPCJxszwMcbM8DXGzPCJxszxscbM83XGzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPNNxszw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcbM8OHGzPNRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM88XGzPE0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPE5xszzycbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8s3GzPAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPAlxszy0cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8fQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszx+cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8bgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszxvcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz+cbM8cwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszyFcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPOFxszx4cbM8FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPBBxszzCcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz0cbM8onGzPC9xszwBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPGZxszz5cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP5xszzccbM8mXGzPHFxszxxcbM8mHGzPNxxszz+cbM8/XGzPMdxszxScbM8CAAAAAAAAAAAcbM8AXGzPDJxszxDcbM8BAAAAAAAAAAAAAAAAAAAAABxszwDcbM8V3GzPOZxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8+3GzPKBxszwkcbM8AQAAAAAAAAAAcbM8AXGzPCVxszyTcbM8fHGzPBcAAAAAAAAAAAAAAABxszwXcbM8enGzPORxszzvcbM8lXGzPEJxszwkcbM8JHGzPENxszyVcbM873GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8oHGzPA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszwEcbM8AgAAAAAAAAAAcbM8B3GzPFFxszzGcbM8/XGzPP9xszz/cbM8/3GzPPVxszzlcbM85XGzPPVxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszzdcbM8JwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPAFxszwucbM8oXGzPPRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszyacbM8AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPFRxszzicbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPHRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPHRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszyacbM8AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPFNxszzicbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszzdcbM8JwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPAFxszwucbM8oHGzPPNxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8n3GzPA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszwEcbM8AgAAAAAAAAAAcbM8B3GzPFFxszzGcbM8/XGzPP9xszz/cbM8/3GzPPVxszzlcbM85XGzPPVxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8+3GzPKBxszwkcbM8AQAAAAAAAAAAcbM8AXGzPCVxszyTcbM8fHGzPBcAAAAAAAAAAAAAAABxszwWcbM8enGzPORxszzvcbM8lXGzPEJxszwkcbM8JHGzPENxszyVcbM873GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP5xszzccbM8mXGzPHFxszxxcbM8mHGzPNxxszz+cbM8/XGzPMdxszxScbM8CAAAAAAAAAAAcbM8AXGzPDJxszxCcbM8BAAAAAAAAAAAAAAAAAAAAABxszwDcbM8VnGzPOZxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz0cbM8onGzPC9xszwBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPGZxszz5cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPOJxszx4cbM8FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPBBxszzCcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz+cbM8cwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszyFcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8bgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszxvcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8fQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxszx+cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8s3GzPAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPAlxszy0cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM88XGzPE0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPE5xszzycbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPNNxszw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcbM8OHGzPNRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszzccbM8bHGzPCJxszwMcbM8DXGzPCJxszxscbM83XGzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8+3GzPOFxszzHcbM8x3GzPOFxszz8cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxncbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxnAAAAAAAAAABxszxmcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszxmAAAAAAAAAABxszxTcbM8+3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPPtxszxTAAAAAAAAAABxszwhcbM823GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPNtxszwhAAAAAAAAAAAAAAAAcbM8bHGzPPZxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM89nGzPGwAAAAAAAAAAAAAAAAAAAAAcbM8B3GzPGxxszzccbM8/HGzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPPxxszzccbM8bHGzPAcAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPAFxszwhcbM8U3GzPGZxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8Z3GzPGdxszxncbM8ZnGzPFNxszwhcbM8AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAA////////AADwAAAAAA8AAOAAAAAABwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAfgAwAAwAAAD/ADAADAAAAf+AMAAMAAAB/4AwAAwAAAP/wDAADAAAA//AMAAMAAAD/4AwAAwAAAf/gDAADAAAD/+AMAAMABg//wAwAAwAfv48ADAADAD/+AAAMAAMAf/gAAAwAAwB/8AAADAADAP/wAAAMAAMA//AAAAwAAwB/8AAADAADAH/4AAAMAAMAP/4AAAwAAwAfv48ADAADAAYP/8AMAAMAAAP/4AwAAwAAAf/gDAADAAAA/+AMAAMAAAD/8AwAAwAAAP/wDAADAAAAf+AMAAMAAAB/4AwAAwAAAD/ADAADAAAAH4AMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADgAAAAAAcAAPAAAAAADwAA////////AAD///////8AACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPAFxszwOcbM8GXGzPBlxszwZcbM8GXGzPBlxszwZcbM8GXGzPBlxszwZcbM8GXGzPBlxszwZcbM8GXGzPBlxszwZcbM8GXGzPBlxszwZcbM8GXGzPBlxszwZcbM8GXGzPBlxszwZcbM8DnGzPAEAAAAAAAAAAAAAAABxszwHcbM8ZHGzPMFxszzUcbM81HGzPNRxszzUcbM81HGzPNRxszzUcbM81HGzPNRxszzUcbM81HGzPNRxszzUcbM81HGzPNRxszzUcbM81HGzPNRxszzUcbM81HGzPNRxszzUcbM81HGzPNRxszzBcbM8ZHGzPAcAAAAAAAAAAHGzPGRxszz2cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz2cbM8ZAAAAABxszwNcbM8v3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszy/cbM8DXGzPBhxszzUcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPNRxszwYcbM8GXGzPNRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPPlxszzdcbM80HGzPOdxszz+cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM81HGzPBlxszwZcbM81HGzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszzjcbM8Z3GzPBxxszwRcbM8K3GzPJRxszz4cbM8/3GzPP9xszz/cbM8/3GzPP9xszzUcbM8GXGzPBlxszzUcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM883GzPFcAAAAAAAAAAAAAAAAAAAAAcbM8CXGzPJhxszz/cbM8/3GzPP9xszz/cbM8/3GzPNRxszwZcbM8GXGzPNRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3Gz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PP9xszz/cbM8/3GzPP9xszz/cbM8nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHGzPBpxszzbcbM8/3GzPP9xszz/cbM8/3GzPNRxszwZcbM8GXGzPNRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszy8cbM8CwAAAAAAAAAAAAAAAAAAAAAAAAAAcbM8NHGzPO1xszz/cbM8/3GzPP9xszz/cbM81HGzPBlxszwZcbM81HGzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPPNxszxXAAAAAAAAAAAAAAAAAAAAAHGzPAlxszyYcbM8/3GzPP9xszz/cbM8/3GzPP9xszzUcbM8GXGzPBlxszzUcbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPONxszxncbM8HHGzPBFxszwrcbM8lHGzPPhxszz/cbM8/3GzPP9xszz/cbM8/3GzPNRxszwZcbM8GXGzPNRxszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPPlxszzdcbM80HGzPOdxszz+cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM81HGzPBlxszwYcbM81HGzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszzUcbM8GHGzPA1xszy/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPL9xszwNAAAAAHGzPGRxszz2cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3GzPP9xszz/cbM8/3Gz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')
    def __init__(self, *args, password=None):
        self._hostname = socket.gethostname()
        self._password = password
        super().__init__(*args)
    def do_GET(self):
        if self.path == '/favicon.ico':
            self.send_response(HTTPStatus.OK)
            self.send_content_length(len(BaseHandler.ico))
            self.send_content_type('image/x-icon')
            self.end_headers()
            self.wfile.write(self.ico)
            return
        if not self._password or self._validate_password():
            self.do_get()
            return
        self.respond_ok(self._build_html_for_password())
    def do_POST(self):
        if not self._password or self._validate_password():
            self.do_post()
            return
        content_length = self.headers['Content-Length']
        if not content_length or not content_length.isdecimal():
            self.respond_bad_request()
            return
        content_length = int(content_length)
        if content_length > 100:
            self.respond_bad_request()
            return
        data = self.rfile.read(content_length).decode()
        data = parse.unquote_plus(data)
        if data != f'password={self._password}':
            self.respond_redirect(self.path)
            return
        self.respond_redirect_cookie(self.path, f'password={parse.quote_plus(self._password)}; path=/')
    def do_get(self):
        self.respond_method_not_allowed()
    def do_post(self):
        self.respond_method_not_allowed()
    def do_multipart(self, save_dir, redirect_location):
        content_length = self.headers['Content-Length']
        if not content_length or not content_length.isdecimal():
            self.respond_bad_request()
            return
        content_length = int(content_length)
        if not self._has_freespace(content_length):
            self.respond_internal_server_error()
            return
        content_type = self.headers['Content-Type']
        if not content_type:
            self.respond_bad_request()
            return
        boundary = self._parse_boundary(content_type)
        if not boundary:
            self.respond_bad_request()
            return
        try:
            parser = MultipartParser(self.rfile, boundary, content_length)
            while parser.has_next():
                name = parser.next_name()
                if name != 'file':
                    self.respond_bad_request()
                    return
                filename = parser.next_filename()
                if not filename:
                    self.respond_bad_request()
                    return
                os.makedirs(save_dir, exist_ok=True)
                save_dir = save_dir.rstrip('/\\')
                with open(f'{save_dir}/{filename}', 'wb') as f:
                    parser.write_next_to(f)
        except MultipartError:
            self.respond_bad_request()
        except PermissionError:
            self.respond_forbidden()
        else:
            self.respond_redirect(redirect_location)
    def _validate_password(self):
        cookie = cookies.SimpleCookie(self.headers['Cookie'])
        password = cookie.get('password')
        return password and parse.unquote_plus(password.value) == self._password
    def _build_html_for_password(self):
        builder = HtmlBuilder()
        builder.start_head()
        builder.start_title()
        builder.append(self._hostname)
        builder.end_title()
        builder.start_style()
        builder.append('.container{height: 80%; display: flex; align-items: center; justify-content: center;}')
        builder.end_style()
        builder.end_head()
        builder.start_body()
        builder.append('<div class="container">')
        builder.append(f'<form action="{self.path}" method="post">')
        builder.append('<input name="password" type="password" placeholder="Enter your password" minlength="3" required autofocus>')
        builder.append(' <input type="submit">')
        builder.append('</form>')
        builder.append('</div>')
        builder.end_body()
        return builder.build()
    def _has_freespace(self, need_size):
        path = self._dir
        if is_windows():
            path, _ = os.path.splitdrive(path)
        total, used, free = shutil.disk_usage(path)
        return free - need_size >= 1073741824
    def _parse_boundary(self, content_type):
        parts = content_type.split('; ')
        if len(parts) != 2:
            return None
        form_data, boundary = parts
        if form_data != 'multipart/form-data':
            return None
        parts = boundary.split('=')
        if len(parts) != 2:
            return None
        key, value = parts
        if key != 'boundary':
            return None
        return value
    def send_content_length(self, content_length):
        self.send_header('Content-Length', str(content_length))
    def send_content_type(self, content_type):
        self.send_header('Content-Type', content_type)
    def send_location(self, location):
        self.send_header('Location', location)
    def send_accept_ranges(self):
        self.send_header('Accept-Ranges', 'bytes')
    def send_content_range(self, start, end, filesize):
        self.send_header('Content-Range', f'bytes {start}-{end}/{filesize}')
    def send_content_disposition(self, filename):
        filename = parse.quote(filename)
        self.send_header('Content-Disposition', f'attachment;filename="{filename}"')
    def send_cookie(self, cookie):
        self.send_header('Set-Cookie', cookie)
    def respond_ok(self, html):
        self.send_response(HTTPStatus.OK)
        response = html.encode()
        self.send_content_length(len(response))
        self.send_content_type('text/html; charset=utf-8')
        self.end_headers()
        self.wfile.write(response)
    def respond_redirect(self, location):
        self.send_response(HTTPStatus.SEE_OTHER)
        self.send_content_length(0)
        self.send_location(location)
        self.end_headers()
    def respond_redirect_cookie(self, location, cookie):
        self.send_response(HTTPStatus.SEE_OTHER)
        self.send_content_length(0)
        self.send_location(location)
        self.send_cookie(cookie)
        self.end_headers()
    def respond_range_not_satisfiable(self):
        self.close_connection = True
        self.send_response(HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
        self.send_content_length(0)
        self.end_headers()
    def respond_bad_request(self):
        self.close_connection = True
        self.send_response(HTTPStatus.BAD_REQUEST)
        self.send_content_length(0)
        self.end_headers()
    def respond_forbidden(self):
        self.close_connection = True
        self.send_response(HTTPStatus.FORBIDDEN)
        self.send_content_length(0)
        self.end_headers()
    def respond_not_found(self):
        self.close_connection = True
        self.send_response(HTTPStatus.NOT_FOUND)
        self.send_content_length(0)
        self.end_headers()
    def respond_method_not_allowed(self):
        self.close_connection = True
        self.send_response(HTTPStatus.METHOD_NOT_ALLOWED)
        self.send_content_length(0)
        self.end_headers()
    def respond_internal_server_error(self):
        self.close_connection = True
        self.send_response(HTTPStatus.INTERNAL_SERVER_ERROR)
        self.send_content_length(0)
        self.end_headers()
    def log_request(self, code, size=None):
        if isinstance(code, HTTPStatus):
            code = code.value
        self.log_message('%s %s %s', self.command, str(code), parse.unquote(self.path))
    def log_message(self, format, *args):
        year, month, day, hh, mm, ss, x, y, z = time.localtime()
        t = f'{year:04}/{month:02}/{day:02} {hh:02}:{mm:02}:{ss:02}'
        sys.stderr.write('%s - %s:%s - %s\n' % (t, self.client_address[0], self.client_address[1], format % args))
class BaseFileShareHandler(BaseHandler):
    def __init__(self, *args, upload=False, **kwargs):
        self._upload = upload
        self._ua_prefixes = {'curl', 'Wget', 'wget2', 'aria2', 'Axel'}
        if is_windows():
            self.is_hidden = self._is_hidden_windows
        else:
            self.is_hidden = self._is_hidden_unix
        super().__init__(*args, **kwargs)
    def split_path(self, path):
        parts = path.split('?', 1)
        path = parts[0]
        params = {}
        if len(parts) > 1:
            query = parts[1]
            for q in query.split('&'):
                if q:
                    words = q.split('=')
                    if len(words) == 2:
                        params[words[0]] = words[1]
        return (path, params)
    def respond_for_file(self, file):
        include_content_disposition = self._is_from_commandline()
        try:
            f = open(file, 'rb')
        except PermissionError:
            self.respond_forbidden()
            return
        except FileNotFoundError:
            self.respond_not_found()
            return
        with f:
            filename = os.path.basename(file)
            filesize = os.path.getsize(file)
            content_type = self._guess_type(file)
            content_range = self.headers['Range']
            if filesize == 0 or not content_range:
                self.send_response(HTTPStatus.OK)
                self.send_content_length(filesize)
                self.send_content_type(content_type)
                self.send_accept_ranges()
                if include_content_disposition:
                    self.send_content_disposition(filename)
                self.end_headers()
                self._copy_file(f, self.wfile)
                return
            content_range = self._parse_range(content_range, filesize)
            if not content_range:
                self.respond_range_not_satisfiable()
                return
            start, end = content_range
            content_length = end - start + 1
            self.send_response(HTTPStatus.PARTIAL_CONTENT)
            self.send_content_length(content_length)
            self.send_content_type(content_type)
            self.send_accept_ranges()
            self.send_content_range(start, end, filesize)
            if include_content_disposition:
                self.send_content_disposition(filename)
            self.end_headers()
            self._copy_file_range(f, self.wfile, start, content_length)
    def build_html(self, path, dirs, files):
        if path == '/':
            title = self._hostname
        else:
            title = os.path.basename(path.rstrip('/'))
        builder = HtmlBuilder()
        builder.start_head()
        builder.start_title()
        builder.append(title)
        builder.end_title()
        builder.start_style()
        builder.append('.container{height: 100%; display: flex; flex-direction: column; padding: 0 8px; overflow-wrap: break-word;}')
        builder.append('.header{display: flex; justify-content: space-between; padding: 8px 0; font-size: x-large;}')
        builder.append('hr{width: 100%;}')
        builder.append('.main{flex: auto; padding: 16px 0;}')
        builder.append('.content{width: 100%; height: 100%;}')
        builder.append('.list-item{display: flex; justify-content: space-between; padding: 2px 0; word-break: break-all;}')
        builder.append('.list-item:nth-child(even){background-color: #f8f8f8;}')
        builder.append('.item-left{display: flex}')
        builder.append('.item-right{min-width: 140px; max-width: 140px; text-align: right;}')
        builder.append('.item-icon{flex: none; margin-right: 4px;}')
        builder.append('.size{color: #666666;}')
        builder.append('iframe{border: 0;}')
        builder.append('a{color: #2965c7; text-decoration: none;}')
        builder.append('a.hidden{color: #42a5f5;}')
        builder.append('a:hover{color: #ff5500;}')
        if self._upload:
            builder.append('.upload{background-color: #76797b; border-color: #76797b; color: white; border-radius: 16px;}')
            builder.append('.upload:hover{background-color: #565e64; border-color: #565e64;}')
            builder.append('.dragging{border: 4px dashed #cccccc; border-radius: 4px;}')
        builder.append('button{cursor: pointer; border: 1px solid #cccccc; color: #333333; background-color: white; border-radius: 4px;}')
        builder.append('button:hover{background-color: #e6e6e6;}')
        builder.append('button:disabled{opacity: .65; pointer-events: none; user-select: none;}')
        builder.append('.btn-view{padding: 1px 4px;}')
        builder.end_style()
        builder.start_script()
        builder.append('function view_file(){')
        builder.append('    src = this.getAttribute("src");')
        builder.append('    let frame = document.createElement("iframe");')
        builder.append('    frame.setAttribute("src",src);')
        builder.append('    frame.setAttribute("allow","fullscreen");')
        builder.append('    frame.setAttribute("width","100%");')
        builder.append('    frame.setAttribute("height","100%");')
        builder.append('    content = document.getElementById("content");')
        builder.append('    content.replaceWith(frame);')
        builder.append('    document.title=src;')
        builder.append('}')
        if self._upload:
            builder.append('function on_upload_click(){')
            builder.append('    document.getElementById("file").click();')
            builder.append('}')
            builder.append('function on_upload(){')
            builder.append('    document.getElementById("upload").setAttribute("disabled", "");')
            builder.append('    document.getElementById("form").submit();')
            builder.append('}')
            builder.append('let drag_counter = 0;')
            builder.append('function on_dragenter(e){')
            builder.append('    e.preventDefault();')
            builder.append('    drag_counter++;')
            builder.append('    e.currentTarget.classList.add("dragging");')
            builder.append('}')
            builder.append('function on_dragover(e){')
            builder.append('    e.preventDefault();')
            builder.append('}')
            builder.append('function on_dragleave(e){')
            builder.append('    e.preventDefault();')
            builder.append('    drag_counter--;')
            builder.append('    if (drag_counter === 0) {')
            builder.append('        e.currentTarget.classList.remove("dragging");')
            builder.append('    }')
            builder.append('}')
            builder.append('function on_drop(e){')
            builder.append('    e.preventDefault();')
            builder.append('    drag_counter = 0;')
            builder.append('    e.currentTarget.classList.remove("dragging");')
            builder.append('    if (e.dataTransfer.files.length == 0) {return;}')
            builder.append('    document.getElementById("file").files = e.dataTransfer.files;')
            builder.append('    on_upload();')
            builder.append('}')
        builder.append('function on_load() {')
        builder.append('    let btns = document.getElementsByClassName("btn-view");')
        builder.append('    for (let i = 0; i < btns.length; i++) {')
        builder.append('        btns[i].onclick = view_file;')
        builder.append('    }')
        if self._upload:
            builder.append('let upload = document.getElementById("upload");')
            builder.append('upload.onclick = on_upload_click;')
            builder.append('let content = document.getElementById("content");')
            builder.append('content.ondragenter = on_dragenter;')
            builder.append('content.ondragover = on_dragover;')
            builder.append('content.ondragleave = on_dragleave;')
            builder.append('content.ondrop = on_drop;')
            builder.append('let file = document.getElementById("file");')
            builder.append('file.onchange = on_upload;')
        builder.append('}')
        builder.append('window.onload = on_load;')
        builder.end_script()
        builder.end_head()
        builder.start_body()
        builder.append('<div class="container">')
        builder.append('<div class="header">')
        builder.append('<div>')
        builder.append(f'<a href="/">{html.escape(self._hostname)}</a>')
        p = ''
        for name in path.split('/'):
            if name:
                p = f'{p}/{name}'
                builder.append(f' / <a href="{html.escape(parse.quote(p))}/">{html.escape(name)}</a>')
        builder.append('</div>')
        if self._upload:
            builder.append('<button id="upload" class="upload">Upload</button>')
            builder.append(f'<form id="form" action="{html.escape(parse.quote(path))}" method="post" enctype="multipart/form-data" style="display: none;">')
            builder.append('<input id="file" name="file" type="file" required multiple>')
            builder.append('</form>')
        builder.append('</div>')
        builder.append('<hr>')
        builder.append('<div class="main">')
        builder.append('<div id="content" class="content">')
        builder.append('<ul>')
        for d, hidden, items in dirs:
            builder.append('<li class="list-item">')
            builder.append(f'<a class="item-left{" hidden" if hidden else ""}" href="{html.escape(parse.quote(d))}/">')
            builder.append('<svg class="item-icon" xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="#76797b"><path d="M0 0h24v24H0z" fill="none"/><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>')
            builder.append(f'{html.escape(d)}')
            builder.append('</a>')
            builder.append('<span class="item-right">')
            builder.append(f'<span class="size">{items} {"item" if items == 1 else "items"}</span>')
            builder.append('</span>')
            builder.append('</li>')
        for f, hidden, size in files:
            builder.append('<li class="list-item">')
            builder.append(f'<a class="item-left{" hidden" if hidden else ""}" href="{html.escape(parse.quote(f))}" download>')
            builder.append('<svg class="item-icon" xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="#76797b"><path d="M0 0h24v24H0z" fill="none"/><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>')
            builder.append(f'{html.escape(f)}')
            builder.append('</a>')
            builder.append('<span class="item-right">')
            builder.append(f'<span class="size">{self._format_size(size)}</span> <button class="btn-view" src="{html.escape(parse.quote(f))}">View</button>')
            builder.append('</span>')
            builder.append('</li>')
        builder.append('</ul>')
        builder.append('</div>')
        builder.append('</div>')
        builder.append('</div>')
        builder.end_body()
        return builder.build()
    def cmp_path(self, s1, s2):
        if s1[0] == '.' and s2[0] != '.':
            return -1
        if s1[0] != '.' and s2[0] == '.':
            return 1
        len1, len2 = len(s1), len(s2)
        i, min_len = 0, min(len1, len2)
        while i < min_len:
            ch1, ch2 = ord(s1[i]), ord(s2[i])
            if 65 <= ch1 <= 90:
                ch1 += 32
            if 65 <= ch2 <= 90:
                ch2 += 32
            if ch1 == ch2:
                i += 1
            elif 48 <= ch1 <= 57 and 48 <= ch2 <= 57:
                num1, idx1 = self._check_number(s1, len1, i)
                num2, idx2 = self._check_number(s2, len2, i)
                if num1 != num2:
                    return num1 - num2
                i = idx1
            else:
                return ch1 - ch2
        return len1 - len2
    def _is_from_commandline(self):
        ua = self.headers['User-Agent']
        if not ua:
            return False
        prefix = ua.split('/', 1)[0]
        return prefix in self._ua_prefixes
    def _guess_type(self, path):
        guess, _ = mimetypes.guess_type(path)
        return guess if guess else 'text/plain'
    def _parse_range(self, content_range, filesize):
        if len(content_range) < 8 or content_range[:6] != 'bytes=':
            return None
        parts = content_range[6:].split('-')
        if len(parts) != 2:
            return None
        start, end = parts
        if not start.isdecimal():
            return None
        if end and not end.isdecimal():
            return None
        start = int(start)
        end = int(end) if end else filesize - 1
        if start > end or end >= filesize:
            return None
        return (start, end)
    def _copy_file(self, src, dest):
        while True:
            data = src.read(65536)
            if not data:
                return
            dest.write(data)
    def _copy_file_range(self, src, dest, start, length):
        src.seek(start)
        buf_size = 65536
        while length:
            if length <= buf_size:
                dest.write(src.read(length))
                return
            dest.write(src.read(buf_size))
            length -= buf_size
    def _is_hidden_windows(self, file_path):
        return self._is_hidden_unix(file_path) or os.stat(file_path).st_file_attributes & stat.FILE_ATTRIBUTE_HIDDEN != 0
    def _is_hidden_unix(self, file_path):
        return os.path.basename(file_path).startswith('.')
    def _check_number(self, s, n, start):
        num, end = 0, start
        while end < n:
            ch = ord(s[end])
            if ch < 48 or ch > 57:
                break
            num = num * 10 + ch - 48
            end += 1
        return (num, end)
    def _format_size(self, size):
        lst = ((1024, 'KiB'), (1048576, 'MiB'), (1073741824, 'GiB'), (1099511627776, 'TiB'))
        idx = 0
        if size < 1048576:
            idx = 0
        elif size < 1073741824:
            idx = 1
        elif size < 1099511627776:
            idx = 2
        else:
            idx = 3
        return f'{size/lst[idx][0]:.2f} {lst[idx][1]}'
class FileShareHandler(BaseFileShareHandler):
    def __init__(self, files, *args, **kwargs):
        self._files = files
        super().__init__(*args, **kwargs)
    def do_get(self):
        path, _ = self.split_path(parse.unquote(self.path))
        if path == '/':
            files = []
            for f in self._files:
                size = 0
                try:
                    size = os.path.getsize(f)
                except PermissionError:
                    pass
                except FileNotFoundError:
                    continue
                files.append((os.path.basename(f), self.is_hidden(f), size))
            files.sort(key=functools.cmp_to_key(lambda s1, s2: self.cmp_path(s1[0], s2[0])))
            self.respond_ok(self.build_html(path, [], files))
            return
        path = path[1:]
        for f in self._files:
            if path == os.path.basename(f):
                self.respond_for_file(f)
                return
        if path == 'file' and len(self._files) == 1:
            self.respond_for_file(self._files[0])
        else:
            self.respond_not_found()
class DirectoryShareHandler(BaseFileShareHandler):
    def __init__(self, dir, all, *args, **kwargs):
        self._dir = dir.rstrip('/\\') + '/'
        self._all = all
        if is_windows():
            self._contains_hidden_segment = self._contains_hidden_segment_windows
        else:
            self._contains_hidden_segment = self._contains_hidden_segment_unix
        super().__init__(*args, **kwargs)
    def do_get(self):
        path, _ = self.split_path(parse.unquote(self.path))
        if not self._all and self._contains_hidden_segment(path):
            self.respond_not_found()
            return
        file_path = self._dir.rstrip('/') + path
        if os.path.isdir(file_path):
            try:
                dirs, files = self.list_dir(file_path)
                dirs.sort(key=functools.cmp_to_key(lambda s1, s2: self.cmp_path(s1[0], s2[0])))
                files.sort(key=functools.cmp_to_key(lambda s1, s2: self.cmp_path(s1[0], s2[0])))
            except PermissionError:
                self.respond_forbidden()
            except FileNotFoundError:
                self.respond_not_found()
            else:
                self.respond_ok(self.build_html(path, dirs, files))
        elif os.path.isfile(file_path):
            self.respond_for_file(file_path)
        else:
            self.respond_not_found()
    def do_post(self):
        if self._upload:
            self.do_multipart(self._dir.rstrip('/') + parse.unquote(self.path), self.path)
        else:
            super().do_post()
    def list_dir(self, dir):
        dirs, files = [], []
        for name in os.listdir(dir):
            path = dir + name
            hidden = self.is_hidden(path)
            if self._all or not hidden:
                if os.path.isdir(path):
                    items = []
                    try:
                        items = [f for f in os.listdir(path) if self._all or not self.is_hidden(f'{path}/{f}')]
                    except:
                        pass
                    dirs.append((name, hidden, len(items)))
                else:
                    size = 0
                    try:
                        size = os.path.getsize(path)
                    except:
                        pass
                    files.append((name, hidden, size))
        return (dirs, files)
    def _contains_hidden_segment_windows(self, path):
        prefix = self._dir
        for segment in path.strip('/').split('/'):
            if self.is_hidden(prefix + segment):
                return True
            prefix = prefix + segment + '/'
        return False
    def _contains_hidden_segment_unix(self, path):
        return path.find('/.') != -1
class FileReceiveHandler(BaseHandler):
    def __init__(self, dir, *args, **kwargs):
        self._dir = dir
        super().__init__(*args, **kwargs)
    def do_get(self):
        if self.path != '/':
            self.respond_redirect('/')
            return
        self.respond_ok(self.build_html())
    def build_html(self):
        builder = HtmlBuilder()
        builder.start_head()
        builder.start_title()
        builder.append(self._hostname)
        builder.end_title()
        builder.start_style()
        builder.append('.container{height: 80%; padding: 0 8px; display: flex; align-items: center; justify-content: center;}')
        builder.append('.upload{width: 100%; height: 60%; border: 1px solid #cccccc; border-radius: 16px; cursor: pointer; background-color: white; color: #333333; font-size: x-large;}')
        builder.append('.upload:hover{background-color: #e6e6e6;}')
        builder.append('.upload:disabled{opacity: .65; pointer-events: none; user-select: none;}')
        builder.append('.dragging{border: 4px dashed #cccccc;}')
        builder.end_style()
        builder.start_script()
        builder.append('function on_upload_click(){')
        builder.append('    document.getElementById("file").click();')
        builder.append('}')
        builder.append('function on_upload(){')
        builder.append('    document.getElementById("upload").setAttribute("disabled", "");')
        builder.append('    document.getElementById("form").submit();')
        builder.append('}')
        builder.append('function on_dragenter(e){')
        builder.append('    e.preventDefault();')
        builder.append('    e.currentTarget.classList.add("dragging");')
        builder.append('}')
        builder.append('function on_dragover(e){')
        builder.append('    e.preventDefault();')
        builder.append('}')
        builder.append('function on_dragleave(e){')
        builder.append('    e.preventDefault();')
        builder.append('    e.currentTarget.classList.remove("dragging");')
        builder.append('}')
        builder.append('function on_drop(e){')
        builder.append('    e.preventDefault();')
        builder.append('    e.currentTarget.classList.remove("dragging");')
        builder.append('    if (e.dataTransfer.files.length == 0) {return;}')
        builder.append('    document.getElementById("file").files = e.dataTransfer.files;')
        builder.append('    on_upload();')
        builder.append('}')
        builder.append('function on_load() {')
        builder.append('    let upload = document.getElementById("upload");')
        builder.append('    upload.onclick = on_upload_click;')
        builder.append('    upload.ondragenter = on_dragenter;')
        builder.append('    upload.ondragover = on_dragover;')
        builder.append('    upload.ondragleave = on_dragleave;')
        builder.append('    upload.ondrop = on_drop;')
        builder.append('    let file = document.getElementById("file");')
        builder.append('    file.onchange = on_upload;')
        builder.append('}')
        builder.append('window.onload = on_load;')
        builder.end_script()
        builder.end_head()
        builder.start_body()
        builder.append('<div class="container">')
        builder.append('<button id="upload" class="upload">')
        builder.append('Upload')
        builder.append('<form id="form" action="/" method="post" enctype="multipart/form-data" style="display: none;">')
        builder.append('<input id="file" name="file" type="file" required multiple>')
        builder.append('</form>')
        builder.append('</button>')
        builder.append('</div>')
        builder.end_body()
        return builder.build()
    def do_post(self):
        if self.path != '/':
            self.respond_bad_request()
            return
        self.do_multipart(self._dir, '/')
class TextShareHandler(BaseHandler):
    def __init__(self, text, *args, **kwargs):
        self._text = text
        super().__init__(*args, **kwargs)
    def do_get(self):
        if self.path != '/':
            self.respond_redirect('/')
            return
        self.respond_ok(self.build_html())
    def build_html(self):
        builder = HtmlBuilder()
        builder.start_head()
        builder.start_title()
        builder.append(self._hostname)
        builder.end_title()
        builder.start_style()
        builder.append('.container{height: 100%; display: flex; flex-direction: column;}')
        builder.append('.content{flex: auto; margin: 10% 8px; word-wrap: break-word; white-space: pre-wrap; overflow-y: auto;}')
        builder.end_style()
        builder.start_body()
        builder.append('<div class="container">')
        builder.append('<pre class="content">')
        builder.append(html.escape(self._text))
        builder.append('</pre>')
        builder.append('</div>')
        builder.end_body()
        return builder.build()
class TextReceiveHandler(BaseHandler):
    def do_get(self):
        if self.path != '/':
            self.respond_redirect('/')
            return
        self.respond_ok(self.build_html())
    def build_html(self):
        builder = HtmlBuilder()
        builder.start_head()
        builder.start_title()
        builder.append(self._hostname)
        builder.end_title()
        builder.start_style()
        builder.append('.container{height: 100%; display: flex; flex-direction: column;}')
        builder.append('.content{flex: auto; margin: 10% 8px; display: flex; flex-direction: column;}')
        builder.append('.textarea{flex: auto; width: 100%;}')
        builder.append('.submit{width: 100%;}')
        builder.end_style()
        builder.start_script()
        builder.append('function on_keydown(e){')
        builder.append('    if (e.key=="Control"){return;}')
        builder.append('    if (e.ctrlKey && e.key=="Enter") {')
        builder.append('        form = document.getElementById("form");')
        builder.append('        if (form.reportValidity()) {')
        builder.append('            form.submit();')
        builder.append('            e.preventDefault();')
        builder.append('            e.stopPropagation();')
        builder.append('        }')
        builder.append('    }')
        builder.append('}')
        builder.append('function on_load() {')
        builder.append('    text = document.getElementById("text");')
        builder.append('    text.onkeydown = on_keydown;')
        builder.append('}')
        builder.append('window.onload = on_load;')
        builder.end_script()
        builder.end_head()
        builder.start_body()
        builder.append('<div class="container">')
        builder.append('<form id="form" class="content" action="/" method="post">')
        builder.append('<textarea id="text" class="textarea" name="text" placeholder="Enter text" required autofocus></textarea>')
        builder.append('<br>')
        builder.append('<input class="submit" type="submit">')
        builder.append('</form>')
        builder.append('</div>')
        builder.end_body()
        return builder.build()
    def do_post(self):
        if self.path != '/':
            self.respond_bad_request()
            return
        content_type = self.headers['Content-Type']
        if content_type != 'application/x-www-form-urlencoded':
            self.respond_bad_request()
            return
        content_length = self.headers['Content-Length']
        if not content_length or not content_length.isdecimal():
            self.respond_bad_request()
            return
        content_length = int(content_length)
        if content_length <= 5 or content_length > 1048576:
            self.respond_bad_request()
            return
        text = self.rfile.read(5).decode()
        if text != 'text=':
            self.respond_bad_request()
            return
        text = self.rfile.read(content_length - 5).decode()
        text = parse.unquote_plus(text)
        self.respond_redirect('/')
        print(text)
class HtmlBuilder:
    def __init__(self):
        self._out = io.StringIO()
    def start_head(self):
        self._out.write('<!DOCTYPE html>')
        self._out.write('<html>')
        self._out.write('<head>')
        self._out.write('<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0"/>')
    def end_head(self):
        self._out.write('</head>')
    def start_title(self):
        self._out.write('<title>')
    def end_title(self):
        self._out.write('</title>')
    def start_style(self):
        self._out.write('<style type="text/css">')
        self._out.write('*{margin: 0; padding: 0; box-sizing: border-box;}')
        self._out.write('@media (min-width: 576px) {')
        self._out.write('html{padding-left: calc(100vw - 100%);}')
        self._out.write('body{width: 80vw;}')
        self._out.write('}')
        self._out.write('@media (min-width: 768px) {')
        self._out.write('body{width: 70vw;}')
        self._out.write('}')
        self._out.write('@media (min-width: 992px) {')
        self._out.write('body{width: 60vw;}')
        self._out.write('}')
        self._out.write('@media (min-width: 1200px) {')
        self._out.write('body{width: 50vw;}')
        self._out.write('}')
        self._out.write('html{height: 100%;}')
        self._out.write('body{height: 100%; margin: 0 auto; font-family: sans-serif;}')
        self._out.write('input{padding: 1px 2px;}')
        self._out.write('button, input[type="submit"]{padding: 1px 6px;}')
        self._out.write('textarea{padding: 2px;}')
    def end_style(self):
        self._out.write('</style>')
    def start_script(self):
        self._out.write('<script type="text/javascript">')
    def end_script(self):
        self._out.write('</script>')
    def start_body(self):
        self._out.write('<body>')
    def end_body(self):
        self._out.write('</body>')
    def append(self, code):
        self._out.write(code)
    def build(self):
        self._out.write('</html>')
        return self._out.getvalue()
    def __del__(self):
        self._out.close()
class MultipartParser:
    def __init__(self, stream, boundary, content_length):
        self._stream = stream
        self._total_length = content_length
        self._read_length = 0
        self._separator = f'--{boundary}\r\n'.encode()
        self._terminator = f'--{boundary}--\r\n'.encode()
        self._state = MultipartState.INIT
        self._content_dispositon_pattern = re.compile(r'^form-data; name="(.+)"; filename="(.+)"\r\n$')
        self._name = None
        self._filename = None
    def has_next(self):
        if self._state == MultipartState.INIT:
            if self._next_line() != self._separator:
                raise MultipartError
            self._state = MultipartState.HEADER_START
        if self._state == MultipartState.HEADER_START:
            self._parse_headers()
            self._state = MultipartState.PART_START
            return True
        if self._state == MultipartState.END and self._read_length == self._total_length:
            return False
        raise MultipartError
    def next_name(self):
        if self._state != MultipartState.PART_START:
            raise MultipartError
        return self._name
    def next_filename(self):
        if self._state != MultipartState.PART_START:
            raise MultipartError
        return self._filename
    def write_next_to(self, out):
        if self._state != MultipartState.PART_START:
            raise MultipartError
        line, next = None, None
        while True:
            if not line:
                line = self._next_line()
            if len(line) >= 2 and line[-2:] == b'\r\n':
                next = self._next_line()
                if next == self._separator:
                    if len(line) > 2:
                        out.write(line[:-2])
                    self._state = MultipartState.HEADER_START
                    return
                if next == self._terminator:
                    if len(line) > 2:
                        out.write(line[:-2])
                    self._state = MultipartState.END
                    return
                out.write(line)
                line = next
            else:
                out.write(line)
                line = None
    def _parse_headers(self):
        self._name = None
        self._filename = None
        while True:
            line = self._next_line().decode()
            if line == '\r\n':
                break
            parts = line.split(': ')
            if len(parts) != 2:
                raise MultipartError
            key, value = parts
            if key == 'Content-Disposition':
                match = self._content_dispositon_pattern.match(value)
                if not match:
                    raise MultipartError
                self._name = match.group(1)
                self._filename = match.group(2)
        if not self._name or not self._filename:
            raise MultipartError
    def _next_line(self):
        if self._read_length >= self._total_length:
            raise MultipartError
        l = min(65536, self._total_length - self._read_length)
        line = self._stream.readline(l)
        if not line:
            raise MultipartError
        self._read_length += len(line)
        return line
class MultipartState:
    INIT = 0
    HEADER_START = 1
    PART_START = 2
    END = 3
class MultipartError(ValueError):
    pass
def get_best_family(host, port):
    info = socket.getaddrinfo(host, port, type=socket.SOCK_STREAM, flags=socket.AI_PASSIVE)
    family, type, proto, canonname, addr = info[0]
    return family, addr
def is_windows():
    return os.name == 'nt'
def on_interrupt(a, b):
    if not is_windows():
        sys.stderr.write('\n')
    sys.exit(1)
def print_prompt():
    if is_windows():
        sys.stderr.write('Enter your text, then press Ctrl + Z followed by the Enter key:\n')
    else:
        sys.stderr.write('Enter your text, then press Ctrl + D:\n')
def start_server(address, port, handler_class):
    ShareServer.address_family, addr = get_best_family(address, port)
    with ShareServer(addr, handler_class) as server:
        host, port = server.socket.getsockname()[:2]
        sys.stderr.write(f'Serving HTTP on {host} port {port} ...\n')
        server.serve_forever()
def main():
    sys.tracebacklimit = 0
    signal.signal(signal.SIGINT, on_interrupt)
    parser = argparse.ArgumentParser(allow_abbrev=False)
    parser.add_argument('-b', '--bind', dest='address', help='bind address [default: all interfaces]')
    parser.add_argument('-p', '--port', type=int, default=8888, help='port [default: 8888]')
    parser.add_argument('-s', '--share', action='store_true', help='share mode (default mode)')
    parser.add_argument('-r', '--receive', action='store_true', help='receive mode, can be used with -s option (only for directory)')
    parser.add_argument('-a', '--all', action='store_true', help='show all files, including hidden ones, only for directory')
    parser.add_argument('-t', '--text', action='store_true', help='for text')
    parser.add_argument('-P', '--password', nargs='?', const=os.getenv('SHARE_PASSWORD'), help='access password, if no PASSWORD is specified, the environment variable SHARE_PASSWORD will be used')
    parser.add_argument('arguments', nargs='*', help='a directory, files or texts')
    args = parser.parse_args()
    if args.password and len(args.password) < 3:
        raise ValueError('password is too short')
    if not args.receive:
        args.share = True
    if args.share and args.receive:
        dir = None
        if not args.arguments:
            dir = os.getcwd()
        elif os.path.isdir(args.arguments[0]):
            dir = args.arguments[0]
        else:
            raise FileNotFoundError(f'{args.arguments[0]} is not a directory')
        handler_class = functools.partial(DirectoryShareHandler, dir, args.all, upload=True, password=args.password)
    elif args.share:
        if args.text:
            if args.arguments:
                text = '\n'.join(args.arguments)
            else:
                print_prompt()
                text = ''.join(sys.stdin.readlines())
                if not text:
                    sys.exit(1)
            handler_class = functools.partial(TextShareHandler, text, password=args.password)
        else:
            dir, files = None, None
            if not args.arguments:
                dir = os.getcwd()
            elif os.path.isdir(args.arguments[0]):
                dir = args.arguments[0]
            else:
                for f in args.arguments:
                    if not os.path.isfile(f):
                        raise FileNotFoundError(f'{f} is not a file')
                files = [os.path.realpath(f) for f in args.arguments]
            if dir:
                handler_class = functools.partial(DirectoryShareHandler, dir, args.all, password=args.password)
            else:
                handler_class = functools.partial(FileShareHandler, files, password=args.password)
    else:
        if args.text:
            handler_class = functools.partial(TextReceiveHandler, password=args.password)
        else:
            dir = None
            if not args.arguments:
                dir = os.getcwd()
            elif os.path.isdir(args.arguments[0]):
                dir = args.arguments[0]
            else:
                raise FileNotFoundError(f'{args.arguments[0]} is not a directory')
            handler_class = functools.partial(FileReceiveHandler, dir, password=args.password)
    start_server(args.address, args.port, handler_class)
main()