basic templating
[public/www-new.git] / src / web.d
1 //#!/usr/bin/rdmd
2 import std.stdio, std.path, std.process, std.file, std.array, std.string, std.algorithm, std.datetime, std.ascii, std.regex;
3 import config;
4
5 string dweb_root;
6 string[string] template_variables;
7 string[string] headers;
8
9 string write_link(string to, bool expand) {
10   bool isdir = dirExists(dweb_root ~ "/srv/" ~ to[url_root.length..$]);
11   string flair = nav_tree_chev? (expand? "» " : "› ") : "";
12   string result = "";
13   result ~= "<li" ~ (expand? " class=\"thisPage\" " : "") ~ ">";
14   result ~= "<a href=\"" ~ to ~ (isdir ? "/" : "") ~ "\">"
15         ~ flair ~ baseName(to) ~ (isdir ? "/" : "") ~ "</a>";
16   result ~= "</li>";
17   return result;
18 }
19
20 string nav_tree_r(string url, string cur_loc, string[] subdirs) {
21   string result = "";
22   string[] dirs = array(map!"a.name"(dirEntries(dweb_root ~ "/srv/" ~ cur_loc, SpanMode.shallow)));
23   sort(dirs);
24   if (dirs.length == 0) return "";
25   bool inserted_ul = false;
26   bool next = false;
27   string next_loc;
28   foreach(string s; dirs) {
29     s= s[(dweb_root ~ "/srv/").length..$];
30     string name = stripExtension(baseName(s));
31     if (name.length == 0) continue; // e.g. ".md", should we do something else with these files?
32     if (name == "index") continue; // "index" will never appear in the nav_tree.
33     if (name[0] == '@') continue; // hidden file
34     if (!inserted_ul) { result ~= "<ul>"; inserted_ul = true; }
35     bool expand = subdirs.length > 0 && name == subdirs[0];
36     result ~= write_link(url_root ~ stripExtension(s), expand);
37
38     if (expand && isDir(dweb_root ~ "/srv/" ~ s)) {
39       if (nav_tree_vert) {
40         result ~= "<li>";
41         result ~= nav_tree_r(url, (cur_loc == "" ? "" : cur_loc ~ "/") ~ subdirs[0], subdirs[1..$]);
42         result ~= "</li>";
43       } else {
44         next = true;
45         next_loc = (cur_loc == "" ? "" : cur_loc ~ "/") ~ subdirs[0];
46       }
47     }
48   }
49
50   if (inserted_ul) result ~= "</ul>";
51   if (next) result ~= nav_tree_r(url, next_loc, subdirs[1..$]);
52   return result;
53 }
54
55 string do_nav_tree(string url) {
56   return nav_tree_r(url, "", cast(string[])array(pathSplitter(url)));
57 }
58
59 string not_found(string path) {
60   headers["Status"] = "404 Not Found";
61   return "The page <code>" ~ path ~ "</code> does not exist. (404)";
62 }
63
64 bool dirExists(string path)  { try { if (isDir(path))  return true; else return false; } catch (Exception e) { return false; } }
65
66 string do_content(string url) {
67   // first, see if we have something that wants to handle url outright
68   foreach (string glob, string h; handlers) {
69     if (globMatch(url, glob)) {
70       return shell(dweb_root ~ "/bin/" ~ h ~ " " ~ url);
71     }
72   }
73   // if that failed, see if we can handle the file
74   if (url == "" ? true : url[$-1] == '/') url ~= "index";
75   foreach (f; array(map!"a.name"(dirEntries(dirName(dweb_root ~ "/srv/" ~ url), SpanMode.shallow)))) {
76     if (isDir(f)) continue;
77     string name = baseName(f); name = name[0] == '@' ? name[1..$] : name;
78     if (stripExtension(name) == baseName(url)) {
79       foreach (string glob, string h; handlers) { 
80         if (globMatch(name, glob)) {
81           return shell(dweb_root ~ "/bin/" ~ h ~ " " ~ f);
82         }
83       }
84     }
85   }
86   if (baseName(url) != "index") return not_found(url);
87   return "";
88 }
89
90 bool evil(string s) {
91   foreach(char c; s) if (!isAlphaNum(c) && c != '/' && c != '-' && c != '_') return true;
92   return false;
93 }
94
95 string simple_template(string text, string[string] vars) {
96   return std.regex.replace!((match) { return vars[match[1]]; })(text, regex("\\{\\{\\s*(\\w+)\\s*\\}\\}", "g"));
97 }
98
99 void send_headers() {
100   foreach (header, header_body; headers) {
101     writefln("%s: %s", header, header_body);
102   }
103   writeln();
104 }
105
106 void main(string[] args) {
107   init_handlers();
108   dweb_root = getcwd()[0..$-4]; // take out bin/
109   headers["Content-Type"] = "text/html; charset=UTF-8";
110
111   string url = getenv("REQUEST_URI")[url_root.length..$];
112   if (evil(url)) {
113     headers["Status"] = "400 Bad Request";
114     send_headers();
115     writeln("bad url.");
116     return;
117   }
118   
119   string pagename = baseName(url);
120   if (pagename.length != 0) pagename = " - " ~ pagename;
121   pagename = site_title ~ pagename;
122   template_variables["url_root"] = url_root;
123   template_variables["site_title"] = site_title;
124   template_variables["site_subtitle"] = site_subtitle;
125   template_variables["pagename"] = pagename;
126   template_variables["nav_tree"] = do_nav_tree(url);
127   template_variables["content"] = do_content(url);
128
129   send_headers();
130   string default_template = readText(dweb_root ~ "/templates/default.html");
131   write(simple_template(default_template, template_variables));
132 }