ColdWeb-3.1/
ColdWeb-3.1/src/
// ------------------------------------------------------------------
//
// Object $connection (Generic connection object)
//
// This handles inbound and outbound connections
//
// for inbound, the connection which binds to the port becomes the
// 'daemon', and it reassigns incoming connections to spawned children.
//
// ------------------------------------------------------------------

new object $connection: $root;

var host = 0;
var port = 0;

public method .host() {
    return host;
};

public method .set_host() {
    arg new_host;

    host = new_host;
};

public method .port() {
    return port;
};

public method .startup() {
    arg start_port;

    port = start_port;
    $sys.log("** Starting httpd on port " + tostr(port) + " **");
    bind_port(port);
};

driver method .connect() {
    arg client, server, socket;
    var d;

    d = .spawn();
    reassign_connection(d);
    d.set_host(client);
};

driver method .disconnect() {
    .close();
};

// override on actual descendants to do something
driver method .parse() {
    arg incoming;
};

public method .close() {
    (| close_connection() |);
    .destroy();
};

// ------------------------------------------------------------------
//
// Object $http_connection
//
// HTTP-specific connection object
//
// handles: GET and POST
//

new object $http_connection: $connection;

var header = #[];
var method = "";
var http = "";
var status = 0;
var URL = "";
var bytes = 0;
var ctype = "";
var prefix = "";
var buffer = `[];
var reading = 0;
var info = #[];
var keep_alive = 0;

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

public method .parse() {
    arg incoming;
    var l, line, i, t;

    catch any {
        buffer += incoming;
        while (!reading) {
            if (!(i = `[13, 10] in buffer))
                break;
            line = buf_to_str(subbuf(buffer, 1, i - 1));
            buffer = subbuf(buffer, i + 2);
            (> .process_line(line) <);
        }
    } with handler {
        .respond(traceback().tb_to_html(status = 500));
        .close();
        $sys.log(traceback().tb_to_text());
    }
};

public method .reset_variables() {
    header = #[];
    method = "";
    http = "HTTP/1.0";
    status = 200;
    URL = "";
    prefix = "";
    ctype = "text/html";
    buffer = `[];
    reading = 0;
    keep_alive = 0;
};

public method .close() {
    arg [args];

    if (!args && keep_alive)
        return;
    pass();
};

public method .die() {
    arg code, message;

    return .respond($sys.response(status = code, message));
};

  // die on HTTP/0.9, we are simply required to understand it,
  // not handle it.
public method .process_line() { 
    arg line;
    var match;
    
    if (!method) { 
        line = explode(line);
        if (listlen(line) != 3) {
            cwrite(str_to_buf($sys.response(400, "HTTP/0.9 not supported.")));
            .close();
            .reset_variables();
            return;
        }
        if ((match = regexp("^GET|POST", line[1])))
            method = match[1];          
        else    
            return .die(405, "Method: \"" + line[1] + "\".");
        URL = line[2];
        http = line[3];
    } else if (line) {
        // record the header
        if ((match = regexp("^([^:]+): *(.+)$", line))) {
            if (match[1] == "Connection" && match[2] == "Keep-Alive")
                keep_alive = 1;
            else
                header = header.add_elem(@match);
        }
    } else {
        // Non line, parse the URL and go with it
        info = URL.explode_url();
        URL = "/" + info['path].join("/");
        catch any {
            if (method == "POST")
                (> .method_POST() <);
            else
                (> .handle_request() <);
        } with {
            if (error() != ~stop) {
                $sys.log(traceback().tb_to_text());
                .respond(traceback().tb_to_html(status = 500));
            }
        }
    }
};

public method .log_request() {
    var line;

    line = strfmt("%s - - [%s] %s \"%s %s\" %s %s",
                  .host(), $time.format("%d %h %y %H:%M"),
                  method, URL, http, status, bytes);
    if ((| header["User-Agent"] |))
         line = line + ";; " + header["User-Agent"].join();
    dblog("LOG: " + line);
    .reset_variables();
};

public method .send_header() {
    arg [fields];
    var x;

    cwrite(strings_to_buf([http + " " + tostr(status),
           "Server: ColdWeb/3.1 (Customized: Server Purpose)",
           "Content-type: " + ctype,
           "Content-length: " + tostr(bytes),
           @fields,
           ""]));
};

public method .redirect() {
    arg url;
    var body;

    body = "<head><title>Redirected</title></head>\n<body>If you are reading this text, your client does not support redirection, and you should go to:<pre><a href=\"" + url + "\">" + url + "</a></pre></body>";
    status = 302;
    body = str_to_buf(body);
    bytes = buflen(body);
    .send_header("Location: " + url);
    cwrite(body);
    .close();
    .log_request();
};

public method .respond() {
    arg body, [close];

    body = str_to_buf(body);
    bytes = buflen(body);
    .send_header();
    cwrite(body);
    .close(@close);
    .log_request();
};

// Just diddle and get things to the point that .handle_request() likes it
public method .method_POST() {
    var len, body, part;

    len = (| header["Content-Length"] |);
    if (len == ~keynf || !len)
        (> .respond($sys.response(status = 400, "No Content-Length.")) <);
    len = toint(len[1]);

    // what a nightmare: sometimes the input for post will
    // not be received immediately, so we wait for it.
    if (buflen(buffer) < len) {
        reading = 300;
        while (buflen(buffer) < len && --reading) {
            $sys.sleep();
            refresh();
        }
        if (buflen(buffer) < len) {
            buffer = `[];
            status = 400;
            .respond($sys.response(400, "Timeout on receiving POST request."));
            return;
        }
    }

    body = buf_to_strings(subbuf(buffer, 1, len));
    buffer = subbuf(buffer, len + 1);
    if ((`[13, 10] in buffer) == 1)
        buffer = subbuf(buffer, 3);

    if (body[listlen(body)] == `[])
        body = delete(body, listlen(body));
    else
        body = replace(body, listlen(body), buf_to_str(body[listlen(body)]));

    // handle this differently in normal situations
    for part in (body)
        info = info.add('args, info['args].union(part.break_fields()));

    .handle_request();
};

private method .handle_request() {
    var page, buf, path, obj;

    // HERE: Insert handling routines, finish with a buffer 'buf'
    status = 200;
    buf = str_to_buf($sys.response(status, "You need to put something here."));

    // send it off
    bytes = buflen(buf);
    .send_header();
    cwrite(buf);
    .close();
    .log_request();
};