/**
* This is the new bug database html error handler. It uses the new
* error handler to do the bug database lookups and avoids using the
* blocking database calls.
* @author Pinkfish
* @started Fri Feb 7 12:31:59 PST 2003
*/
#include <error_handler.h>
#include <player_handler.h>
#include <http.h>
#include <db.h>
#undef SAVE_FILE
#define SAVE_FILE "/save/www/errors"
#define TIMEOUT_PERIOD (30 * 60)
class user_data {
class error_query query;
class error_summary* errors;
int last_touched;
string direction;
}
private mapping _user_query;
void load_me();
void create() {
_user_query = ([ ]);
call_out("flush_old", TIMEOUT_PERIOD);
load_me();
}
void flush_old() {
string name;
class user_data data;
call_out("flush_old", TIMEOUT_PERIOD);
foreach (name, data in _user_query) {
if (time() - data->last_touched > TIMEOUT_PERIOD) {
map_delete(_user_query, name);
}
}
}
void load_me() {
unguarded( (: restore_object(SAVE_FILE) :) );
}
void save_me() {
unguarded( (: save_object(SAVE_FILE, 2) :) );
}
private varargs string create_header(class http_request req, string title,
int no_timeout) {
return "<HTML>\n<HEAD>\n"
"<TITLE>" + title + "</TITLE>\n"
"<META http-equiv=\"Refresh\" content=\"" + TIMEOUT_PERIOD +
";URL=http://" + req->hostname +
"/secure/errors_new.c?action=timeout\">"
"</HEAD>\n<BODY bgcolor=\"#ffffff\" text=\"#000030\" link=\"#4a529c\" "
"vlink=\"#b57339\">\n<FONT face=\"arial,helvetica\">\n"
//"<i>(Imagine a pretty picture here to save space)</i>\n<H2>Errors</H2>\n"
//"<H3><I>When cabbages are just not enough.</I></H3>\n<BR clear=\"left\">\n"
"<BR>\n";
} /* create_header() */
private string create_footer(class http_request req) {
return ("/www/footer"->www_function("footer", ([ ]), req))+"</BODY>\n</HTML>\n";
}
private string htmlify_no_address(string str) {
return replace(str, ({
"&", "&",
"<", "<",
">", ">",
"\n", "<BR>\n",
/* " ", " ", */
}) );
}
/*
* Makes all the '<' turn into $gt; and the '>' the same. Turn
* http addresses into real addresses...
*/
string htmlify(string str) {
string *bits;
string start;
string end;
string extra;
int i;
str = htmlify_no_address(str);
if (strsrch(str, "http:") > 0) {
bits = explode("#" + str, "http:");
bits[0] = bits[0][1..];
for (i = 1; i < sizeof(bits); i++) {
if (sscanf(bits[i], "%s %s", start, end) == 2) {
end = " " + end;
if (sscanf(start, "%s<BR>%s", start, extra) == 2) {
end = "<BR>" + extra + end;
}
} else if (sscanf(bits[i], "%s<BR>%s", start, end) == 2) {
end = "<BR>" + end;
} else {
start = bits[i];
end = "";
}
if (start[<1] == '.') {
start = start[0..<2];
end = "." + end;
}
bits[i] = "<A href=\"http:" + start + "\">http:" + start + "</A>" +
end;
}
str = implode(bits, "");
}
return str;
}
string html_error(class http_request req, string error) {
return create_header(req, error) +
"<h2>" + error + "</h2>" +
create_footer(req);
}
int find_index(class http_request req, int id) {
int i;
class error_summary* errors;
if (!_user_query[req->user]) {
return 0;
}
_user_query[req->user]->last_touched = time();
errors = _user_query[req->user]->errors;
for (i = 0; i < sizeof(errors); i++) {
if (id == errors[i]->id) {
return i;
}
}
return 0;
}
int max_index(class http_request req) {
if (!_user_query[req->user]) {
return 0;
}
_user_query[req->user]->last_touched = time();
return sizeof(_user_query[req->user]->errors);
}
int id_at(class http_request req, int index) {
if (!_user_query[req->user] || index < 0 ||
index >= sizeof(_user_query[req->user]->errors)) {
return 0;
}
_user_query[req->user]->last_touched = time();
return _user_query[req->user]->errors[index]->id;
}
void finish_query(function callback,
class http_request req,
int type, mixed data, string query) {
class error_summary summary;
string ret;
string fname;
string line;
if (type != DB_SUCCESS) {
evaluate(callback, html_error(req, "Error: " + data));
return ;
}
ret = create_header(req, "Error list");
_user_query[req->user]->errors = data;
save_me();
if (!sizeof(data)) {
ret += sprintf("No bugs found.");
} else {
line = "";
if (sizeof(data) == 150) {
line += "[ <a href=\"error_query.html\">Query</a> | "
"<a href=\"errors_new.c?action=newoffset&offset=" +
(_user_query[req->user]->query->bug_offset + 150) + "\">Next 150 bugs</a> ";
} else {
line += "[ <a href=\"error_query.html\">Query</a> ";
}
if (_user_query[req->user]->query->bug_offset != 0) {
line += "| <a href=\"errors_new.c?action=newoffset&offset=" +
(_user_query[req->user]->query->bug_offset - 150) +
"\">Previous 150 bugs</a> ";
}
line += "]";
ret += "<b>Offset #" + _user_query[req->user]->query->bug_offset +
"</b> " + line + "<br>";
ret += "<table width=100%><tr><th>Id</th>"
"<th>File Name</th>"
"<th>Status</th>"
"<th>Date Reported</th><th>Reporter</th>"
"<th>Assigned to</th>"
"<th>Category</th><th>Type</th>"
"<th>Directory</th></tr>";
foreach (summary in data) {
fname = explode(summary->filename, "/")[<1];
ret += "<tr><td>" +
"<a href=\"errors_new.c?action=bug&id=" + summary->id + "\">" +
summary->id + "</a></td><td>" +
fname + "</td><td>" +
summary->status + "</td><td>" +
ctime(summary->entry_date)[4..15] + "</td><td>" +
summary->reporter + "</td><td>" +
summary->assigned_to + "</td><td>" +
summary->category + "</td><td>" + summary->type + "</td><td>" +
summary->directory + "</td></tr>";
}
ret += "</table>";
ret += line + "<br>";
}
ret += create_footer(req);
evaluate(callback, ret);
}
int compare_query(class error_query q1, class error_query q2) {
if (!q1 || !q2) {
return 0;
}
if (q1->dir != q2->dir) {
return 0;
}
if (q1->reporter != q2->reporter) {
return 0;
}
if (q1->fixer != q2->fixer) {
return 0;
}
if (q1->file_name != q2->file_name) {
return 0;
}
if (q1->recursive != q2->recursive) {
return 0;
}
if (q1->assigned_to != q2->assigned_to) {
return 0;
}
if (sizeof(q1->status) != sizeof(q2->status) ||
sizeof(q1->status - q2->status)) {
return 0;
}
if (sizeof(q1->type) != sizeof(q2->type) ||
sizeof(q1->type - q2->type)) {
return 0;
}
if (sizeof(q1->category) != sizeof(q2->category) ||
sizeof(q1->category - q2->category)) {
return 0;
}
return 1;
}
mixed do_query(function callback, mapping args, class http_request req) {
class error_query query;
string bing;
if (args["dir"] == "" && args["filename"] == "" &&
args["reporter"] == "" && args["assigned"] == "") {
evaluate(callback, html_error(req, "One of 'dir', 'filename' or 'reporter' must be not blank."));
return 1;
}
query = new(class error_query);
if (args["dir"] != "") {
query->dir = args["dir"];
}
if (args["recurse"]) {
query->recursive = 1;
}
// Check each of the statuses to see if they were set.
query->status = ({ });
foreach (bing in ({ "fixing", "considering", "open", "fixed", "denied" })) {
if (args[bing]) {
query->status += ({ upper_case(bing) });
}
}
query->type = ({ });
foreach (bing in ({ "bug", "typo", "idea", "comment" })) {
if (args[bing]) {
query->type += ({ upper_case(bing) });
}
}
query->category = ({ });
foreach (bing in ({ "room", "help", "object", "command", "general", "ritual", "spell" })) {
if (args[bing]) {
query->category += ({ upper_case(bing) });
}
}
if (args["reporter"] != "") {
query->reporter = args["reporter"];
}
if (args["fixer"] != "") {
query->fixer = args["fixer"];
}
if (args["filename"] != "") {
query->file_name = args["filename"];
}
if (args["assigned"] != "") {
query->assigned_to = args["assigned"];
}
if (args["bug_offset"] != "") {
query->bug_offset = to_int(args["bug_offset"]);
}
query->order_by = args["order_by"];
if (!_user_query[req->user]) {
_user_query[req->user] = new(class user_data);
}
_user_query[req->user]->direction = "next";
_user_query[req->user]->last_touched = time();
// Redo the query each time.
//if (!compare_query(_user_query[req->user]->query, query)) {
_user_query[req->user]->query = query;
if (!ERROR_HANDLER->do_query_bug_summary(query, (: finish_query, callback, req :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
//} else {
//finish_query(callback, req, DB_SUCCESS,
//_user_query[req->user]->errors, "some select thing");
//}
return 1;
}
int do_new_offset(function callback, int offset, class http_request req) {
class error_query query;
query = _user_query[req->user]->query;
if (query->bug_offset == offset) {
finish_query(callback, req, DB_SUCCESS, _user_query[req->user]->errors, "");
} else {
query->bug_offset = offset;
if (!ERROR_HANDLER->do_query_bug_summary(query, (: finish_query, callback, req :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
}
return 1;
}
void finish_details(function callback, class http_request req,
int type, mixed data) {
string ret;
string bing;
class error_details details;
class error_complete complete;
class error_comment comment;
class error_forward forward;
class error_replies reply;
int index;
int max;
if (type != DB_SUCCESS) {
evaluate(callback, html_error(req, "Error: " + data));
return ;
}
complete = data[0];
details = complete->details;
ret = create_header(req, "Bug #" + details->summary->id);
ret += "<table width=100%>";
ret += "<tr><td><b>Date Reported</b>:</td><td>" + ctime(details->summary->entry_date) + "</td></tr>";
ret += "<tr><td><b>Assigned To</b>:</td><td>" +
"<table border=0 width=100%><tr><td>" +
details->summary->assigned_to + "</td><td align=right>"
"<form action=\"errors_new.c?action=assign&id=" +
details->summary->id +
"\" method=get><input type=text size=12 name=\"assigned\">"
"<input type=hidden name=id value=" + details->summary->id + ">"
"<input type=hidden name=action value=assign>"
"<input type=submit name=rabbit value=assign></form>"
"</td></tr></table>";
ret += "<tr><td><b>Reporter</b>:</td><td>" + details->summary->reporter + "</td></tr>";
ret += "<tr><td><b>Directory</b>:</td><td>" + details->summary->directory + "</td></tr>";
ret += "<tr><td><b>File name</b>:</td><td>" + details->summary->filename + "</td></tr>";
ret += "<tr><td><b>Category</b>:</td><td>"
"<table border=0 width=100%><tr><td>" +
details->summary->category +
"</td><td align=right><font size=-2>Change to:";
foreach (bing in ERROR_CATEGORIES) {
if (bing != details->summary->category) {
ret += "<a href=\"errors_new.c?action=changecategory&id=" +
details->summary->id +
"&category=" + bing +
"\">" + bing + "</a> ";
}
}
ret += "</i></font></td></tr></table></td></tr>";
ret += "<tr><td><b>Type</b>:</td><td>"
"<table border=0 width=100%><tr><td>" +
details->summary->type +
"</td><td align=right><font size=-2>Change to:";
foreach (bing in ERROR_TYPE) {
if (bing != details->summary->type) {
ret += "<a href=\"errors_new.c?action=changetype&id=" +
details->summary->id +
"&type=" + bing +
"\">" + bing + "</a> ";
}
}
ret += "</i></font></td></tr></table></td></tr>";
ret += "<tr><td><b>Status</b>:</td><td>"
"<table border=0 width=100%><tr><td>" +
details->summary->status +
"</td><td align=right><font size=-2>Change to:";
foreach (bing in ERROR_STATUS) {
ret += "<a href=\"errors_new.c?action=changestatus&id=" +
details->summary->id +
"&status=" + bing +
"\">" + bing + "</a> ";
}
ret += "<br><a href=\"errors_new.c?action=changestatus&id=" +
details->summary->id +
"&status=THANKS\">Thanks</a> ";
ret += "<a href=\"errors_new.c?action=changestatus&id=" +
details->summary->id +
"&status=NOTREPRO\">Not reproducable</a> ";
ret += "<a href=\"errors_new.c?action=changestatus&id=" +
details->summary->id +
"&status=NOTPOSSIBLE\">Not possible to fix</a> ";
ret += "<a href=\"errors_new.c?action=changestatus&id=" +
details->summary->id +
"&status=NOINFO\">Not enough Information</a> ";
ret += "<a href=\"errors_new.c?action=changestatus&id=" +
details->summary->id +
"&status=TEMPORARY\">Temporary</a> ";
ret += "</i></font></td></tr></table></td></tr>";
if (details->summary->status == "FIXED" ||
details->summary->status == "DENIED") {
ret += "<tr><td><b>Fixed By</b></td><td>" + details->fixer +
" (" + ctime(details->fix_date) + ")</td></tr>";
}
if (sizeof(complete->forwards)) {
ret += "<tr><td><b>Forwards</b>:</td><td>";
// Print out the forwards.
foreach (forward in complete->forwards) {
ret += forward->forwarder + " from " +
forward->old_directory + " at " +
ctime(forward->date)[4..15] + "<br>";
}
ret += "</td></tr>";
}
ret += "</table>";
ret += "<div align=right><form action=\"errors_new.c?action=forward&id=" +
details->summary->id + "\" method=get>";
index = find_index(req, details->summary->id);
if (index != -1) {
// Update the summary details, just in case they changed.
_user_query[req->user]->errors[index] = details->summary;
}
max = max_index(req);
ret += (index + 1) + " of " + max + " ";
ret += "<b>[ <a href=\"errors_new.c?action=index\">Index</a> | ";
if (index + 1 < max) {
ret += "<a href=\"errors_new.c?action=next&id=" + id_at(req, index + 1) + "\">Next</a> | ";
}
if (index > 0) {
ret += "<a href=\"errors_new.c?action=prev&id=" + id_at(req, index - 1) + "\">Prev</a> | ";
}
ret += "<a href=\"errors_new.c?action=comment&id=" + details->summary->id + "\">Add Comment</a> | ";
ret += "<a href=\"error_query.html\">Query</a> ]</b><br>";
ret += "\n<input type=text size=30 name=\"forward\">"
"<input type=hidden name=id value=" + details->summary->id + ">"
"<input type=hidden name=action value=forward>"
"<input type=submit name=rabbit value=forward></form></div>\n";
ret += htmlify(details->report);
ret += htmlify(details->runtime);
ret += "<blockquote>";
// Show the error replies before the comments.
foreach (reply in complete->replies) {
ret += "<p><b>Date Sent:</b> " + ctime(reply->senddate)[4..15] +
"<br><b>From:</b> " + reply->sender +
"<br><b>To:</b> " + reply->recipient +
"<br><b>Subject:</b> " + reply->subject + "<br>" +
htmlify(reply->message);
}
if (sizeof(complete->replies) && sizeof(complete->comments)) {
ret += "<hr width=30%>";
}
foreach (comment in complete->comments) {
ret += "<p><b>Comment by " + comment->commenter + " at " +
ctime(comment->date)[4..15] + "</b><br>" +
htmlify(comment->comment);
}
ret += "</blockquote>";
ret += create_footer(req);
evaluate(callback, ret);
}
int do_bug_details(function callback, mapping args, class http_request req,
int update_position) {
int index;
int max;
int val;
if (update_position) {
index = find_index(req, to_int(args["id"]));
max = max_index(req);
val = to_int(args["id"]);
switch (_user_query[req->user]->direction) {
case "prev" :
if (index - 1 >= 0) {
val = id_at(req, index - 1);
}
case "next" :
default :
if (index + 1 < max) {
val = id_at(req, index + 1);
}
break;
}
tell_creator("pinkfish", "%O %O %O\n", args["id"], index, max);
args["id"] = val + "";
}
if (!ERROR_HANDLER->do_query_bug_details(to_int(args["id"]), (: finish_details, callback, req :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
return 1;
}
int do_change_type(function callback, mapping args, class http_request req) {
if (!ERROR_HANDLER->do_change_type(to_int(args["id"]), args["type"],
(: do_bug_details($(callback), $(args), $(req), 0) :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
return 1;
}
int do_change_category(function callback, mapping args, class http_request req) {
if (!ERROR_HANDLER->do_change_category(to_int(args["id"]), args["category"],
(: do_bug_details($(callback), $(args), $(req), 0) :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
return 1;
}
int do_forward(function callback, mapping args, class http_request req) {
if (!ERROR_HANDLER->do_forward(to_int(args["id"]), req->user,
args["forward"],
(: do_bug_details($(callback), $(args), $(req), 1) :))) {
evaluate(callback, html_error(req, "Cannot forward to a non-existant "
"directory or to '/'"));
}
return 1;
}
int do_assign(function callback, mapping args, class http_request req) {
if (!ERROR_HANDLER->do_assign_bug_to(to_int(args["id"]),
args["assigned"],
(: do_bug_details($(callback), $(args), $(req), 1) :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
return 1;
}
void finish_change_status_details(function callback, mapping args,
class http_request req,
int type, mixed data) {
class error_complete complete;
class error_details details;
string str;
string internal_status;
if (type != DB_SUCCESS) {
evaluate(callback, html_error(req, "Error: " + data));
return ;
}
complete = data[0];
details = complete->details;
internal_status = args["status"];
if (internal_status == "TEMPORARY") {
internal_status = "FIXED";
}
if (internal_status == "NOTREPRO" || internal_status == "NOINFO" ||
internal_status == "NOTPOSSIBLE" || internal_status == "THANKS") {
internal_status = "DENIED";
}
str = create_header(req, "Change status of #" + details->summary->id);
str += "New status: " + internal_status;
str += "<form action=\"errors_new.c?action=changestatusmessage\" method=post>\n";
switch (args["status"]) {
case "TEMPORARY" :
str += "<input type=text size=60 name=subject value=\"" +
lower_case(details->summary->category) + " " +
lower_case(details->summary->type) +
" bug #" + args["id"] + " was a temporary bug\"><br>";
break;
case "NOINFO" :
str += "<input type=text size=60 name=subject value=\"Not enough " +
"information on " +
lower_case(details->summary->category) + " " +
lower_case(details->summary->type) +
" bug #" + args["id"] + "\"><br>";
break;
case "NOTPOSSIBLE" :
str += "<input type=text size=60 name=subject value=\"" +
lower_case(details->summary->category) + " " +
lower_case(details->summary->type) +
" bug #" + args["id"] + " was not possible to fix\"><br>";
break;
case "NOTREPRO" :
str += "<input type=text size=60 name=subject value=\"" +
lower_case(details->summary->category) + " " +
lower_case(details->summary->type) +
" bug #" + args["id"] + " was not reproducable\"><br>";
break;
case "THANKS" :
str += "<input type=text size=60 name=subject value=\"Thanks for " +
lower_case(details->summary->category) + " " +
lower_case(details->summary->type) +
" bug #" + args["id"] + "\"><br>";
break;
default :
str += "<input type=text size=60 name=subject value=\"Change status of " +
lower_case(details->summary->category) + " " +
lower_case(details->summary->type) +
" bug #" + args["id"] + " to " +
lower_case(args["status"]) + "\"><br>";
}
str += "<textarea name=message cols=80 rows=20>";
switch (args["status"]) {
case "FIXED" :
str += "Thank you for your " + lower_case(complete->details->summary->type) +
" report, this " + lower_case(complete->details->summary->type) +
" has now been fixed.\n\nGood luck!\n" +
capitalize(req->user) + "\n";
break;
case "DENIED" :
str += "Thank you for your " + lower_case(complete->details->summary->type) +
" report."
"\n\nThanks!\n" +
capitalize(req->user) + "\n\n";
break;
case "THANKS" :
str += "Thank you for your " + lower_case(complete->details->summary->type) +
" report.\n\nThanks!\n" +
capitalize(req->user) + "\n\n";
break;
case "NOTREPRO" :
str += "The " + lower_case(complete->details->summary->type) +
" report was unable to be reproduced. Please submit a new bug "
"report with more specific details if it happens again.\n\n"
"Thanks!\n" +
capitalize(req->user) + "\n\n";
break;
case "NOINFO" :
str += "The " + lower_case(complete->details->summary->type) +
" report did not contain enough information to fix the bug. "
"Please make a new report with more details.\n\n"
"Good luck!\n" +
capitalize(req->user) + "\n\n";
break;
case "NOTPOSSIBLE" :
str += "Sadly, this " + lower_case(complete->details->summary->type) +
" is cannot be fixed due to coding "
"constraints.\n\n"
"Good luck!\n" +
capitalize(req->user) + "\n\n";
break;
case "TEMPORARY" :
str += "The " + lower_case(complete->details->summary->type) +
" in question was a temporary problem in the game and has now "
"been solved. Please report this if it happens again.\n\n"
"Good luck!\n" +
capitalize(req->user) + "\n\n";
break;
}
str += "> Bug #" + details->summary->id + " reported at " +
ctime(details->summary->entry_date)[4..15] + "\n>\n";
str += "> " + replace_string(htmlify_no_address(sprintf("%79-=s", details->report)), "<BR>\n", "\n> ");
str += "</textarea>";
str += "<br>";
str += "<input type=hidden name=status value=\"" + internal_status + "\">";
str += "<input type=hidden name=id value=\"" + details->summary->id + "\">";
str += "<input type=submit name=submit value=\"Change Status\">";
str += "<input type=checkbox name=nomail value=yes> Don't send mail<br>";
str += "</form>";
str += create_footer(req);
evaluate(callback, str);
}
int do_change_status(function callback, mapping args, class http_request req) {
if (!ERROR_HANDLER->do_query_bug_details(to_int(args["id"]), (: finish_change_status_details, callback, args, req :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
return 1;
}
int do_change_status_message(function callback, mapping args, class http_request req) {
int nomail;
args = req->body->data;
if (args["nomail"] == "yes") {
nomail = 1;
}
args["message"] = replace_string(args["message"], "\r\n", "\n");
if (!ERROR_HANDLER->do_change_status(to_int(args["id"]), args["status"],
nomail, args["subject"],
req->user, args["message"],
(: do_bug_details($(callback), $(args), $(req), 1) :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
return 1;
}
string do_comment(mapping args, class http_request req) {
string str;
str = create_header(req, "Comment on #" + args["id"]);
str += "<h2>Comment to add to #" + args["id"] + "</h2>"
"<form action=\"errors_new.c?action=commentmessage&id=" + args["id"] +
"\" method=post>";
str += "<textarea name=message cols=80 rows=20>"
"</textarea><br>"
"<input type=submit name=\"Add Comment\" value=\"Add Comment\">"
"</form>";
str += create_footer(req);
return str;
}
int do_comment_message(function callback, mapping args, class http_request req) {
string mess;
mess = replace_string(req->body->data["message"], "\r\n", "\n");
if (!ERROR_HANDLER->do_comment(to_int(args["id"]), req->user,
req->body->data["message"],
(: do_bug_details($(callback), $(args), $(req), 0) :))) {
evaluate(callback, html_error(req, "Error doing stuff."));
}
return 1;
}
mixed www_delayed(function callback, string str, mapping args,
class http_request req ) {
if (!PLAYER_HANDLER->test_creator(req->user)) {
return 0;
}
switch (args["action"]) {
case "query" :
return do_query(callback, args, req);
case "next" :
case "prev" :
_user_query[req->user]->direction = args["action"];
case "bug" :
return do_bug_details(callback, args, req, 0);
case "index" :
finish_query(callback, req, DB_SUCCESS,
_user_query[req->user]->errors, "some select thing");
return 1;
case "changetype" :
return do_change_type(callback, args, req);
case "changecategory" :
return do_change_category(callback, args, req);
case "changestatus" :
return do_change_status(callback, args, req);
case "changestatusmessage" :
return do_change_status_message(callback, args, req);
case "forward" :
return do_forward(callback, args, req);
case "assign" :
return do_assign(callback, args, req);
case "commentmessage" :
return do_comment_message(callback, args, req);
case "newoffset" :
return do_new_offset(callback, to_int(args["offset"]), req);
}
return 0;
}
mixed www_request(string str, mapping args, class http_request req) {
if (!PLAYER_HANDLER->test_creator(req->user)) {
return 0;
}
switch (args["action"]) {
case "timeout" :
map_delete(_user_query, req->user);
str = create_header(req, "Session timed out.", 1);
str += "<h2>Your session timed out</h2>"
"<a href=\"error_query.html\">Query page</a>";
return str + create_footer(req);
case "comment" :
return do_comment(args, req);
}
}
mapping query_dynamic_auto_load() {
return _user_query;
}
void init_dynamic_arg(mapping details) {
if (mapp(details)) {
_user_query = details;
}
}