/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $http_interface: $connection_interface;

var $dmi_data descriptions = #[];
var $has_commands local = #[];
var $has_commands remote = #[];
var $has_commands shortcuts = #[];
var $http_interface args = #[];
var $http_interface bytes = 205;
var $http_interface cache = [];
var $http_interface ctype = 0;
var $http_interface full = 0;
var $http_interface header = 0;
var $http_interface http = "HTTP/1.0";
var $http_interface info = #[];
var $http_interface keep_alive = 0;
var $http_interface method = "GET";
var $http_interface sent = 0;
var $http_interface status = 0;
var $root created_on = 863766118;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root inited = 1;
var $root managed = [$http_interface];
var $root manager = $http_interface;
var $root quota_exempt = 1;

public method ._respond() {
    arg input;
    var output, t;
    
    switch (type(input)) {
        case 'list:
            output = `[];
            for t in (input)
                output += ._respond(t);
        case 'string:
            output = str_to_buf(input) + `[10];
        case 'buffer:
            output = input;
        case 'frob:
            output = $parse_lib.filter_ctext(input, #[['formatter, $html_format]]);
        default:
            output = str_to_buf((("Error, unable to send output type: " + type(input)) + " -- ") + input);
    }
    return output;
};

protected method .close_interface() {
    arg @args;
    var c;
    
    if ((!args) && keep_alive) {
        .reset_variables();
        return;
    }
    (.connection()).close();
};

public method .connection_going_away() {
    arg @args;
    
    (> .close() <);
};

public method .expand_url() {
    arg partial_url;
    var base;
    
    base = "http://" + ($http_daemon.site_url());
    switch (partial_url[1]) {
        case "?":
            return ((base + (info['script_name])) + (info['path_info])) + partial_url;
        case "/":
            return base + partial_url;
        default:
            return partial_url;
    }
};

public method .get_info() {
    arg key;
    
    return (> info[key] <);
};

public method .header() {
    arg field;
    
    return (> header[field] <);
};

public method .http_method_GET() {
    var page, r, description, processed_args, final_args, item, lock;
    
    status = 200;
    page = $directories.find_page(info['path], this());
    if (page == 'pagenf) {
        .respond(.response(404, "Page not found"));
        return;
    } else if ((type(page) == 'list) && ((page[1]) == 'redirect)) {
        r = .expand_url(page[2]);
        .respond(.response(302, "Relocated at: " + r), "Location: " + r);
        return;
    }
    if (info['lock_object]) {
        lock = (info['lock_object]).get_lock();
        r = (lock[1]).(lock[2])(this(), @lock[3]);
        if (r) {
            switch (type(r)) {
                case 'list:
                    if ((r[1]) == 'redirect) {
                        r = .expand_url(r[2]);
                        .respond(.response(302, "Relocated at: " + r), "Location: " + r);
                        return;
                    } else {
                        status = 401;
                        .respond(["<HEAD><TITLE>Authorization Required</TITLE></HEAD>", "<BODY>", "<H1>Authorization Required</H1>", "Browser not authentication-capable or authentication failed.", "</BODY>"], ((("WWW-Authenticate: " + (r[1])) + " realm=\"") + (r[2])) + "\"");
                        return;
                    }
                case 'symbol:
                    .respond(.response(403, "Access forbidden"));
                    return;
            }
        }
    }
    catch any {
        description = (| $interface_registry.describe(page, info['generate]) |) || ((| page.describe_method(info['generate]) |) || #[]);
        if ((| description['args] |))
            processed_args = $adapter.process_args(args, description['args]);
        else
            processed_args = #[];
    } with {
        switch (error()) {
            case ~objnf:
                .respond(.response(500, "Object not found"));
                return;
            case ~missingarg:
                .respond(.response(500, "Missing Argument"));
                return;
            default:
                status = 500;
                .respond($parse_lib.html_traceback(traceback(), status));
                $sys.log($parse_lib.traceback(traceback()));
                return;
        }
    }
    if ((| description['auth] |)) {
        if ((description['auth]) != 'none) {
            r = ((description['auth])[1]).((description['auth])[2])(this(), @(description['auth])[3]);
            if (r) {
                switch (type(r)) {
                    case 'list:
                        if ((r[1]) == 'redirect) {
                            r = .expand_url(r[2]);
                            .respond(.response(302, "Relocated at: " + r), "Location: " + r);
                            return;
                        } else {
                            status = 401;
                            .respond(["<HEAD><TITLE>Authorization Required</TITLE></HEAD>", "<BODY>", "<H1>Authorization Required</H1>", "Browser not authentication-capable or authentication failed.", "</BODY>"], ((("WWW-Authenticate: " + (r[1])) + " realm=\"") + (r[2])) + "\"");
                            return;
                        }
                    case 'symbol:
                        .respond(.response(403, "Access forbidden"));
                        return;
                }
            }
        }
    } else if (!(page.is($page))) {
        .respond(.response(500, "Authorization info missing"));
        return;
    }
    catch any {
        if (page.is($page))
            final_args = [header, info, processed_args];
        else if ((| description['arg_order] |))
            final_args = map item in (description['arg_order]) to (processed_args[item]);
        else
            final_args = [];
        if ((| info['run_as] |))
            r = (info['run_as]).as_this_run(page, info['generate], final_args);
        else
            r = page.(info['generate])(@final_args);
        if (type(r) == 'list) {
            if ((r[1]) == 'redirect) {
                page = .expand_url(r[2]);
                .respond(.response(302, "Relocated at: " + page), "Location: " + page);
                return;
            } else if (type(r[1]) != 'list) {
                r = [r];
            }
        } else {
            r = [r];
        }
        if (r != [0])
            .respond(@r);
    } with {
        switch (error()) {
            case ~methodnf:
                .respond(.response(500, "Method not found: " + (info['generate])));
            default:
                status = 500;
                .respond($parse_lib.html_traceback(traceback(), status));
                $sys.log($parse_lib.traceback(traceback()));
        }
    }
    if (!sent)
        .respond(.response(500, "It should have generated a page by now"));
};

public method .http_method_HEAD() {
    var page, r, description, processed_args, final_args, item, lock;
    
    status = 200;
    page = $directories.find_page(info['path], this());
    if (page == 'pagenf) {
        status = 404;
        .respond([]);
        return;
    } else if ((type(page) == 'list) && ((page[1]) == 'redirect)) {
        r = .expand_url(page[2]);
        status = 302;
        .respond([], "Location: " + r);
        return;
    }
    if (info['lock_object]) {
        lock = (info['lock_object]).get_lock();
        r = (lock[1]).(lock[2])(this(), @lock[3]);
        if (r) {
            switch (type(r)) {
                case 'list:
                    if ((r[1]) == 'redirect) {
                        r = .expand_url(page[2]);
                        status = 302;
                        .respond([], "Location: " + r);
                        return;
                    } else {
                        status = 401;
                        .respond([], ((("WWW-Authenticate: " + (r[1])) + " realm=\"") + (r[2])) + "\"");
                        return;
                    }
                case 'symbol:
                    status = 403;
                    .respond([]);
                    return;
            }
        }
    }
    catch any {
        description = (| $interface_registry.describe(page, info['generate]) |) || ((| page.describe_method(info['generate]) |) || #[]);
        if ((| description['args] |))
            processed_args = $adapter.process_args(args, description['args]);
        else
            processed_args = #[];
    } with {
        status = 500;
        switch (error()) {
            case ~objnf:
                .respond([]);
                return;
            case ~missingarg:
                .respond([]);
                return;
            default:
                status = 500;
                .respond([]);
                $sys.log($parse_lib.traceback(traceback()));
                return;
        }
    }
    if ((| description['auth] |)) {
        if ((description['auth]) != 'none) {
            r = ((description['auth])[1]).((description['auth])[2])(this(), @(description['auth])[3]);
            if (r) {
                switch (type(r)) {
                    case 'list:
                        if ((r[1]) == 'redirect) {
                            r = .expand_url(page[2]);
                            status = 302.0.respond([], "Location: " + r);
                            return;
                        } else {
                            status = 401;
                            .respond([], ((("WWW-Authenticate: " + (r[1])) + " realm=\"") + (r[2])) + "\"");
                            return;
                        }
                    case 'symbol:
                        status = 403;
                        .respond([]);
                        return;
                }
            }
        }
    } else if (!(page.is($page))) {
        status = 500;
        .respond([]);
        return;
    }
    .respond([]);
};

protected method .http_method_POST() {
    var len, body, part;
    
    len = (| header["Content-Length"] |);
    if ((len == ~keynf) || (!len))
        (> .respond(.response(400, "No Content-Length.")) <);
    body = (.connection()).handle_POST_input(toint(len[1]));
    
    // handle this differently in normal situations
    for part in (body)
        args = args.union($http_lib.explode_http_encoding(part));
    .http_method_GET();
};

root method .init_http_interface() {
    .reset_variables();
};

protected method .log_request() {
    arg host;
    var line;
    
    line = (((((((((host + " - - \"") + method) + " ") + (info['URI])) + " ") + http) + "\" ") + tostr(status)) + " ") + tostr(bytes);
    if ((| header["User-Agent"] |))
        line = (line + ";; ") + ((header["User-Agent"]).join());
    $sys.log(line);
};

public method .parse_line() {
    arg line;
    var match, URI;
    
    if (!method) {
        if (!line)
            return;
        line = explode(line);
        if (listlen(line) != 3) {
            (.connection()).write(.response(400, "HTTP/0.9 not supported."));
            (.connection()).close();
            return;
        }
        if ((match = regexp(line[1], "^(GET|HEAD|POST)$"))) {
            info = #[['URI, line[2]]];
            method = match[1];
        } else {
            info = #[['URI, line[2]]];
            .respond(.response(405, ("Method: \"" + (line[1])) + "\"."));
            (.connection()).close();
            return;
        }
        http = line[3];
    } else if (line) {
        if ((match = regexp(line, "^([^:]+): *(.+)$"))) {
            if (((match[1]) == "Connection") && ((match[2]) == "Keep-Alive"))
                keep_alive = 1;
            else if ((match[1]) == "Authorization")
                .process_auth_info(match[2]);
            else
                header = header.add_elem(@match);
        }
    } else {
        // parse the URI
        match = $http_lib.explode_url(info['URI]);
        info = info.union(match[1]);
        args = match[2];
        catch any {
            (> .(tosym("http_method_" + method))() <);
        } with {
            if (error() != ~stop)
                .respond($parse_lib.html_traceback(traceback(), (status = 500)));
        }
    }
};

public method .process_auth_info() {
    arg str;
    
    str = str.explode();
    switch (str[1]) {
        case "Basic":
            str = ((str[2]).decode64()).explode(":");
            .set_info('browser_auth, ['basic, [str[1], (| str[2] |) || ""]]);
        default:
            $sys.log("$security_db: Unknown authorization type '" + (str[1]));
            .set_info('browser_auth, ['unknown, str]);
    }
};

protected method .reset_variables() {
    method = "";
    http = "HTTP/1.0";
    status = 200;
    bytes = 0;
    ctype = $http_lib.html_version();
    full = 1;
    header = #[['interface, sender()]];
    info = #[];
    args = #[];
};

public method .respond() {
    arg body, @rest;
    var host, out;
    
    if (!(sender().is($page)))
        (> .perms(sender(), 'writer) <);
    if (status >= 400)
        keep_alive = 0;
    out = ._respond(body);
    bytes = buflen(out);
    .send_header(@rest);
    (.connection()).write(out);
    host = (.connection()).address();
    .log_request(host);
    .close_interface();
    sent = 1;
};

public method .respond_with_file() {
    arg fstat, filename;
    var host;
    
    if (!(sender().is($page)))
        (> .perms(sender(), 'writer) <);
    bytes = fstat[2];
    .send_header();
    (.connection()).cwritef(filename);
    host = (.connection()).address();
    .log_request(host);
    .close_interface();
    sent = 1;
};

public method .response() {
    arg code, message;
    var name, x;
    
    status = code;
    if (!(name = (| $http_lib.get_code(code) |)))
        return .response(500, "We had a booboo!  Invalid code: " + tostr(code));
    if (type(message) == 'string)
        message = [("<p align=center>" + message) + "</p>"];
    return [((("<head><title>" + tostr(code)) + " ") + name) + "</title></head>", "<body bgcolor=\"#ffffff\">", ((("<h1 align=center>" + tostr(code)) + " ") + name) + "</h1>", "<hr size=1 noshade>", @message, ("<hr size=1 noshade><a href=\"/\"><b>" + ($motd.server_name())) + "</b></a></body>"];
};

protected method .send_header() {
    arg @lines;
    var code_str, content_len;
    
    if (!full)
        return;
    if (!(| (code_str = $http_lib.get_code(status)) |))
        code_str = "Unknown Code";
    if (bytes)
        content_len = ["Content-length: " + bytes];
    else
        content_len = [];
    (.connection()).write([(((http + " ") + status) + " ") + code_str, "Server: ColdWeb/2.1 (Virtual Environment)", "Connection: " + (keep_alive ? "Keep-Alive" : "close"), "Content-type: " + ctype, @content_len, @lines, ""]);
};

public method .set_ctype() {
    arg ct;
    
    ctype = ct;
};

public method .set_info() {
    arg key, data;
    
    info = info.add(key, data);
};

public method .set_status() {
    arg s;
    
    (> .perms(caller(), $http_connection) <);
    status = s;
};