1
0
Fork 0
mirror of git://git.psyced.org/git/psyced synced 2024-08-15 03:25:10 +00:00

let the past begone in cvs land. welcome to igit igit!

This commit is contained in:
PSYC 2009-01-26 20:21:29 +01:00
commit 4e601cf1c7
509 changed files with 77963 additions and 0 deletions

72
world/net/http/call.c Normal file
View file

@ -0,0 +1,72 @@
#include <net.h>
#include <text.h>
#include <ht/http.h>
protected mapping sessions = ([ ]);
string make_session(string nick, int expiry, string jack) {
string sid;
#ifndef TELEPHONY_EXPIRY
# define TELEPHONY_EXPIRY expiry - time()
#endif
while (sessions[sid = RANDHEXSTRING]);
sessions[sid] = ({ nick, expiry, jack });
call_out( (: return m_delete(sessions, sid); :), TELEPHONY_EXPIRY);
return sid;
}
htget(prot, query, headers, qs) {
#ifdef TELEPHONY_SERVER
string sid = query["sid"];
if (!(sid && sessions[sid])) {
// no session found
return;
}
if (sessions[sid][1] < time()) {
// session expired
return;
}
string ni = sessions[sid][0];
string t = query["thats"];
unless (t) {
object uo = find_person(ni);
if (!uo || !sendmsg(uo, "_notice_answer_talk_click",
"Your phone call request has been clicked upon.", ([
"_time_expire" : to_string(sessions[sid][1]),
"_check_call" : sessions[sid][2],
"_session" : sid,
]))) {
hterror(prot, R_GATEWTIMEOUT,
"User cannot be reached.");
return 1;
}
}
htok3(prot, 0, "Expires: 0\n");
localize(query["lang"], "html");
w("_HTML_head");
w("_HTML_call", 0, ([
// "_path_object": "/static/call.swf",
// "_amount_width_object": "330",
// "_amount_height_object": "121",
"_path_object_local": "/static/local.swf",
"_path_object": "/static/remote.swf",
"_amount_width_object": "320",
"_amount_height_object": "240",
// url = rtmp server
"_uniform_server_media": TELEPHONY_SERVER,
// stream infos (thats, user, expiry, jack)
"_QUERY_STRING": qs,
"_role": t ? "I": "U",
"_nick": ni,
"_time_expire": to_string(sessions[sid][1]),
"_check_call" : sessions[sid][2],
]));
w("_HTML_tail");
#else
hterror(prot, R_NOTIMPLEM, // "_error_unavailable_telephony"
"Sorry, no telephony configured on this server.");
#endif
return 1;
}

571
world/net/http/configure.c Normal file
View file

@ -0,0 +1,571 @@
// $Id: configure.c,v 1.58 2007/06/17 21:07:54 lynx Exp $ // vim:syntax=lpc
//
// web configurator interface. does a lot of cool things for you,
// and it should always learn to do some more. if you don't need or
// like it, simply don't load it (from init.ls, or by fetching the url).
//
#include <net.h>
#include <driver.h>
#if HAS_PORT(HTTP_PORT, HTTP_PATH) || HAS_PORT(HTTPS_PORT, HTTP_PATH)
#include <text.h>
#include <person.h>
#include <misc.h>
inherit NET_PATH "sockets";
#ifdef WEB_CONFIGURE
// should this use something else?
# define CONFIGFILE CONFIG_PATH "local.h"
volatile protected mapping description;
void create() {
sTextPath(0, 0, "html");
description = ([
// this has all moved into psyced.ini
// "CHATNAME" : "Name of this Chat Community",
// "DEFPLACE" : "Standard place unregistered users go to",
// "ADMINISTRATORS" : "List of administrators <small>(example: "
// "&quot;admin1&quot;, &quot;admin2&quot;, "
// "&quot;admin3&quot;)</small>",
// "__DOMAIN_NAME__" : "Domain name of this server",
// "SERVER_HOST" : "Address of this server",
// "OSTYPE" : "Your Operating System",
// "MACHTYPE" : "The flavour of your processor",
// optimise on tunings, like this one?
"MAX_VISIBLE_USERS" : "MAX_VISIBLE_USERS",
// a bit absurd: specify a file on the fs instead of having a motd textarea
"MOTD_FILE" : "The file that contains the IRC-MOTD",
]);
}
denyAccess(prot) {
hterror(prot, 200, "You are not allowed to use the configure-page. "
"You need to be from <i>localhost</i> to do that.");
}
parseConfig(fname) {
mapping cv;
int size;
cv = ([ ]);
if (file_size(fname) <= 0) {
return ([ ]);
}
foreach(string line : explode(read_file(fname), "\n")) {
mixed key, value;
if (sscanf(line, "#define%t%s%t%s", key, value)
|| sscanf(line, "#define%t%s", key)
|| sscanf(line, "#%tdefine%t%s%t%s", key, value)
|| sscanf(line, "#%tdefine%t%s", key)
) {
//if (dummy=="" && index(key, ' ')==-1 && index(key, '\t')==-1)
// && (member(description, key) != -1))
cv[key] = value;
}
}
return cv;
}
saveConfig(string fname, mapping config, mapping del) {
string file, key, value, tail;
int added;
unless (mappingp(del)) {
del = ([ ]);
}
if (file_size(fname) <= 0) {
if (abbrev("place/", fname)) {
file = w("_PAGES_configure_room_new");
} else {
return;
}
} else {
file = read_file(fname);
}
added = 0;
foreach (key, value : config) {
// re scheint keine + syntax zu beherrschen
P2(("===> %O\n", value))
if (to_string(to_int(value)) != value && value != "") {
unless (value[0] == '"')
value = "\"" + value;
unless (value[<1] == '"')
value += "\"";
}
if (sizeof(regexp(({ file }),
"#([ \t]*)define([ \t][ \t]*)"+key+
"([ \t][ \t]*)[^\n]*"))) {
tail = "([ \t][ \t]*)[^\n]*";
} else if (sizeof(regexp(({ file }),
"#([ \t]*)define([ \t][ \t]*)"+key))) {
tail = "";
} else {
// adden
file = regreplace(file,
"#include <place.gen>",
"#define "+key+" "+
(value != "" ? " "+value : "")+
"\n#include <place.gen>", 0);
added = 1;
}
unless (value == "" && added == 0) {
file = regreplace(file,
"#([ \t]*)define([ \t][ \t]*)"+key+tail,
"#\\1define "+key+"\t"+value, 0);
} else if (added == 0){
file = regreplace(file,
"#([ \t]*)define([ \t][ \t]*)"+key+tail,
"#\\1define "+key, 0);
}
// old ldmud requires 4th argument
// if (t == file) printf("Could not change %s into %O\n", key, value);
}
foreach (key, value : del) {
file = regreplace(file,
"#([ \t]*)define([ \t][ \t]*)"+key+"[^\n]*\n",
"", 0);
}
rm(fname);
write_file(fname, file);
return w("_PAGES_configure_advice_restart") + "<font size=6><pre>\n" +
"Congratulations, this is your new configuration file:\n" +
"</pre><font color=green><plaintext>\n" + file;
}
htget(prot, query, headers, qs) {
string s; // output buffer
int trustworthy = legal_host(query_ip_number(), HTTP_PORT, "http", 0);
if (trustworthy < 7) {
denyAccess(prot);
return 1;
}
if (file_size(CONFIGFILE) <= 0) {
hterror(prot, 404, "You don't have any configfile, do you?");
return 1;
}
htok3(prot, "text/html", "Cache-Control: no-cache\n");
switch (query["option"]) {
case "user_mgm":
s = w("_PAGES_configure_user",
([ "_title" : "User Management" ]));
break;
case "list_users":
write(w("_PAGES_configure_user_list",
([ "_title" : "User List" ])));
list_sockets();
return 1;
case "block_modify":
if (query["ip"] && query["ip"] != 0) {
object ob;
DAEMON_PATH "hosts"->modify(query["ip"]);
foreach (ob : users()) {
if (abbrev(query["ip"], query_ip_number(ob))) {
log_file("BEHAVIOUR",
"[%s] %O blocks %O (%s)\n",
ctime(), ME, ob, query["ip"]);
ob -> sanction("kill");
}
}
}
case "list_blocks":
write(w("_PAGES_configure_user_block_head",
([ "_title" : "Block Management",
"_submit_value" : "Submit" ])));
DAEMON_PATH "hosts"->list();
return 1;
case "user_password":
s = w("_PAGES_configure_user_password",
([ "_title" : "Reset Password",
"_submit_value" : "Edit" ]));
break;
case "user_password_edit":
{
object o;
unless (o = find_person(query["nick"])) {
unless (o = summon_person(query["nick"])) {
s = w("_PAGES_configure_user_password_failure",
([ "_title" : "Reset Password" ]));
break;
}
}
if (o->isNewbie()) {
destruct(o);
s = w("_PAGES_configure_user_password_failure",
([ "_title" : "Reset Password" ]));
break;
}
s = w("_PAGES_configure_user_password_edit",
([ "_title" : "Reset Password",
"_submit_value" : "Reset Password",
"_nick" : query["nick"] ]));
break;
}
case "user_password_save":
{
object o;
if (! query["password"] || strlen(query["password"]) <= 2) {
s = w("_PAGES_configure_user_password_failure_bad",
([ "_title" : "Reset Password" ]));
break;
}
unless (o = find_person(query["nick"])) {
unless (o = summon_person(query["nick"])) {
s = w("_PAGES_configure_user_password_failure",
([ "_title" : "Reset Password" ]));
break;
}
}
if (o->isNewbie()) {
destruct(o);
s = w("_PAGES_configure_user_password_failure",
([ "_title" : "Reset Password" ]));
break;
}
o->vSet("password", query["password"]);
o->save();
s = w("_PAGES_configure_user_password_save",
([ "_title" : "Reset Password",
"_nick" : query["nick"],
"_password" : query["password"] ]));
break;
}
case "basic_mgm":
s = w("_PAGES_configure_basic",
([ "_title" : "Basic Server Management" ]));
break;
case "restart":
s = w("_PAGES_configure_head",
([ "_title" : "Restart" ]));
s += "<b>Your chatserver is being restarted.</b>";
// write at the bottom wouldn't be called after shutdown, would it?
write(s);
shutdown();
return 1;
case "list_udp":
{
mapping ports;
string port;
s = w("_PAGES_configure_basic_udp_head",
([ "_title" : "List UDP Ports" ]));
ports = DAEMON_PATH "udp"->listPorts(1);
foreach (port : m_indices(ports)) {
s += w("_PAGES_configure_basic_udp_entry",
ports[port]);
}
s += w("_PAGES_configure_basic_udp_tail");
break;
}
case "room_mgm":
s = w("_PAGES_configure_room",
([ "_title" : "Room Management" ]));
break;
case "room_owned":
s = w("_PAGES_configure_room_owned",
([ "_title" : "Manage Owned Rooms",
"_submit_value" : "Edit / Add" ]));
break;
case "room_owned_save":
{
mapping del;
del = ([ ]);
m_delete(query, "option");
if (query["delete"]) {
rm("place/" + lower_case(query["NAME"]) + ".c");
s = w("_PAGES_configure_room_deleted",
([ "_title" : "Room Deleted",
"_name" : query["NAME"] ]));
break;
}
if (query["ALLOW_EXTERNAL"]) {
query["ALLOW_EXTERNAL"] = "";
} else {
del += ([ "ALLOW_EXTERNAL" ]);
}
if (query["REGISTERED"]) {
query["REGISTERED"] = "";
} else {
del += ([ "REGISTERED" ]);
}
s = w("_PAGES_configure_head",
([ "_title" : "Owned Room Management" ]));
s += saveConfig("place/" + lower_case(query["NAME"]) + ".c", query, del);
break;
}
case "room_owned_edit":
{
mapping rs;
string name;
mixed t;
rs = parseConfig("place/" + lower_case(query["NAME"]) + ".c");
P2(("===> %O\n", rs))
unless ((t = isRoomType("OWNED", rs)) == 1) {
if (t != -1) {
s = w("_PAGES_configure_room_error_type",
([ "_title" : "Manage Owned Rooms",
"_name" : query["NAME"],
"_expected" : "Owned",
"_type" : t
]));
} else {
s = w("_PAGES_configure_room_error_type_undefined",
([ "_title" : "Manage Owned Rooms",
"_name" : query["NAME"],
"_expected" : "Owned" ]));
}
break;
}
if (rs["NAME"]) {
name = replace(rs["NAME"], "\"", "");
} else {
name = query["NAME"] || "";
}
s = w("_PAGES_configure_room_owned_edit",
([ "_title" : "Manage Owned Rooms",
"_submit_value" : "Save",
"_name" : name,
"_registered" : member(rs, "REGISTERED") ? " checked" : "",
"_owned" : replace(rs["OWNED"] || "", "\"", "&quot;"),
"_external" : member(rs, "ALLOW_EXTERNAL") ? " checked" : "" ]));
break;
}
case "room_public":
s = w("_PAGES_configure_room_public",
([ "_title" : "Manage Public Rooms",
"_submit_value" : "Edit / Add" ]));
break;
case "room_public_save":
{
mapping del;
del = ([ ]);
m_delete(query, "option");
if (query["delete"]) {
rm("place/" + lower_case(query["NAME"]) + ".c");
s = w("_PAGES_configure_room_deleted",
([ "_title" : "Room Deleted",
"_name" : query["NAME"] ]));
break;
}
if (query["HISTORY"]) {
query["HISTORY"] = "";
} else {
del += ([ "HISTORY" ]);
}
if (query["ALLOW_EXTERNAL"]) {
query["ALLOW_EXTERNAL"] = "";
} else {
del += ([ "ALLOW_EXTERNAL" ]);
}
if (query["REGISTERED"]) {
query["REGISTERED"] = "";
} else {
del += ([ "REGISTERED" ]);
}
s = w("_PAGES_configure_head",
([ "_title" : "Public Room Management" ]));
s += saveConfig("place/" + lower_case(query["NAME"]) + ".c", query, del);
break;
}
case "room_public_edit":
{
mapping rs;
string name;
mixed t;
rs = parseConfig("place/" + lower_case(query["NAME"]) + ".c");
P2(("===> %O\n", rs))
unless (intp(t = isRoomType("PUBLIC", rs))) {
s = w("_PAGES_configure_room_error_type",
([ "_title" : "Manage Public Rooms",
"_name" : query["NAME"],
"_expected" : "Public",
"_type" : t
]));
break;
}
if (rs["NAME"]) {
name = replace(rs["NAME"], "\"", "");
} else {
name = query["NAME"] || "";
}
s = w("_PAGES_configure_room_public_edit",
([ "_title" : "Manage Public Rooms",
"_submit_value" : "Save",
"_name" : name,
"_history" : member(rs, "HISTORY") ? " checked" : "",
"_registered" : member(rs, "REGISTERED") ? " checked" : "",
"_external" : member(rs, "ALLOW_EXTERNAL") ? " checked" : "" ]));
break;
}
case "save_config":
{
s = w("_PAGES_configure_head",
([ "_title" : "Edit Config" ]));
s += saveConfig(CONFIGFILE, query);
break;
}
#if 0
// was um alles in der welt soll das?
case "set":
m_delete(query, "option");
break; // gehört da n break hin?
#endif
case "edit_config":
{
mapping cv;
cv = parseConfig(CONFIGFILE);
// sowas gehört in externe dateien.. am besten .fmt wegen
// mehrsprachigkeit und layouting und template syntax und und..
s = w("_PAGES_configure_head", ([ "_title" : "Edit Config" ]));
s += "<div align=\"center\" style=\"float:clear; text-align:center;\">\n"
"<table bgcolor=\"#004411\" cellspacing=0 cellpadding=3>\n"
"<form method=GET action=configure name=zero>\n"
"<input type=hidden name=option value=\"save_config\">\n";
foreach(string key, string value : cv) {
if (value) {
value = replace(value, "\"", "&quot;");
s += "<tr><td style=\"border-left: 1px solid #f90; border-top: 1px solid #f90;\"><b>" + key + ":</b></td><td style=\"border-top: 1px solid #f90; border-right: 1px solid #f90;\"><input type=\"text\" value=\"" + value
+ "\" name=\"" + key + "\"></td></tr>\n"
"<tr><td colspan=2 style=\"padding-left:10px; border-bottom: 1px solid #fc0; border-right: 1px solid #f90; border-left: 3px solid #fc0;\">" + (description[key] || "") + "</td></tr>\n"
"<tr><td><span style=\"font-size:1px;\">&nbsp;</span></td></tr>\n";
}
}
s += "<tr><td colspan=2 align=center><p>&nbsp;</p>";
s += w("_PAGES_configure_submit",
([ "_submit_value" : "Save" ]));
s += "</td></tr>\n"
"</form></table>\n"
"</div>\n";
break;
}
default:
s = w("_PAGES_configure_menu", ([ "_title" : "Menu" ]));
}
write(s);
}
#else
htget(prot, query, headers, qs) {
hterror(prot, 501, "This server hasn't enabled webbased administration");
}
#endif
isRoomType(expected, mapping rs) {
string type;
unless (sizeof(rs)) {
return 1;
}
foreach (type : filter(({ "OWNED", "MODERATED", "NEWSFEED_RSS",
"SLAVE", "CONNECTED_IRC", "THREADS",
"GAMESERV", "LINK", "PUBLIC" }),
lambda( ({ 'x }), ({#'?, ({ #'!=, 'x, expected }) })) )) {
if(member(rs, type))
return type[0..0] + lower_case(type[1..]);
}
if (member(rs, expected)) {
return 1;
}
return -1;
}
w(mc, mixed vars, bla) {
if (bla) {
vars = bla;
}
if (mc == "_list_user_technical") {
unless (mappingp(vars))
vars = ([ ]);
write(psyctext(T(mc, ""), vars));
return;
}
unless (mappingp(vars)) {
return T(mc, "");
}
return psyctext(T(mc, ""), vars);
}
// only used for HOSTS_D
pr(mc, text, vars) {
string s, ip, reason;
// we'll keep it this way, yet
switch (mc) {
case "_list_hosts_disabled":
s = "";
foreach (ip, reason: vars) {
s += w(mc,
([ "_ip" : ip,
"_reason" : reason ]));
}
}
write(s);
}
#endif

154
world/net/http/edit.c Normal file
View file

@ -0,0 +1,154 @@
// this code contributed from symlynX webchat. currently not in use.
#include <net.h>
#include <text.h>
#include <person.h>
#include <ht/http.h>
#include <url.h>
#define NO_INHERIT
#include <user.h>
#undef NO_INHERIT
// #define v(VAR) user->vQuery(VAR)
htget(prot, query, headers, qs) {
string nick, lang, layout, token;
mixed user;
mapping _v, sups;
int newbie;
P2(("edit query: %O\n", query))
nick = query["user"];
user = find_person(nick);
token = query["token"];
lang = query["lang"];
sTextPath(0, lang, "ht");
htok(prot);
unless (nick && user && user->validToken(token)) {
write( hthead("edit") +
T("_PAGES_edit_invalid", "Who are you?<br>"));
return 1;
}
// unless (newbie = user->isNewbie()) _v = user->vMapping();
_v = user->vMapping() || ([]);
unless (lang) lang = v("language");
//newbie = v("password") == 0;
newbie = user->isNewbie();
if (v("name")) nick = v("name");
sups = v("subscriptions") || ([]);
#define FORM_HIDDEN "\n\
<input type=hidden name=user value=\""+nick+"\">\n\
<input type=hidden name=token value=\""+token+"\">\n\
<input type=hidden name=lang value=\""+lang+"\">\n\
<input type=hidden name=layout value=\""+layout+"\">"
#define FORM_OPTS " name=f method=GET action=\"/"+layout+"/modify\""
#ifdef VOLATILE
# define FORM_START "<form" FORM_OPTS
# define FORM_PAGE "_PAGES_register_newsletter"
#else
# define FORM_START "<form target=cout_" CHATNAME FORM_OPTS
# define FORM_PAGE newbie? "_PAGES_register_form": "_PAGES_edit_form"
#endif
// here comes the most obvious demonstration
// of the necessity of the psyctext parser :)
write(T("_SCRIPT_edit", 0)
+ hthead(newbie? "register": "edit")
+ psyctext(T(FORM_PAGE, 0), ([
// "_FORM_start" : FORM_START ">" FORM_HIDDEN,
// "_FORM_start_check_email" : FORM_START "\
onSubmit=\"return checkEmail(f.email.value)\">" FORM_HIDDEN,
"_FORM_start" : FORM_START "\
onSubmit=\"return check(f)\">" FORM_HIDDEN,
"_FORM_end" : "</form>",
"_nick" : nick,
"_EDIT_nick" : v("name") || nick,
"_EDIT_nick_size" : strlen(v("name") || nick),
"_EDIT_email" : v("email") || "",
#if 1
"_EDIT_subscription_events_weekly"
: member(sups,"events-weekly")? "checked": "",
"_EDIT_subscription_politik_digital"
: member(sups,"poldi")? "checked": "",
#ifdef VOLATILE
"_EDIT_subscription_events" : "checked",
#else
"_EDIT_subscription_events" : newbie||member(sups,"events")?
"checked": "",
# ifdef NEWSTICKER
"_EDIT_subscription_politik" : member(sups,"dpa-pl")? "checked": "",
"_EDIT_subscription_wirtschaft" : member(sups,"dpa-wi")? "checked": "",
"_EDIT_subscription_vermischtes": member(sups,"dpa-vm")? "checked": "",
# endif
#endif
"_EDIT_place_home" : v("home") || "",
"_EDIT_color" : v("color") || "",
// no longer up to date here..
// might not behave exactly as intended
"_EDIT_checkbox_filter" : v("filter") == FILTER_STRANGERS ?
"checked" : "",
"_EDIT_checkbox_colors_ignored" : v("ignorecolors") ? "checked" : "",
"_EDIT_checkbox_visibility" : v("visibility") == "off" ?
"" : "checked",
"_EDIT_action_speak" : v("speakaction") || "",
"_EDIT_action_motto" : v("me") || "",
"_EDIT_page_public" : v("publicpage") || "",
"_EDIT_page_private" : v("privatepage") || "",
"_EDIT_file_key_public" : v("keyfile") || "",
"_EDIT_description_private" : v("privatetext") || "",
"_EDIT_description_public" : v("publictext") || "",
"_EDIT_description_preferences" : v("likestext") || "",
"_EDIT_description_preferences_not" : v("dislikestext") || "",
"_EDIT_favorite_animal" : v("animalfave") || "",
"_EDIT_favorite_popstar" : v("popstarfave") || "",
"_EDIT_favorite_music" : v("musicfave") || "",
"_EDIT_page_start" : v("startpage") || "",
#endif
])
));
write("<div class='roomsub'>Room Subscriptions<br/>\n");
foreach(mixed key, mixed val : v("subscriptions")) {
// note: the immediateness is not really used these days
write("<div class='each'><a href='");
unless(is_formal(key)) {
write(query_server_unl() + "@" + key);
} else {
write(key);
}
write("'>" + key + "</a></div>\n");
}
write("</div>\n");
// build unified data structure for contacts
mapping paliases = ([ ]);
foreach(mixed key, mixed val : v("people")) {
paliases[key] = ({ 0, val });
}
foreach(mixed key, mixed val : v("aliases")) {
if (paliases[val])
paliases[val][0] = key;
else
paliases[val] = ({ key, 0 });
}
// make a link to a edit page for each contact?
write("<div class='friends'>Friends<br/>\n");
foreach(mixed key, mixed val : paliases) {
// note: look at val to determine status
write("<div class='each'><a href='" + key);
write("'>" + (val[0] || key) + "</a>");
if (val[1])
write(sprintf("subscription state %O\n", val[1]));
write("</div>\n");
}
write("</div>\n");
return 1;
}

29
world/net/http/examine.c Normal file
View file

@ -0,0 +1,29 @@
#include <net.h>
#include <text.h>
#include <person.h>
htget(prot, query, headers, qs) {
string nick, t;
object user;
htok3(prot, 0, "Expires: 0\n");
if (nick = query["user"]) user = find_person(nick);
unless (user) {
// should be a different mc here..
t = "_error_invalid_authentication_token";
} else unless (user->validToken(query["token"])) {
t = "_error_invalid_authentication_token";
} else if ((t = query["cmd"]) && strlen(t)) {
user->parsecmd(t); // htcmd?
t = "_echo_execute_web";
} else if (t = user->htDescription(prot, query, headers, qs, "")) {
P4(("result: %O\n", t))
write(t);
return 1;
}
localize(query["lang"], "html");
w("_HTML_head");
w(t || "_failure_unavailable_description");
w("_HTML_tail");
return 1;
}

214
world/net/http/fetch.c Normal file
View file

@ -0,0 +1,214 @@
// $Id: fetch.c,v 1.39 2008/04/09 08:29:37 lynx Exp $ // vim:syntax=lpc
//
// generic HTTP GET client, mostly used for RSS -
// but we could fetch any page or data with it, really
// tobij even made the object have the URL as its object name. fancy! ;)
//
#include <net.h>
#include <ht/http.h>
#include <url.h>
#include <services.h>
virtual inherit NET_PATH "output"; // virtual: in case we get inherited..
inherit NET_PATH "connect";
//inherit NET_PATH "place/master";
#ifdef NEW_QUEUE
inherit NET_PATH "queue2";
#else
inherit NET_PATH "queue";
#endif
volatile mapping headers, fheaders;
volatile string modificationtime, etag, http_message;
volatile string useragent = SERVER_VERSION;
volatile int http_status, port, fetching, ssl;
volatile string buffer, thehost, url, fetched, host, resource;
volatile string basicauth;
int parse_status(string all);
int parse_header(string all);
int buffer_content(string all);
string qHost() { return thehost; }
void fetch(string murl) {
if (url) return;
url = replace(murl, ":/", "://");
P3(("%O: fetch(%O)\n", ME, url))
connect();
}
object load() { return ME; }
void setHTTPBasicAuth(string user, string password) {
basicauth = "Authorization: Basic " + encode_base64(user + ":" + password) + "\r\n";
}
string sAgent(string a) {
PT(("sAgent(%O) in %O\n", a, ME))
return useragent = a;
}
// net/place/news code follows.
void connect() {
mixed t;
fetching = 1;
ssl = 0;
unless (thehost) {
unless (sscanf(url, "http%s://%s/%!s", t, thehost)) {
P0(("%O couldn't parse %O\n", ME, url))
return 0;
}
thehost = lower_case(thehost);
ssl = t == "s";
}
P3(("URL, THEHOST: %O, %O\n", url, thehost))
unless (port)
unless (sscanf(thehost, "%s:%d", thehost, port) == 2)
port = ssl? HTTPS_SERVICE: HTTP_SERVICE;
P2(("Resolving %O and connecting.\n", thehost))
::connect(thehost, port);
}
varargs int real_logon(int arg) {
string scheme;
headers = ([ ]);
http_status = 500;
http_message = "(failure)"; // used by debug only
unless(::logon(arg)) return -1;
unless (url) return -3;
unless (resource) sscanf(url, "%s://%s/%s", scheme, host, resource);
buffer = "";
if (modificationtime)
buffer += "If-Modified-Since: "+ modificationtime + "\r\n";
if (useragent) buffer += "User-Agent: "+ useragent +"\r\n";
//if (etag)
// emit("If-None-Match: " + etag + "\r\n");
// we won't need connection: close w/ http/1.0
//emit("Connection: close\r\n\r\n");
PT(("%O using %O\n", ME, buffer))
emit("GET /"+ resource +" HTTP/1.0\r\n"
"Host: "+ host +"\r\n"
+ buffer +
"\r\n");
buffer = "";
next_input_to(#'parse_status);
return 0; // duh.
}
varargs int logon(int arg, int sub) {
// when called from xmlrpc.c we can't do TLS anyway
if (sub) return ::logon(arg);
if (ssl) tls_init_connection(ME, #'real_logon);
else real_logon(arg);
return 0; // duh.
}
int parse_status(string all) {
string prot;
string state;
sscanf(all, "%s%t%s", prot, state);
sscanf(state, "%d%t%s", http_status, http_message);
P3(("%O got %O %O from %O\n", ME, http_status, http_message, host));
// P2(("http_status %O == %O?\n", http_status, R_OK))
next_input_to(#'parse_header);
return 1;
}
int parse_header(string all) {
string key, val;
D2(D("htroom::parse is: " + all + "\n");)
// TODO: parse status code
if (all != "") {
if (sscanf(all, "%s:%1.0t%s", key, val) == 2) {
headers[lower_case(key)] = val;
// P2(("ht head: %O = %O\n", key, val))
}
next_input_to(#'parse_header);
return 1;
} else {
// das wollen wir nur bei status 200
next_input_to(#'buffer_content);
return 1;
}
return 1;
}
int buffer_content(string all) {
buffer += all + "\n";
next_input_to(#'buffer_content);
return 1;
}
disconnected(remainder) {
headers["_fetchtime"] = isotime(ctime(time()), 1);
if (headers["last-modified"])
modificationtime = headers["last-modified"];
if (headers["etag"])
etag = headers["etag"]; // heise does not work with etag
fetched = buffer;
if (remainder) fetched += remainder;
fheaders = headers;
buffer = headers = 0;
switch (http_status) {
case R_OK:
mixed *waiter;
while (qSize(ME)) {
waiter = shift(ME);
funcall(waiter[0], fetched, waiter[1] ? fheaders : copy(fheaders));
}
break;
default:
monitor_report("_error_unknown_method_HTTP",
S("http/fetch'ing %O returned %O %O", url || ME,
http_status, http_message));
// fall thru
case R_NOTMODIFIED:
qDel(ME);
qInit(ME, 150, 5);
}
fetching = 0;
return 1; // presume this disc was expected
}
varargs string content(closure cb, int force, int willbehave) {
if (cb) {
if (fetched) {
if (force) {
funcall(cb, fetched, willbehave ? fheaders : copy(fheaders));
}
} else {
enqueue(ME, ({ cb, willbehave }));
}
}
return fetched;
}
varargs mapping headers(int willbehave) {
return willbehave ? fheaders : copy(fheaders);
}
string qHeader(mixed key) {
if (mappingp(fheaders)) return fheaders[key];
return 0;
}
varargs void refetch(closure cb, int willbehave) {
enqueue(ME, ({ cb, willbehave }));
unless (fetching) connect();
}
protected create() {
qCreate();
qInit(ME, 150, 5);
}

42
world/net/http/header.i Normal file
View file

@ -0,0 +1,42 @@
// vim:noexpandtab:syntax=lpc
// $Id: header.i,v 1.5 2007/10/08 11:00:31 lynx Exp $
#include <ht/http.h>
volatile int headerDone = 0;
http_ok(string prot, string type, string extra) {
string h;
// yes, this is compatible to pre-HTTP/1.0 browsers. sick, i know.
if (!prot || headerDone++) return;
h = type || extra ? htheaders(type, extra) +"\n"
: "Content-type: " DEFAULT_CONTENT_TYPE "\n\n";
emit(HTTP_SVERS " 200 Sure\n"+ h);
}
varargs http_error(string prot, int code, string comment, string html) {
string out;
out = "<body text=white bgcolor=black link=green vlink=green>\n";
if (html) out = sprintf("<title>%s</title>\n%s%s", comment, out, html);
else out = sprintf("\
<title>error %d</title>\n\
%s\n\
<table width=\"100%%\" height=\"90%%\"><tr><th><h1><br>\n\n\
%s\n\n\
</h1></th></tr></table>\n\
",
code, out, comment
);
// <a href=\"mailto:%s?subject=%s\">%s</a>\n
//, WEBMASTER_EMAIL, comment, WEBMASTER_EMAIL
// yes, this is compatible to pre-HTTP/1.0 browsers. sick, i know.
if (!headerDone++ && prot) {
// I used to output the comment, but Id have to cut out the
// newline from the db
emit(sprintf(HTTP_SVERS " 200 Actually %03d but MSIE steals my error page\n%s\n%s", code, htheaders(), out));
} else emit(out);
}

178
world/net/http/library.i Normal file
View file

@ -0,0 +1,178 @@
// vim:noexpandtab:syntax=lpc
// $Id: library.i,v 1.38 2008/04/08 23:12:16 lynx Exp $
//
// HTTP function library -lynx
//
// (extension to "simul_efun")
#include <net.h>
#include <services.h>
#include "driver.h"
//#include CONFIG_PATH "ports.h"
#include "ht/http.h"
#if !HAS_PORT(HTTP_PORT, HTTP_PATH)
# undef HTTP_PORT
# define HTTP_PORT 44444 // should return null instead?
#endif
// not worthy of summoning url.h for this one..
string htuniform(object server, string prot, mapping headers) {
// it is really totally crazy to trust what the browser says
string host = headers["host"];
int port = HTTP_PORT;
#if __EFUN_DEFINED__(tls_query_connection_state)
#ifndef HTTPS_HOST
string scheme = "http://";
# endif
unless (server) server = this_interactive();
if (tls_query_connection_state(server)) {
#ifdef HTTPS_HOST
return HTTPS_HOST;
#else
scheme = "https://";
# if HAS_TLS_PORT(HTTPS_PORT)
port = HTTPS_PORT;
# endif
#endif
}
# define SCHEME scheme
# define PORT port
#else
# define SCHEME "http://"
# define PORT HTTP_PORT
#endif
unless (host) {
#ifdef HTTP_HOST
return HTTP_HOST;
#else
host = SERVER_HOST;
// HTTP_SERVICE is 80 from services.h
if (port != HTTP_SERVICE) host += ":"+to_string(port);
#endif
}
return SCHEME + host;
}
varargs string htheaders(string type, string extra) {
string out;
unless (stringp(type)) type = DEFAULT_CONTENT_TYPE;
out = "Content-type: "+ type +"\n";
// if (length) {
// printf("Content-length: %d\n", length);
// }
if (extra) out += extra;
return out;
}
varargs string htheaders2(string type, string extra) {
return htheaders(type, extra)
+ "Server: " HTTP_SERVER "\nDate: "+ ctime(time()) +"\n";
}
void htrequireauth(string prot, string type, string realm) {
if (prot) write(HTTP_SVERS + " " + to_string(R_UNAUTHORIZED) +
"Your password, please\n");
if (type == "digest") {
write("WWW-Authenticate: Digest realm=\"" + realm +
"\", nonce=\"" + time() + "\"\n");
} else {
write("WWW-Authenticate: Basic realm=\"" + realm + "\"\n");
}
}
varargs string htredirect(string prot, string target, string comment, int permanent) {
if (!comment) comment = "Check this out";
if (!target) target = "/";
if (prot) {
printf("%s %d %s\n%s", HTTP_SVERS,
permanent ? R_MOVED : R_FOUND, comment, htheaders());
}
printf("\
Location: %s\n\
\n\
<a href=\"%s\">%s</a>.\n\
",
target, target, comment);
return 0;
}
// written and donated by _Marcus_
//
// thanx!!
/* convert %XX escapes to real chars */
static int xx2l(string str) {
int x;
str=lower_case(str);
if (str[0]>='0' && str[0]<='9') x=(str[0]-'0')*16;
if (str[0]>='a' && str[0]<='f') x=(str[0]-'a'+10)*16;
if (str[1]>='0' && str[1]<='9') x+=str[1]-'0';
if (str[1]>='a' && str[1]<='f') x+=str[1]-'a'+10;
return x;
}
/* Content-Encoding: url-encoded */
string urldecode(string txt) {
string *xx;
int i;
txt=implode(explode(txt,"+")," ");
#if __EFUN_DEFINED__(regexplode)
xx=regexplode(txt,"%..");
for (i=1;i<sizeof(xx)-1;i+=2)
xx[i]=sprintf("%c",xx2l(xx[i][1..]));
txt=implode(xx,"");
#endif
return txt;
}
#ifndef DEFAULT_HT_TYPE
# define DEFAULT_HT_TYPE "text/plain"
#endif
string content_type(string suffix) {
switch(lower_case(suffix)) {
case "html":
return "text/html";
case "xml":
//return "application/xml";
return "text/xml"; // what type is to be used here?
case "bz2":
return "application/x-bzip2";
case "class":
return "application/octet-stream";
case "css":
return "text/css";
case "gif":
return "image/gif";
case "gz":
return "application/x-gzip";
case "jpeg":
case "jpg":
return "image/jpeg";
case "js":
return "application/x-javascript";
case "pdf":
return "application/pdf";
case "png":
return "image/png";
case "ps":
case "eps":
return "application/postscript";
case "tar":
return "application/tar";
case "zip":
return "application/zip";
case "swf":
return "application/x-shockwave-flash";
default:
return DEFAULT_HT_TYPE;
}
return 0;
}

26
world/net/http/login.c Normal file
View file

@ -0,0 +1,26 @@
#include <net.h>
#include <text.h>
#include <person.h>
htget(prot, query, headers, qs) {
string nick, t;
object user;
if (nick = query["user"]) user = find_person(nick);
unless (user) {
// should be a different mc here..
t = "_error_invalid_authentication_token";
} else unless (user->validToken(query["token"])) {
t = "_error_invalid_authentication_token";
} else {
PT(("replacing cookie %O\n", headers["cookie"]))
htok3(prot, 0, "Set-Cookie: psyced=\""+ qs +"\";\n");
t = "_PAGES_login";
}
htok3(prot, 0, "Expires: 0\n");
localize(query["lang"], "html");
w("_HTML_head");
w(t);
w("_HTML_tail");
return 1;
}

140
world/net/http/modify.c Normal file
View file

@ -0,0 +1,140 @@
// this code contributed from symlynX webchat. currently not in use.
#include <net.h>
#include <text.h>
#include <ht/html.h>
#include <ht/http.h>
#include <person.h>
volatile string nick;
volatile mixed user;
htget(prot, query, headers, qs) {
string key, value, k;
int okay;
P2(("\n\nQuery: %O\n", query))
nick = query["user"];
localize(query["lang"], "ht");
htok(prot);
write (hthead("modifying "+nick+"'s settings") + htfs_on "<th>");
unless (nick) {
write( T("_PAGES_register_nickless", "Who are you?")
+ htfs_off);
return 1;
}
unless (user = find_person(nick)) {
write( T("_PAGES_register_offline",
"You're not in the chat?<br>Please enter it.")
+ T("_HTML_back", "") + htfs_off );
return 1;
}
unless (user -> validToken(query["token"])) {
write( T("_error_invalid_authentication_token",
"Invalid token. Please log in anew.")
+ htfs_off);
return 1;
}
okay = 1;
// endlich mal hier verallgemeinern. damn! TODO
#ifdef NEWSTICKER
user->subscribe(member(query, "sub-events") ? SUBSCRIBE_TEMPORARY : SUBSCRIBE_NOT, "events", 1);
user->subscribe(member(query, "sub-events-weekly") ? SUBSCRIBE_TEMPORARY : SUBSCRIBE_NOT, "events-weekly", 1);
#endif
// sonderfall wegen logischer inversion :(
key = "visibility";
user -> vSet(key, query[key] || "off");
unless (query["colors"]) user -> vDel("ignorecolors");
unless (query["filter"]) user -> vDel("filter");
// statt einzelauswertung sollte man einen _request_store
// erzeugen ähnlich zu den vCards die jabber clients
// schicken.
//
foreach (key : query) switch(k = lower_case(key)) {
case "sub-poldi":
case "sub-events":
case "sub-events-weekly":
case "sub-dpa-PL":
case "sub-dpa-WI":
case "sub-dpa-VM":
case "opass":
case "pass":
case "pass2":
case "token":
case "layout":
case "user":
case "lang": // should this be renamed to "language" ??
case "x":
case "y":
case "visibility":
break;
case "me":
value = query[key];
if (!value || value == "" || value == "-")
user->vDel(k);
else user->vSet(k, value);
break;
default:
// if (trail("text", k)) {
// // catch CR?
// }
value = query[key];
// D("checking ("+key+", "+value+")\n");
if (user -> checkVar(&k, &value)) {
// D("valid as "+value+"\n");
if (!value || value == "" || value == "-")
user->vDel(k);
else user->vSet(k, value);
} else okay = 0;
}
#ifndef VOLATILE
okay = okay && changePassword(query);
#endif
if (okay) {
#ifndef VOLATILE
user -> save();
#else
log_file("NEWSLETTER", "[%s] %O %O\n\n",
ctime(), query_ip_name(), query);
#endif
write( T("_PAGES_edit_stored",
"Settings successfully stored.")
+ T("_HTML_back", "") + htfs_off );
} else
write( T("_HTML_back", "") + htfs_off );
return 1;
}
#ifndef VOLATILE
static changePassword(query) {
string pw;
string* t;
pw = query["pass"];
unless (pw && pw != "") return 1;
if (t = legal_password(pw, nick)) {
write( T(t[0],t[1]) );
return 0;
}
if (pw != query["pass2"]) {
write( T("_error_invalid_password_repetition",
"Sorry, but the repetition does not match the password.<br>Please check.") );
return 0;
}
if (user ->checkPassword(query["opass"])) {
string token;
user->vSet("password", pw);
} else {
printf( T("_error_invalid_password",
"You provided an invalid password, %s."), nick );
return 0;
}
return 1;
}
#endif

View file

@ -0,0 +1,11 @@
#include <net.h>
#include <text.h>
#include <ht/http.h>
// phone call thing. we need to pass the query data to a flash film
// but we don't have any flash film yet. TODO
//
htget(prot, query, headers, qs) {
htredirect(prot, "/static/myVideo.swf", "stupid flash film", 0);
return 1;
}

212
world/net/http/profile.c Normal file
View file

@ -0,0 +1,212 @@
// $Id: profile.c,v 1.2 2007/04/11 13:48:57 lynx Exp $ vim:syntax=lpc
//
// <kuchn> profile.c, a web based user settings/profile changer,
// called from net/http/server.
//
// TODO: please make use of the new convert_profile(vars, 0, "set") and
// convert_profile(settings, "set", 0) to convert a mapping of settings
// into a mapping of PSYC variables and back. this should make a lot of
// code in here unnecessary. avoiding replication is g00000d.
//
// i'm unhappy with the way of authing.. first i thought this could be
// inherited somehow to be a part of the user object, but thats schmarn.
// but i think psyc auth would be a nice way here..
// using checkPassword() the way you do is fine! we are not planning
// to use a web-based editor for remote PSYC items.. and the
// asynchronicity of it isn't something HTTP can easily handle.
// HTTP isn't as cool as PSYC you know? hahahahahahahahahahah
// as long as this is in development it could cause security breaches
// in production servers. so please only use this on experimental servers.
#ifdef EXPERIMENTAL
#include <net.h>
#include <text.h>
#include <person.h>
object user;
create() {
sTextPath(0, 0, "html");
}
htget(prot, query, headers, qs) {
htok3(prot, "text/html", "Cache-Control: no-cache\n");
w("_PAGES_user_header");
auth(prot, query, headers, qs);
}
checkAuth(val, prot, query, headers, qs, user) {
if(! val)
{
w("_PAGES_user_login_failed");
return;
}
w("_PAGES_user_header");
switch(query["action"]) {
case "settings":
if(query["set"] == "1")
settings(prot, query, headers, qs, user);
else
w("_PAGES_user_settings_body", ([
"_username" : query["username"],
"_password" : query["password"],
"_speakaction" : user->v("speakaction") ? user->v("speakaction") : "",
"_commandcharacter" : user->v("commandcharacter") ? user->v("commandcharacter") : ""
]) );
break;
case "profile":
if(query["set"] == "1")
profile(prot, query, headers, qs, user);
else
w("_PAGES_user_profile_body", ([
"_username" : query["username"],
"_password" : query["password"],
"_me" : user->v("me") ? user->v("me") : "",
"_publicpage" : user->v("publicpage") ? user->v("publicpage") : "",
"_publictext" : user->v("publictext") ? user->v("publictext") : "",
"_publicname" : user->v("publicname") ? user->v("publicname") : "",
"_animalfave" : user->v("animalfave") ? user->v("animalfave") : "",
"_popstarfave" : user->v("popstarfave") ? user->v("popstarfave") : "",
"_musicfave" : user->v("musicfave") ? user->v("musicfave") : "",
"_privatetext" : user->v("privatetext") ? user->v("privatetext") : "",
"_likestext" : user->v("likestext") ? user->v("likestext") : "",
"_dislikestext" : user->v("dislikestext") ? user->v("dislikestext") : "",
"_privatepage" : user->v("privatepage") ? user->v("privatepage") : "",
"_email" : user->v("email") ? user->v("email") : "",
"_color" : user->v("color") ? user->v("color") : "",
"_language" : user->v("language") ? user->v("language") : "",
"_telephone" : user->v("telephone") ? user->v("telephone") : ""
]) );
break;
default:
w("_PAGES_user_index", ([ "_username" : query["username"], "_password" : query["password"] ]));
break;
}
w("_PAGES_user_footer");
}
auth(prot, query, headers, qs) {
if(! stringp(query["username"]) || ! stringp(query["password"]))
{
w("_PAGES_user_login_body");
return 0;
}
if(query["username"] == "" || query["password"] == "")
{
w("_PAGES_user_login_empty");
return 0;
}
user = summon_person(query["username"]);
if(!user || user->isNewbie())
{
w("_PAGES_user_login_notregistered");
return 0;
}
int ok = 1;
user->checkPassword(query["password"], "plain", "", "", #'checkAuth, prot, query, headers, qs, user);
}
settings(prot, query, headers, qs, user) {
foreach(string k, string v : query) {
int ok = 0;
switch(k) {
case "password":
ok = 1;
break;
case "speakaction":
ok = 1;
break;
case "commandcharacter":
ok = 1;
break;
}
if(! ok)
continue;
if(v == "")
{
//if(v != "password")
// user->vDel(k);
// do nothing? maybe every setting is needed.. then this would be bad.
}
else
user->vSet(k, v); // password won't be set? humm.
}
w("_PAGES_user_settings_changed", ([ "_username" : query["username"], "_password" : query["password"] ]) );
}
profile(prot, query, headers, qs, user) {
foreach(string k, string v : query) {
int ok = 0;
switch(k)
{
case "me":
ok = 1;
break;
case "publicpage":
ok = 1;
break;
case "publictext":
ok = 1;
break;
case "publicname":
ok = 1;
break;
case "animalfave":
ok = 1;
break;
case "popstarfave":
ok = 1;
break;
case "musicfave":
ok = 1;
break;
case "privatetext":
ok = 1;
break;
case "likestext":
ok = 1;
break;
case "dislikestext":
ok = 1;
break;
case "privatepage":
ok = 1;
break;
case "email":
ok = 1;
break;
case "telephone":
ok = 1;
break;
}
if(! ok)
continue;
if(v == "")
user->vDel(k);
else
user->vSet(k, v);
}
w("_PAGES_user_profile_changed", ([ "_username" : query["username"], "_password" : query["password"] ]) );
}
w(mc, vars) { write(psyctext(T(mc, ""), vars)); }
#endif

76
world/net/http/register.c Normal file
View file

@ -0,0 +1,76 @@
// this code contributed from symlynX webchat. currently not in use.
#include <net.h>
#include <text.h>
#include <person.h>
htget(prot, query, headers, qs) {
string nick, pw, email, lang, layout;
mixed user;
array(string) t;
if (mappingp(query)) {
nick = query["user"];
email = query["email"];
pw = query["pass", 0];
lang = query["lang", 0];
}
sTextPath(0, lang, "ht");
htok(prot);
unless (nick) {
printf(hthead("register") + T("_PAGES_register_nickless",
"Who are you?<br>"));
return 1;
}
if (!pw || pw == "" && !email) {
write( hthead("register") + psyctext(
T("_PAGES_register_form", 0), ([
"_nick": nick,
"_FORM_start": "\
<form name=f method=GET action=\""+object2url(ME)+"\">\n\
<input type=hidden name=user value=\""+nick+"\">\n\
<input type=hidden name=lang value=\""+lang+"\">\n\
<input type=hidden name=layout value=\""+layout+"\">",
"_EDIT_email": email || "",
"_EDIT_subscription_politik": "checked",
"_EDIT_subscription_wirtschaft": "checked",
"_EDIT_subscription_vermischtes": "checked",
"_FORM_end": "</form>",
]) ));
return 1;
}
if (t = legal_password(pw, nick)) {
write(hthead("illegal password") +
T(t[0],t[1]) + T("_HTML_back", "") );
return 1;
}
if (pw != query["pass2"]) {
write(hthead("mistyped") +
T("_error_invalid_password_repetition",
"Sorry, but the repetition does not match the password. Please check.") +
T("_HTML_back", "") );
return 1;
}
unless (user = find_person(nick)) {
write(hthead("register") +
T("_PAGES_register_offline",
"You're not in the chat?<br>Please enter it.\n"));
return 1;
}
if (user ->checkPassword()) {
string token;
user->vSet("password", pw);
if (email) user->vSet("email", email);
#ifdef NEWSTICKER
user->subscribe(!member(query, "sub-dpa-PL"), "dpa-PL", 1);
user->subscribe(!member(query, "sub-dpa-WI"), "dpa-WI", 1);
user->subscribe(!member(query, "sub-dpa-VM"), "dpa-VM", 1);
#endif
return 1;
}
write( hthead("register") + T("_PAGES_register_taken",
"This nickname is already registered."));
return 1;
}

235
world/net/http/server.c Normal file
View file

@ -0,0 +1,235 @@
// $Id: server.c,v 1.58 2008/03/11 13:42:25 lynx Exp $ // vim:syntax=lpc
//
// yes, psyced is also a web server, like every decent piece of code. ;)
//
#include <net.h>
#include <server.h>
#include <text.h>
#include "header.i"
volatile string url, file, qs, version;
volatile mapping headers;
// 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 :)
parse_url(input);
parse_header(input);
devNull();
qScheme() { return "none"; }
logon() {
D2(D("»»» New SmallHTTP user\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
next_input_to(#'parse_url);
call_out(#'quit, TIME_LOGIN_IDLE);
}
disconnected(remainder) {
// TODO: shouldn't ignore remainder
D2(D("««« SmallHTTP got disconnected.\n");)
destruct(ME);
return 1; // expected death of socket
}
// gets called from async apps
done() { quit(); }
#if DEBUG > 1
quit() {
D2(D("««« SmallHTTP user done.\n");)
::quit();
}
#endif
void create() {
if (clonep(ME)) headers = ([ ]);
}
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);
}
parse_url(input) {
P3(("=== SmallHTTP got: %O\n", input))
unless (sscanf(input, "GET%t%s%tHTTP/%s", url, version)) {
if (sscanf(input, "CONNECT%t%~s")) {
next_input_to(#'parse_wait);
return;
} else {
quit();
return;
}
}
version = "HTTP/" + version;
P2(("=== SmallHTTP user requested url: %O\n", url))
next_input_to(#'parse_header);
}
parse_header(input) {
string key, val;
P4((input + "\n"))
unless (input == "") {
if (sscanf(input, "%s:%1.0t%s", key, val)) {
headers[lower_case(key)] = val;
}
next_input_to(#'parse_header);
} else {
process();
next_input_to(#'devNull);
}
}
parse_query(query, qs) {
foreach (string pair : explode(qs, "&")) {
string key, val;
if (sscanf(pair, "%s=%s", key, val)) {
P3(("query: pair: %s, %s\n", urldecode(key),
urldecode(val)))
query[urldecode(key)] = urldecode(val);
} else {
P3(("query: single: %s\n", urldecode(pair)))
query[urldecode(pair)] = 1;
}
}
return query;
}
process() {
string t, ext;
mapping query = ([]);
object o;
int done = 1;
// take defaults from cookie, then override by query string
t = headers["cookie"];
P4(("found cookie: %O\n", t))
if (t && sscanf(t, "psyced=\"%s\"", t)) {
P3(("got cookie: %O\n", t))
query = parse_query(query, t);
P4(("parsed cookie: %O\n", query))
}
if (sscanf(url, "%s?%s", file, qs)) {
P3(("got query: %O\n", qs))
query = parse_query(query, qs);
} else {
file = url;
}
P4(("parsed query: %O\n", query))
switch (file) {
case "/favicon.ico":
#if 0
htredirect(version, "http://www.psyced.org/favicon.ico",
"This one looks neat", 1);
quit();
return 1;
#else
file = "/static/favicon.ico";
break;
#endif
case "/":
case "":
// should we look for text/wml in the accept: and go directly
// to /net/wap/index ?
//
http_ok(version);
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.
htredirect(version, "/static/", "use the trailing slash", 1);
quit();
return 1;
case "/static/":
file = "/static/index.html";
break;
}
switch (file[1]) {
case '~':
if (o = summon_person(file[2..], NET_PATH "user")) {
o->htinfo(version, query, headers, qs);
}
quit();
return 1;
case '@':
file = PLACE_PATH+ lower_case(file[2..]);
break;
default:
if (abbrev("/static/", file)) {
if (file_size(file) > 0) {
if (sscanf(file, "%!s.%s", ext)) {
while (sscanf(ext, "%!s.%s", ext)) ;
}
http_ok(version, content_type(ext), 0);
binary_message(read_file(file));
quit();
return 1;
}
} else if (sscanf(file, "/%s/%s.page", ext, t) == 2) {
http_ok(version);
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;
}
}
if (index(file, ':') != -1) {
http_error(version, 501, "Not Implemented. Whatever you are trying "
"there, this server won't help you.");
quit();
return;
}
o = file -> load();
if (objectp(o) || o = find_object(file))
done = o->htget(version, query, headers, qs) != HTMORE;
if (done)
quit();
else
remove_call_out(#'quit);
return 1;
}
// wozu binary_message nochmal durch eine funktion jagen? lieber so nennen:
emit(a) { return binary_message(a); }
devNull() {
next_input_to(#'devNull);
D2(D("=== SmallHTTP just ignored some input\n");)
}

View file

@ -0,0 +1,167 @@
//
// <kuchn> additional user modules putted into net/user/ (initial revision):
// configure.c, a web based user settings/profile changer, called by net/http
//
// i'm unhappy with the way of authing.. first i thought this could be inheriated somehow to be a part of the user object, but thats schmarn.
// but i think psyc auth would be a nice way here..
//
#include <net.h>
#include <text.h>
#include <person.h>
create() {
sTextPath(0, 0, "html");
}
htget(prot, query, headers, qs) {
htok3(prot, "text/html", "Cache-Control: no-cache\n");
w("_PAGES_user_header");
unless(auth(prot, query, headers, qs))
{
w("_PAGES_user_footer");
write("PROB");
}
}
checkAuth(val, prot, query, headers, qs, user) {
unless(val) {
w("_PAGES_user_login_failed");
w("_PAGES_user_footer");
return;
}
switch(query["action"]) {
case "settings":
if(query["set"] == "1")
settings(prot, query, headers, qs, user);
else
w("_PAGES_user_settings_body", ([
"_username" : query["username"],
"_password" : query["password"],
"_speakaction" : user->vQuery("speakaction") || "",
"_commandcharacter" : user->vQuery("commandcharacter") || ""
]) );
break;
case "profile":
if(query["set"] == "1")
profile(prot, query, headers, qs, user);
else
w("_PAGES_user_profile_body", ([
"_username" : query["username"],
"_password" : query["password"],
"_me" : user->vQuery("me") || "",
"_publicpage" : user->vQuery("publicpage") || "",
"_publictext" : user->vQuery("publictext") || "",
"_publicname" : user->vQuery("publicname") || "",
"_animalfave" : user->vQuery("animalfave") || "",
"_popstarfave" : user->vQuery("popstarfave") || "",
"_musicfave" : user->vQuery("musicfave") || "",
"_privatetext" : user->vQuery("privatetext") || "",
"_likestext" : user->vQuery("likestext") || "",
"_dislikestext" : user->vQuery("dislikestext") || "",
"_privatepage" : user->vQuery("privatepage") || "",
"_email" : user->vQuery("email") || "",
"_color" : user->vQuery("color") || "",
"_language" : user->vQuery("language") || "",
"_telephone" : user->vQuery("telephone") || ""
]) );
break;
default:
w("_PAGES_user_index", ([ "_username" : query["username"], "_password" : query["password"] ]));
break;
}
w("_PAGES_user_footer");
}
auth(prot, query, headers, qs) {
if(! stringp(query["username"]) || ! stringp(query["password"]))
{
w("_PAGES_user_login_body");
return 0;
}
if(query["username"] == "" || query["password"] == "")
{
w("_PAGES_user_login_empty");
return 0;
}
object user = summon_person(query["username"]);
if(! user || user->isNewbie())
{
w("_PAGES_user_login_notregistered");
return 0;
}
user->checkPassword(query["password"], "plain", "", "", #'checkAuth, prot, query, headers, qs, user);
return 1;
}
settings(prot, query, headers, qs, user) {
foreach(string k, string v : query) {
int ok = 0;
switch(k) {
case "password":
case "speakaction":
case "commandcharacter":
ok = 1;
break;
}
if(! ok)
continue;
if(v == "")
{
if(k == "speakaction")
user->vDel(k);
}
else
user->vSet(k, v); // password won't be set? humm.
}
w("_PAGES_user_settings_changed", ([ "_username" : query["username"], "_password" : query["password"] ]) );
}
profile(prot, query, headers, qs, user) {
foreach(string k, string v : query) {
int ok = 0;
switch(k)
{
case "me":
case "publicpage":
case "publictext":
case "publicname":
case "animalfave":
case "popstarfave":
case "musicfave":
case "privatetext":
case "likestext":
case "dislikestext":
case "privatepage":
case "email":
case "telephone":
ok = 1;
break;
}
if(! ok)
continue;
if(v == "")
user->vDel(k);
else
user->vSet(k, v);
}
w("_PAGES_user_profile_changed", ([ "_username" : query["username"], "_password" : query["password"] ]) );
}
w(mc, vars) { write(psyctext(T(mc, ""), vars)); }

296
world/net/http/xmlrpc.c Normal file
View file

@ -0,0 +1,296 @@
// $Id: xmlrpc.c,v 1.23 2008/03/11 13:42:26 lynx Exp $ // vim:syntax=lpc
//
// TODO: shares to much code with url fetcher
// in the ideal world this would only contain marshal/unmarshal code
// for xmlrpc
// possibly we can come up with an marshal/unmarshal api and use
// the same framework for xml-rpc, soap and other things
//
#include <net.h>
#include <ht/http.h>
#include <url.h>
#include <xml.h>
#include <lpctypes.h>
virtual inherit NET_PATH "output"; // virtual: in case we get inherited..
inherit NET_PATH "http/fetch";
inherit NET_PATH "xml/parse";
volatile string postbody;
volatile closure callback;
int parse_status(string all);
int parse_header(string all);
int buffer_content(string all);
// recursively dump a structure
dump(mixed value) {
/* TODO: what happens if we feed a recursive structure here? */
string t;
switch(typeof(value)) {
case T_NUMBER:
return sprintf("<value><int>%d</int></value>", value);
case T_FLOAT:
return sprintf("<value><double>%O</double></value>", value);
case T_STRING:
// in theory we would have to convert this to utf-8!
// must to encode '<' and '&'
return sprintf("<value><string>%s</string></value>", value);
case T_MAPPING: // -> struct
// NOTE: THIS DOES NOT SUPPORT mappings with m_width > 2
t = "<value><struct>\n";
foreach(string key, mixed val : value) {
t += sprintf("<member>\n"
"<name>%s</name>\n"
"%s\n"
"</member>\n",
key, dump(val));
}
t += "</struct></value>";
return t;
case T_POINTER: // array
t = "<value><array><data>\n";
foreach(mixed val : value) {
t += dump(val) + "\n";
}
t += "</data></array></value>";
return t;
default:
PT(("do not know how to serialize %O type %d\n", value, typeof(value)))
PT(("yes, this is a bug!\n"))
break;
}
return "";
}
marshal(params) {
string s = "<params>\n";
foreach(mixed param : params) {
s += sprintf("<param>\n%s\n</param>\n", dump(param));
}
return s + "</params>\n";
}
void fetch(string murl) {
if (url) return;
url = murl;
}
void request(string method, mixed params, closure cb) { // TODO: errback API
if (fetching) {
enqueue(ME, ({ method, params, cb }));
return;
}
callback = cb;
postbody = sprintf("<?xml version='1.0'?>\n"
"<methodCall>\n"
"<methodName>%s</methodName>\n"
"%s\n"
// last is a \r\n!
"</methodCall>\r\n", method, marshal(params));
P3(("%O: request(%O)\n", ME, url))
connect();
}
int logon(int arg) {
buffer = "";
httpheaders = ([ ]);
http_status = 500;
// this is all not https: compatible..
unless(::logon(arg, 1)) return -1;
unless (url) return -3;
unless (resource) sscanf(url, "http://%s/%s", host, resource);
// do somthing
emit("POST /" + resource + " HTTP/1.0\r\n"
+ "Host: " + host + "\r\n"
+ "Content-Type: text/xml\r\n"
+ "Content-Length: " + sizeof(postbody) + "\r\n");
emit("\r\n");
//+ "User-Agent: " + SERVER_VERSION + "\r\n");
emit(postbody);
next_input_to(#'parse_status);
return 0; // duh.
}
mixed * innerMarshal(mixed val) {
mixed args;
string type;
// TODO: methodSignature behaves strangely
XMLNode value;
foreach(mixed key, mixed vval : val) {
if (stringp(key) && key[0] == '/') {
type = key[1..];
value = val[key];
break;
}
}
switch(type) {
case "boolean": // map to 0, 1
// should probably check if this string is really "0" or "1"
case "int":
return ({ to_int(value[Cdata]) });
case "double":
// sigh... this is not double
return ({ to_float(value[Cdata]) });
case "dateTime.iso8601": // uhm... what do we map this to?
// unix timestamp probably?
break;
case "base64":
// probably containing binary data
return ({ decode_base64(value[Cdata]) });
// these two are complicated
case "array":
args = ({ });
value = value["/data"]["/value"];
unless(nodelistp(value)) value = ({ value });
foreach(mixed v : value) {
args += innerMarshal(v);
}
return ({ args });
case "struct": // mapping
args = ([ ]);
unless(nodelistp(value["/member"]))
value["/member"] = ({ value["/member"] });
foreach(mixed v: value["/member"]) {
args[v["/name"][Cdata]] = innerMarshal(v["/value"])[0];
}
return ({ args });
case "fault": // something is wrong
break;
default: // if no type is indicated, the type is string
case "string": // utf-8 encoded!
return ({ value[Cdata] });
}
return ({ });
}
mixed unMarshal(XMLNode parsed) {
XMLNode params;
mixed *helper;
mixed args = ({ }); // could be pre-allocated
unless(parsed["/fault"]) {
if (parsed["/params"]) {
params = parsed["/params"]["/param"];
} else {
params = ({ });
}
unless(nodelistp(params)) params = ({ params }); // special case
foreach(mixed param : params) {
args += innerMarshal(param["/value"]);
}
// should probably watch for HTTP 200
return args;
} else {
// TODO: fault handling
return -1;
}
}
void disconnected(remainder) {
mixed *args;
httpheaders["_fetchtime"] = isotime(ctime(time()), 1);
if (httpheaders["last-modified"])
modificationtime = httpheaders["last-modified"];
if (httpheaders["etag"])
etag = httpheaders["etag"]; // heise does not work with etag
fetched = buffer;
fheaders = httpheaders;
buffer = httpheaders = 0;
fetching = 0;
if (pointerp(args)) // no fault
funcall(callback, args...);
if (qSize(ME)) {
args = shift(ME);
call_out(#'request, 0, args...); // at next heart_beat
// request(args...);
}
return 1; // kind of expected
}
// helper methods, useful at least for ORA meerkat
// could do caching for these things
void listMethods(closure cb) {
request("system.listMethods", ({ }), cb);
}
void methodHelp(string method, closure cb) {
request("system.methodHelp", ({ method }), cb);
}
void methodSignature(string method, closure cb) {
request("system.methodSignature", ({ method }), cb);
}
varargs string content(closure cb, int force, int willbehave) { return ""; } // makes no sense
create() {
qCreate();
qInit(ME, 150, 5);
#ifdef MODULE_TESTS
selftest();
#endif
}
/*--------------------------------------------------------------------
* MODULE TESTS
* you should run them whenever you change the code
* if they fail, debug!
* do not check in code that does not pass the tests
*--------------------------------------------------------------------*/
#ifdef MODULE_TESTS
// automatic test cases
// add some if you like
selftest() {
// self tests...
// be careful, the order for structs is not guaranteed
int i, result;
mixed *structures = ({
({ 1, 2, 3, 4 }),
// ({ (["a" : 1, "b" : "boo" ]) }),
({ 1, 1.5, "a", ({ 1 }), (["a" : "foo" ]) })
});
mixed *strings = ({
"<params>\n<param>\n<value><int>1</int></value>\n</param>\n<param>\n<value><int>2</int></value>\n</param>\n<param>\n<value><int>3</int></value>\n</param>\n<param>\n<value><int>4</int></value>\n</param>\n</params>\n",
// "<params>\n<param>\n<value><struct>\n<member>\n<name>a</name>\n<value><int>1</int></value>\n</member>\n<member>\n<name>b</name>\n<value><string>boo</string></value>\n</member>\n</struct></value>\n</param>\n</params>\n",
"<params>\n<param>\n<value><int>1</int></value>\n</param>\n<param>\n<value><double>1.5</double></value>\n</param>\n<param>\n<value><string>a</string></value>\n</param>\n<param>\n<value><array><data>\n<value><int>1</int></value>\n</data></array></value>\n</param>\n<param>\n<value><struct>\n<member>\n<name>a</name>\n<value><string>foo</string></value>\n</member>\n</struct></value>\n</param>\n</params>\n"
});
i = 0;
foreach(mixed test : structures) {
result = marshal(test) == strings[i];
if (result) {
PT(("passed marshal test %d\n", i))
} else {
PT(("marshal test %d failed\n", i))
PT(("%O\n%O\n", marshal(test), strings[i]))
break;
}
i++;
}
i = 0;
foreach(mixed test : strings) {
// set callback internally! it is safe to do so as we are
// doing only synchronus operations
XMLNode x = new_XMLNode;
x["/params"] = xmlparse(test);
result = sprintf("%O", unMarshal(x)) == sprintf("%O", structures[i]);
if (result) {
PT(("passed unmarshal test %d\n", i))
} else {
PT(("unmarshal test %d failed\n", i))
PT(("%O\n%O\n", unMarshal(x), structures[i]))
break;
}
i++;
}
}
#endif