/* /daemon/http.c
* from Nightmare IV
* an http daemon that can talk to Mosaic and other WWW clients
* created by Descartes of Borg 940521
* callback fix by Robocoder 950117
*/
#include <std.h>
#include <daemons.h>
#include <security.h>
#include <network.h>
#include "http.h"
inherit DAEMON;
static private int __SocketHTTP;
static private mapping __Sockets, __Activity;
void create() {
daemon::create();
set_no_clean(1);
__Sockets = ([]);
__Activity = ([]);
call_out("setup", 2);
call_out("clean_sockets", 180);
}
static void setup() {
if((__SocketHTTP=socket_create(STREAM,"read_callback","close_callback"))<0){
log_file("httpd", "Failed to create socket.\n");
return;
}
if(socket_bind(__SocketHTTP, PORT_HTTP) < 0) {
socket_close(__SocketHTTP);
log_file("httpd", "Failed to listen to socket.\n");
return;
}
if(socket_listen(__SocketHTTP, "listen_callback") < 0) {
socket_close(__SocketHTTP);
log_file("httpd", "Failed to listen to socket.\n");
}
}
void listen_callback(int fd) {
int neu;
if((neu = socket_accept(fd, "read_callback", "write_callback")) < 0)
return;
write_callback(neu);
}
void write_callback(int fd) {
string tmp;
int x;
if(!__Sockets[fd]) {
sscanf(socket_address(fd), "%s %*s", tmp);
x = resolve(tmp, "resolve_incoming");
__Sockets[fd] = ([ "address": tmp, "key": x, "time": time() ]);
add_activity(fd, sprintf("CONNECTED (%s)\n", ctime(time())));
}
else close_connection(fd);
}
void read_callback(int fd, string str) {
string cmd, args;
if(!str || str == "") {
http_error(fd, BAD_CMD);
return;
}
str = explode(replace_string(str, "\r", ""), "\n")[0];
sscanf(str, "%s %s", cmd, args);
switch(lower_case(cmd)) {
case "get": get_file(fd, args); return;
default: http_error(fd, BAD_CMD); return;
}
}
void close_callback(int fd) {
log_file("httpd", "Socket closed.\n");
socket_close(fd);
if(fd == __SocketHTTP) this_object()->remove();
}
void resolve_incoming(string nom, string addr, int cle) {
int *fds;
int i;
i = sizeof(fds = keys(__Sockets));
while(i--)
if(__Sockets[fds[i]]["key"]==cle && __Sockets[fds[i]]["address"]==addr) {
__Sockets[fds[i]]["name"] = (nom ? nom : addr);
return;
}
log_file("httpd",sprintf("Ip %s resloved to %s after connection closed.\n",
addr, (nom ? nom : "NOT RESOLVED")));
}
static private void http_error(int fd, mapping err) {
string str;
add_activity(fd, sprintf("ERROR: %s (%s)\n", err["error"], ctime(time())));
retry(fd, str = read_file(err["file"]) ? str : "");
}
static private void add_activity(int fd, string act) {
if(!__Activity[fd]) __Activity[fd] = ({ act });
else __Activity[fd] += ({ act });
}
void close_connection(int fd) {
int i, maxi;
maxi = sizeof(__Activity[fd]);
if(!__Sockets[fd]["name"]) __Sockets[fd]["name"]=__Sockets[fd]["address"];
for(i =0; i<maxi; i++)
log_file("httpd", sprintf("%s: %s",
__Sockets[fd]["name"], __Activity[fd][i]));
map_delete(__Sockets, fd);
map_delete(__Activity, fd);
socket_close(fd);
}
static void clean_sockets() {
int *cles;
int i;
i = sizeof(cles = keys(__Sockets));
while(i--)
if(time() - __Sockets[cles[i]]["time"] > 180) close_connection(cles[i]);
call_out("clean_sockets", 180);
}
static private void get_file(int fd, string file) {
string *parts, *tmp;
mixed str;
string id, args;
int x;
add_activity(fd, sprintf("GET %s (%s)\n", file, ctime(time())));
file = (tmp = explode(file, " "))[0];
id = sizeof(parts) > 1 ? parts[1] : "";
if(file[0] != '/') file = sprintf("/%s", file);
parts = explode(file = absolute_path("/", file), "/");
if(!sizeof(parts)) file = sprintf("%s/index.html", DIR_WWW);
else if(parts[0][0] == '~') {
parts[0] = sprintf("%spublic_html",
user_path(parts[0][1..strlen(parts[0])-1]));
file = implode(parts, "/");
}
if(strsrch(file, DIR_WWW) && strsrch(file, REALMS_DIRS))
file = sprintf("%s%s", DIR_WWW, file);
if(file_size(file) == -2) file = sprintf("%s/index.html", file);
if(!strsrch(file, DIR_WWW_GATEWAYS)) {
if(sscanf(file, DIR_WWW_GATEWAYS+"/%s/%s", id, args) != 2) {
args = 0;
sscanf(file, DIR_WWW_GATEWAYS+"/%s", id);
}
if(catch(str=(string)call_other(DIR_WWW_GATEWAYS+"/"+id,"gateway",args))) {
http_error(fd, BAD_GATEWAY);
return;
}
str = strip_colours(str);
}
else if(!file_exists(file)) {
http_error(fd, NOT_FOUND);
return;
}
else str = read_buffer(file);
retry(fd, str);
}
void retry(int fd, string str) {
int res;
if((res = socket_write(fd, str)) != EECALLBACK) {
if(res == EEWOULDBLOCK) call_out("retry", 10, fd, str);
else close_connection(fd);
}
}
int remove() {
socket_close(__SocketHTTP);
return daemon::remove();
}
string format_date(int x) {
string *parts;
string str;
parts = explode(replace_string(ctime((int)TIME_D->query_tzone("GMT")),
" ", " "), " ");
switch(parts[0]) {
case "Mon": str = "Monday, "; break;
case "Tue": str = "Tuesday, "; break;
case "Wed": str = "Wednesday, "; break;
case "Thu": str = "Thursday, "; break;
case "Fri": str = "Friday, "; break;
case "Sat": str = "Saturday, "; break;
case "Sun": str = "Sunday, "; break;
}
str = sprintf("%s%s-%s-%s %s GMT", str, parts[2], parts[1],
parts[4][2..3], parts[3]);
return str;
}