psyced/world/net/http/server.c

293 lines
8.0 KiB
C
Raw Normal View History

2009-01-26 20:12:53 +00:00
// $Id: server.c,v 1.64 2008/05/13 09:51:07 lynx Exp $ // vim:syntax=lpc
//
// yes, psyced is also a web server, like every decent piece of code. ;)
//
2009-01-26 20:12:53 +00:00
#include <ht/http.h>
#include <net.h>
#include <text.h>
#include "header.i"
2011-08-27 08:15:19 +00:00
volatile string url, qs, prot, method, body = "";
volatile mixed item;
volatile mapping headers;
volatile int length;
// we're using #'closures to point to the functions we're giving the
// next_input_to(). as i don't want to restructure the whole file, i need
// to predefine some functions.
//
// quite stupid indeed, as they don't got any modifiers or whatever :)
2011-08-27 10:58:38 +00:00
parse_request(input);
parse_header(input);
parse_body(input);
devNull();
2009-01-26 20:12:53 +00:00
qScheme() { return "html"; }
2011-08-27 08:44:26 +00:00
quit() {
2011-08-27 08:48:32 +00:00
D2(D("««« HTTP done.\n");)
2011-08-27 08:44:26 +00:00
destruct(ME);
}
2011-08-27 08:48:32 +00:00
timeout() {
if (method == "post" && stringp(body) && strlen(body))
body(); // try using incomplete post
else quit();
}
logon() {
2011-08-27 08:48:32 +00:00
D2(D("»»» HTTP request:\n");)
// bigger buffer (for psyc logo)
set_buffer_size(32768);
// unfortunately limited to a compilation limit
// so we would have to push large files in chunks
// using heart_beat() or something like that TODO
2011-08-27 10:58:38 +00:00
next_input_to(#'parse_request);
2011-08-27 08:48:32 +00:00
call_out(#'timeout, 23);
}
disconnected(remainder) {
// TODO: shouldn't ignore remainder
2011-08-27 08:48:32 +00:00
D2(D("««« HTTP got disconnected.\n");)
destruct(ME);
return 1; // expected death of socket
}
// gets called from async apps
done() { quit(); }
parse_wait(null) { // waiting to send my error message here
if (null == "") {
http_error("HTTP/1.0", 405, "Invalid Request (Welcome Proxyscanner)");
quit();
}
next_input_to(#'parse_wait);
}
2011-08-27 10:58:38 +00:00
parse_request(input) {
// reset state. in case we support HTTP/1.1. do we?
method = item = url = prot = body = qs = 0;
headers = ([]);
P2(("=== HTTP got: %O from %s\n", input, query_ip_name(ME)))
if (!input || input=="") {
// should return error?
input_to(#'parse_request); // lets just ignore the empty line
return 1;
}
input = explode(input, " ");
switch(sizeof(input)) {
default:
2011-08-27 10:58:38 +00:00
prot = input[2];
next_input_to(#'parse_header);
case 2:
// earlier HTTP versions have no headers
// ok, it's excentric to support HTTP/0.9 but
// it is practical for debugging and doesn't
// cost us a lot extra effort
url = input[1];
unless (sscanf(url, "%s?%s", item, qs)) item = url;
method = lower_case(input[0]);
break;
case 1:
// should return error!
quit();
}
P4(("=== HTTP user requested url: %O\n", url))
if (method == "connect") next_input_to(#'parse_wait);
else if (!prot) body(); // HTTP/0.9 has no headers
else next_input_to(#'parse_header);
}
parse_header(input) {
string key, val;
P4(("parse_header(%O)\n", input))
unless (input == "") {
if (sscanf(input, "%s:%1.0t%s", key, val)) {
headers[lower_case(key)] = val;
}
next_input_to(#'parse_header);
} else {
if (method == "POST" && (length = to_int(headers["content-length"])) &&
headers["content-type"] == "application/x-www-form-urlencoded") {
input_to(#'parse_body, INPUT_IGNORE_BANG | INPUT_CHARMODE | INPUT_NO_TELNET);
} else {
process();
next_input_to(#'devNull);
}
}
}
parse_body(input) {
//P4(("parse_body(%O)\n", input))
body += input;
if (strlen(body) == length)
process();
else
input_to(#'parse_body, INPUT_IGNORE_BANG | INPUT_CHARMODE | INPUT_NO_TELNET);
}
process() {
string t, ext;
mapping query = ([]);
object o;
int done = 1;
// take defaults from cookie, then override by query string
2009-01-26 20:12:53 +00:00
// lynXism cookie behaviour, normal one is below
t = headers["cookie"];
P4(("found cookie: %O\n", t))
if (t && sscanf(t, "psyced=\"%s\"", t)) {
P3(("got cookie: %O\n", t))
query = url_parse_query(query, t);
P4(("parsed cookie: %O\n", query))
}
2009-01-26 20:12:53 +00:00
#ifdef GENERIC_COOKIES // we might need them someday..?
// if within the same domain other cookies are being used, like
// by including google-analytics, then we might be receiving them
// here and have no friggin' idea what they are good for.
// thus: we *need* a way to ensure a cookie is our own.
// FIXME: this is not really compliant
else if (t) {
mapping cook = ([ ]);
string k, v;
while(t && sscanf(t, "%s=%s;%t%s", k, v, t) >= 2) {
cook[k] = v;
}
if (sscanf(t, "%s=%s", k, v))
cook[lower_case(k)] = v; // case insensitive
cook[0] = headers["cookie"]; // save cookie-string
headers["cookie"] = cook;
}
#endif
2011-08-27 10:58:38 +00:00
if (qs) {
P3(("got query: %O\n", qs))
query = url_parse_query(query, qs);
}
if (method == "POST" && headers["content-type"] == "application/x-www-form-urlencoded") {
query = url_parse_query(query, body);
}
P4(("parsed query: %O\n", query))
2011-08-27 08:15:19 +00:00
switch (item) {
case "/favicon.ico":
#if 0
2011-08-27 08:15:19 +00:00
htredirect(prot, "http://www.psyced.org/favicon.ico",
"This one looks neat", 1);
quit();
return 1;
#else
2011-08-27 08:15:19 +00:00
item = "/static/favicon.ico";
break;
#endif
case "/":
case "":
// should we look for text/wml in the accept: and go directly
// to /net/wap/index ?
//
2011-08-27 08:15:19 +00:00
http_ok(prot);
sTextPath(0, query["lang"], "html");
write( //T("_HTML_head", "<title>" CHATNAME "</title><body>\n"
// "<center><table width=404><tr><td>") +
T("_PAGES_index", "<i><a href=\"http://www.psyc.eu\"><img src="
"\"static/psyc.gif\" width=464 height=93 border=0></a><p>"
"<a href=\"http://www.psyced.org\">psyced</a></i> -"
" your multicast capable web application server.") );
// T("_HTML_tail", "</td></tr></table></center></body>"));
quit();
return 1;
case "/static": // really don't like to do this, but the IE stores directories
// (history) without trailing slash, even if the url originaly
// has one, at least IIRC.
2011-08-27 08:15:19 +00:00
htredirect(prot, "/static/", "use the trailing slash", 1);
quit();
return 1;
case "/static/":
2011-08-27 08:15:19 +00:00
item = "/static/index.html";
break;
case "/oauth":
object oauth;
2011-08-27 08:15:19 +00:00
http_ok(prot);
//PT((">>> looking up token %O in shm: %O\n", query["oauth_token"], shared_memory("oauth_request_tokens")))
if (oauth = shared_memory("oauth_request_tokens")[query["oauth_token"]]) {
//PT((">>> oauth: %O\n", oauth))
oauth->verified(query["oauth_verifier"]);
m_delete(shared_memory("oauth_request_tokens"), query["oauth_token"]);
write("OAuth succeeded, you can now return to your client.");
} else {
write("OAuth failed: token not found");
}
quit();
return 1;
}
string name;
2011-08-27 08:15:19 +00:00
switch (item[1]) {
case '~':
2011-08-27 08:15:19 +00:00
string channel, nick = item[2..];
if (sscanf(item, "/~%s/%s", nick, channel)) {
name = "~" + nick + "#" + channel;
} else if (o = summon_person(nick, NET_PATH "user")) {
2011-08-27 08:15:19 +00:00
o->htinfo(prot, query, headers, qs, channel);
quit();
return 1;
}
//fall thru
case '@':
2011-08-27 08:15:19 +00:00
unless(name) name = item[2..];
o = find_place(name);
break;
default:
2011-08-27 08:15:19 +00:00
if (abbrev("/static/", item)) {
if (file_size(item) > 0) {
if (sscanf(item, "%!s.%s", ext)) {
while (sscanf(ext, "%!s.%s", ext)) ;
}
2011-08-27 08:15:19 +00:00
http_ok(prot, content_type(ext), 0);
binary_message(read_file(item));
quit();
return 1;
}
2011-08-27 08:15:19 +00:00
} else if (sscanf(item, "/%s/%s.page", ext, t) == 2) {
http_ok(prot);
sTextPath(0, query["lang"] || ext, "html");
t = replace(t, "/", "_");
write(T("_HTML_head", "<title>" CHATNAME "</title><body>\n"
"<center><table width=404><tr><td>") +
T("_PAGES_"+t, "[no such page]\n") +
T("_HTML_tail", "</td></tr></table></center></body>"));
quit();
return 1;
}
}
2011-08-27 08:15:19 +00:00
if (index(item, ':') != -1) {
http_error(prot, 501, "Not Implemented. Whatever you are trying "
"there, this server won't help you.");
quit();
return;
}
2011-08-27 08:15:19 +00:00
unless (o) o = item -> load();
if (objectp(o) || o = find_object(item))
done = o->htget(prot, query, headers, qs) != HTMORE;
if (done)
quit();
else
2011-08-27 08:48:32 +00:00
remove_call_out(#'timeout);
return 1;
}
// wozu binary_message nochmal durch eine funktion jagen? lieber so nennen:
emit(a) { return binary_message(a); }
devNull() {
next_input_to(#'devNull);
2011-08-27 08:48:32 +00:00
D2(D("=== HTTP just ignored some input\n");)
}