/* Template system (from C) */ import std.c.stdio; import std.stdio; import std.string; struct templateChunk{ int type; templateChunk* next; size_t restSize; char rest[1]; } struct templateElement{ char* name; char* value; templateElement* parent; size_t numChildren; templateElement** children; } extern(C){ templateElement* addElement(char* name, char* value, templateElement* parent); void deleteElement(templateElement * e); templateChunk * readStream(FILE* fp); int createOutput(FILE* outputFile, templateChunk * t, templateElement * root); } /** Wraps up a HTML template. The Template class is the preferred method of outputting HTML pages. Its constructor takes the filename for a template that is in my format. You then use addElement to add a name/contents pair, optionally with a parent element to pass data to it. When ready, you call send to send the HTML page to the user. */ typedef const(char)[] HTMLString; class Template{ /// fn = template filename this(const char[] fn){ root = .addElement(null, null, null); filename = fn.dup; } ~this(){ deleteElement(root); } /// Sends the template and the content type header to the user's browser void send(){ FILE* fp = fopen(std.string.toStringz(filename), "rt"); if(fp is null) throw new Exception("File not found"); scope(exit) fclose(fp); templateChunk* t = readStream(fp); if(t is null) throw new Exception("File format error"); puts("Content-type: text/html\n"); createOutput(stdout, t, root); } /// Keep track of returned Elements from addElement if you want to use them as parents later class Element{ private: templateElement* e; this(){ } } /// Adds a name/value pair to the element's variable list. Element addElement(const char[] name, invariant char[] contents, Element parent = null){ return this.addElement(name, cast(HTMLString) contents, parent); } Element addElement(const char[] name, HTMLString contents, Element parent = null){ Element e = new Element; e.e = .addElement(std.string.toStringz(name), std.string.toStringz(contents), parent is null ? root : parent.e); return e; } /+ Element addElement(T)(const char[] name, T contents, Element parent = null){ static if(is(typeof(contents[0]) == char)){ Element e = new Element; e.e = .addElement(std.string.toStringz(name), std.string.toStringz(contents), parent is null ? root : parent.e); return e; } else return this.addElement(name, contents.toString, parent); } +/ private: templateElement* root; char[] filename; } void failOnException(Exception e){ writefln("Content-type: text/html\n\n"); writef("
%s
" , htmlEncode(e.toString)); } /// Outputs a redirect header to the user's browser. argument is the url to redirect to void redirect(const char[] where){ writefln("Location: %s\n\n", where); } /// Encodes a string into valid html HTMLString htmlEncode(const char[] s){ const(char)[] a = s.dup; a = replace(a, "<", "<"); a = replace(a, ">", ">"); a = replace(a, "'", "'"); a = replace(a, "\"", """); return cast(HTMLString)a; } /+ import std.string; auto a = new String; auto b = a['v'..'w']; class String{ public: char[] opSlice(char x, char y){ return data[find(data, x)..find(data,y)]; } int length(){ return data.length; } private: char[] data; } +/ char[][char[]] GET; char[][char[]] POST; char[][char[]] COOKIES; char[] REMOTE_ADDR; char[] HTTP_USER_AGENT; char[] PATH_INFO; /// Retrieves a get variable given in the first argument, or the default passed in the second if it is not present const char[] get(const char[] what, const char[] def){ char[]* w = what in GET; if(w is null) return cast(char[])def; return *w; } /// same as get(), but for post variables instead const char[] post(const char[] what, const char[] def){ char[]* w = what in POST; if(w is null) return cast(char[])def; return *w; } // more private like char[] REQUEST_METHOD; int CONTENT_LENGTH; char[] CONTENT_TYPE; char[] HTTP_COOKIE; import std.c.stdlib; import std.c.string; char[] readEnv(const(char)* v){ char[] qs; char* queryString = getenv(v); if(queryString is null) return "".dup; qs.length = strlen(queryString); for(int a = 0; a < qs.length; a++) qs[a] = queryString[a]; return qs; } static this(){ REQUEST_METHOD = readEnv("REQUEST_METHOD"); CONTENT_TYPE = readEnv("CONTENT_TYPE"); CONTENT_LENGTH = std.string.atoi(readEnv("CONTENT_LENGTH")); REMOTE_ADDR = readEnv("REMOTE_ADDR"); HTTP_USER_AGENT = readEnv("HTTP_USER_AGENT"); HTTP_COOKIE = readEnv("HTTP_COOKIE"); PATH_INFO = readEnv("PATH_INFO"); const(char)[][] cookies = split(HTTP_COOKIE, ";"); const(char)[][] words; foreach(cookie; cookies){ words = split(cookie, "="); if(words.length == 2) COOKIES[words[0]] = words[1].dup; else if(words.length == 0) COOKIES[words[0]] = "".dup; } char[] qs = readEnv("QUERY_STRING"); if(qs.length != 0) parseThing(qs, &GET); if(REQUEST_METHOD == "POST"){ char[] p; p.length = CONTENT_LENGTH; for(int a = 0; a < CONTENT_LENGTH; a++) p[a] = fgetc(stdin); parseThing(p, &POST); // FIXME: handle different content-types including file uploads } return; } void parseThing(char[] qs, char[][char[]]* w){ const(char)[][] components = split(qs, "&"); foreach(component; components){ const(char)[][] words = split(component, "="); if(words.length == 0) throw new Exception("wtf"); if(words.length == 2) (*w)[words[0]] = percentThing(words[1]); else (*w)[words[0]] = "".dup; } } import std.uri; char[] percentThing(const char[] w){ // return decode(w); char[] result; result.length = w.length; int p = 0; for(int a = 0; a < w.length; a++){ if(w[a] == '%'){ char[2] r; r[0] = w[++a]; r[1] = w[++a]; result[p] = fromHex(r); } else { result[p] = w[a]; } p++; } result.length = p; return result; } int fromHex(char[2] l){ int a, b; if(l[0] >= '0' && l[0] <= '9') a = l[0] - 0x30; if(l[0] >= 'a' && l[0] <= 'f') a = l[0] - 'a' + 10; if(l[0] >= 'A' && l[0] <= 'F') a = l[0] - 'A' + 10; if(l[1] >= '0' && l[1] <= '9') b = l[1] - 0x30; if(l[1] >= 'a' && l[1] <= 'f') b = l[1] - 'a' + 10; if(l[1] >= 'A' && l[1] <= 'F') b = l[1] - 'A' + 10; return a * 16 + b; }