ColdWeb-3.1/
ColdWeb-3.1/src/
// ------------------------------------------------------------------
//
// Root Object
//
// We define some base object handling methods here
// (initialization/de-initialization etc)
//
// object variable 'child' defines whether we are a core object,
// or a spawned child object.
//
// ------------------------------------------------------------------

object $root;

var child = 0;
var index = 0;

// ----------------------------------------------
// initialization methods

private method .initialize(): nooverride {
    var ancestors, ancestor, pos, len, method, name;

    ancestors = ancestors();
    len = ancestors.length();
    for pos in [0 .. len - 1] {
        ancestor = ancestors[len - pos];
        name = substr(tostr(ancestor), 2);
        if (!name)
            continue;
        method = tosym("init_" + tostr(name));
        catch ~methodnf {
            if (find_method(method) != ancestor) {
                throw(~perm, "Initialization method for " + ancestor
                             + " in wrong place("
                             + find_method(method) + ")");
            }
            .(method)();
        }
    }
    child = 1;
};

private method .uninitialize(): nooverride {
    var ancestor, p, name, method;

    for ancestor in (ancestors()) {
        name = substr(tostr(ancestor), 2);
        if (!name)
            continue;
        method = tosym("uninit_" + name);
        catch ~methodnf {
            if ((> find_method(method) <) != ancestor) {
                throw(~perm, "Uninitialization method for " + ancestor
                       + " in wrong place (" + find_method(method) + ")");
            }
            (> .(method)() <);
        }
    }
};

// ----------------------------------------------
// create/destroy objects

public method .spawn(): nooverride {
    arg [name];
    var base, obj;

    if (child)
        throw(~perm, "Attempt to spawn a non-core object (" + .objname() + ")");
    obj = (> create([this()]) <);
    catch any {
        (> obj.initialize() <);
    } with handler {
        $sys.log(traceback().tb_to_text());
        if (!(| obj.destroy() |))
            throw(~ack, "Unable to destroy aborted attempt", traceback());
        rethrow(error());
    }
    if (name && type(name[1]) == 'symbol)
        name = name[1];
    else
        name = .next_objname();
    (| obj.set_objname(name) |);

    return obj;
};

public method .set_core() {
    child = 0;
};

// children (specifically $slate) can override this to return a more
// random yet unique objname

public method .next_objname() {
    index = index + 1;
    return tosym(tostr((| objname() |) || objnum()) + "_" + index);
};

public method .destroy() {
    if (!child)
        throw(~perm, "Attempt to destroy a core object.");
    catch any {
        (| .uninitialize() |);
        (> destroy() <);
    }
};
    
// ----------------------------------------------
// thunk the objname handling functions

public method .set_objname(): nooverride {
    arg name;

    (> set_objname(name) <);
};

public method .objname(): nooverride {
    arg [args];
    var name;

    name = (| objname() |);
    if (args)  
        return name;
    if (name)
        return "$" + tostr(name);
    return toliteral(this());
};

// ----------------------------------------------
// root initialization method

root method .init_root() {
    child = 1;
};

// ----------------------------------------------
// so others can find out what children we have

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

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

// ------------------------------------------------------------------
//
// System Object
//
// System methods, 'global' values (primarily for the web)
//
// ------------------------------------------------------------------

object $sys;

var http_codes = #[[200, "Ok"],\
                   [201, "Created"],\ 
                   [202, "Accepted"],\
                   [203, "Provisional Information"],\  
                   [204, "No Content"],\
                   [300, "Multiple Choices"],\
                   [301, "Moved Permanently"],\
                   [302, "Moved Temporarily"],\
                   [303, "Method"],\
                   [304, "Not Modified"],\
                   [400, "Bad Request"],\
                   [401, "Unauthorized"],\
                   [402, "Payment Required"],\
                   [403, "Forbidden"],\
                   [404, "Not Found"],\
                   [405, "Method Not Allowed"],\
                   [406, "None Acceptable"],\
                   [407, "Proxy Authentication Required"],\
                   [408, "Request Timeout"],\
                   [409, "Conflict"],\
                   [410, "Gone"],\
                   [500, "Internal Server Error"],\
                   [501, "Not Implemented"],\
                   [502, "Bad Gateway"],\
                   [503, "Service Unavailable"],\
                   [504, "Gateway Timeout"]];
var page_head = "";
var page_tail = "";
var primary_server = "";
var sleeping = [];
var server_name = "";

public method .log() {
    arg text;
    var l;

    if (type(text) == 'list) {
        for l in (text)
            .log(l);
    } else {
        dblog("[" + $time.format("%d %h %y %H:%M") + "] " + text);
    }
};

driver method .startup() {
    arg args;
    var obj, v;

    catch any {
        .read_config();
       
        if ("-d" in args) {
            .log("** System variables:");
            for v in (variables())
                .log("**   " + v + " = " + toliteral(get_var(v)));
        }

        // cleanup old connections
        for obj in ($http_connection.children() +
                    ((| $login_connection.children() |) || []))
            (| obj.destroy() |);

        // start web daemon
        (> $http_connection.startup( (| toint(args[1]) |) || 2000) <);

        // give us a heartbeat every 1 second (sleep needs it this fast)
        set_heartbeat(1);
    } with handler {
        (| .log("** Error Encountered upon startup:") |);
        (| .log(traceback().tb_to_text()) |);
        shutdown();
    }
};

// see if we need to wake any sleeping objects
driver method .heartbeat() {
    var task, sleepers;

    sleepers = sleeping;
    sleeping = [];
    while (sleepers) {
        task = sleepers[1];
        sleeping = delete(sleepers, 1);
        resume(task);
    }
};

public method .sleep() {
    sleeping = setadd(sleeping, task_id());
    suspend();
};

driver method .signal() {
    arg signal;

};

//
// this point on deals with the configuration file
// 
public method .set_value() {
    arg value;
    var list, tag, reg, out, sub;

    list = match_template("*=*", value);
    if (!list)
        throw(~invalid, "Invalid configuration, must be 'tag=value'");
    value = list[2];
    tag = tosym(lowercase(list[1].strip().trim()));
    (> set_var(tag, value) <);
};

public method .page_head() {
    arg [args];
    var out, title;

    title = [@args, server_name][1];
    out = "<head><title>" + title + "</title></head>\n";
    if (listlen(args) > 1)
        return [out + page_head];
    return [out + page_head];
};

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

    if (args)
        return [page_tail];
    return page_tail;
};

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

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

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

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

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

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

public method .response() {
    arg code, message;
    var name, x;

    if (!(name = (| http_codes[code] |)))
        return .response(500, "We had a booboo!  Invalid code: " + code);
    return "<head><title>" + code + " " + name +
           "</title></head>\n<body>\n<h1 align=center>" + code + " " + name +
           "</h1>\n<hr>" + message + .page_tail();
};

public method .read_config() {
    var line, count, more, tag, value, name, i;

    catch ~file, ~directory {
        (> fopen("config") <);
    } with handler {
        shutdown();
        throw(~shutdown, "Unable to open 'config' file in root db filesystem.");
    }

    atomic(1);
    catch any {
        while ((line = (| fread() |)) != ~eof) {
            count++;
            line = line.trim();
            if (!line || line[1] == "#")
                continue;

            if (!more) {
                i = ":" in line;
                if (!i)
                    throw(~invalid, strfmt("Line %s invalid (%25e)", line));
                tag   = substr(line, 1, i - 1);
                value = substr(line, i + 1).trim();
            } else {
                if (!strcmp(line, more)) {
                    more = 0;
                    value += "\n";
                } else {
                    value = value + "\n" + line;
                    continue;
                }
            }

            if (match_begin(value, "<<")) {
                more = substr(value, 3).trim();
                value = "";
                continue;
            }

            name = lowercase(strsed(tag, "-", "_", "g"));
            name = (| tosym(name) |);
            if (!name)
                throw(~invtag, "Invalid tag '" + tag + "'");
    
            catch any
                set_var(name, value);
            with { dblog("name: " + name);
                throw(~invtag, "Invalid tag '" + tag + "'"); }

            refresh();
        }
    }

    atomic(0);
    fclose();
};