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;
};