URL handling
[hcoop/domtool2.git] / src / plugins / apache.sml
... / ...
CommitLineData
1(* HCoop Domtool (http://hcoop.sourceforge.net/)
2 * Copyright (c) 2006, Adam Chlipala
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *)
18
19(* Apache HTTPD handling *)
20
21structure Apache :> APACHE = struct
22
23open Ast
24
25val _ = Env.type_one "proxy_port"
26 Env.int
27 (fn n => n > 1024)
28
29val _ = Env.type_one "proxy_target"
30 Env.string
31 (fn s =>
32 let
33 fun default () = List.exists (fn s' => s = s') Config.Apache.proxyTargets
34 in
35 case String.fields (fn ch => ch = #":") s of
36 ["http", "//localhost", rest] =>
37 (case String.fields (fn ch => ch = #"/") rest of
38 port :: _ =>
39 (case Int.fromString port of
40 NONE => default ()
41 | SOME n => n > 1024 orelse default ())
42 | _ => default ())
43 | _ => default ()
44 end)
45
46val _ = Env.type_one "rewrite_arg"
47 Env.string
48 (CharVector.all Char.isAlphaNum)
49
50fun validLocation s =
51 size s > 0 andalso size s < 1000 andalso CharVector.all
52 (fn ch => Char.isAlphaNum ch
53 orelse ch = #"-"
54 orelse ch = #"_"
55 orelse ch = #"."
56 orelse ch = #"/") s
57
58val _ = Env.type_one "location"
59 Env.string
60 validLocation
61
62val dl = ErrorMsg.dummyLoc
63
64val _ = Main.registerDefault ("WebNodes",
65 (TList (TBase "node", dl), dl),
66 (fn () => (EList (map (fn s => (EString s, dl)) Config.Apache.webNodes), dl)))
67
68val _ = Main.registerDefault ("SSL",
69 (TBase "bool", dl),
70 (fn () => (EVar "false", dl)))
71
72val _ = Main.registerDefault ("User",
73 (TBase "your_user", dl),
74 (fn () => (EString (Domain.getUser ()), dl)))
75
76val _ = Main.registerDefault ("Group",
77 (TBase "your_group", dl),
78 (fn () => (EString (Domain.getUser ()), dl)))
79
80val _ = Main.registerDefault ("DocumentRoot",
81 (TBase "your_path", dl),
82 (fn () => (EString ("/home/" ^ Domain.getUser () ^ "/public_html"), dl)))
83
84val _ = Main.registerDefault ("ServerAdmin",
85 (TBase "email", dl),
86 (fn () => (EString (Domain.getUser () ^ "@" ^ Config.defaultDomain), dl)))
87
88
89val redirect_code = fn (EVar "temp", _) => SOME "temp"
90 | (EVar "permanent", _) => SOME "permanent"
91 | (EVar "seeother", _) => SOME "seeother"
92 | (EVar "redir300", _) => SOME "300"
93 | (EVar "redir301", _) => SOME "301"
94 | (EVar "redir302", _) => SOME "302"
95 | (EVar "redir303", _) => SOME "303"
96 | (EVar "redir304", _) => SOME "304"
97 | (EVar "redir305", _) => SOME "305"
98 | (EVar "redir307", _) => SOME "307"
99 | _ => NONE
100
101val flag = fn (EVar "redirect", _) => SOME "R"
102 | (EVar "forbidden", _) => SOME "F"
103 | (EVar "gone", _) => SOME "G"
104 | (EVar "last", _) => SOME "L"
105 | (EVar "chain", _) => SOME "C"
106 | (EVar "nosubreq", _) => SOME "NS"
107 | (EVar "nocase", _) => SOME "NC"
108 | (EVar "qsappend", _) => SOME "QSA"
109 | (EVar "noescape", _) => SOME "NE"
110 | (EVar "passthrough", _) => SOME "PT"
111 | (EApp ((EVar "mimeType", _), e), _) =>
112 Option.map (fn s => "T=" ^ s) (Env.string e)
113 | (EApp ((EVar "redirectWith", _), e), _) =>
114 Option.map (fn s => "R=" ^ s) (redirect_code e)
115 | (EApp ((EVar "skip", _), e), _) =>
116 Option.map (fn n => "S=" ^ Int.toString n) (Env.int e)
117 | (EApp ((EApp ((EVar "env", _), e1), _), e2), _) =>
118 (case Env.string e1 of
119 NONE => NONE
120 | SOME s1 => Option.map (fn s2 => "E=" ^ s1 ^ ":" ^ s2)
121 (Env.string e2))
122
123 | _ => NONE
124
125val cond_flag = fn (EVar "cond_nocase", _) => SOME "NC"
126 | (EVar "ornext", _) => SOME "OR"
127 | _ => NONE
128
129
130val vhostsChanged = ref false
131
132val () = Slave.registerPreHandler
133 (fn () => vhostsChanged := false)
134
135val () = Slave.registerFileHandler (fn fs =>
136 let
137 val spl = OS.Path.splitDirFile (#file fs)
138 in
139 if String.isSuffix ".vhost" (#file spl)
140 orelse String.isSuffix ".vhost_ssl" (#file spl) then
141 (vhostsChanged := true;
142 case #action fs of
143 Slave.Delete =>
144 ignore (OS.Process.system (Config.rm
145 ^ " -rf "
146 ^ Config.Apache.confDir
147 ^ "/"
148 ^ #file spl))
149 | _ =>
150 ignore (OS.Process.system (Config.cp
151 ^ " "
152 ^ #file fs
153 ^ " "
154 ^ Config.Apache.confDir
155 ^ "/"
156 ^ #file spl)))
157 else
158 ()
159 end)
160
161val () = Slave.registerPostHandler
162 (fn () =>
163 (if !vhostsChanged then
164 Slave.shellF ([Config.Apache.reload],
165 fn cl => "Error reloading Apache with " ^ cl)
166 else
167 ()))
168
169val vhostFiles : TextIO.outstream list ref = ref []
170fun write s = app (fn file => TextIO.output (file, s)) (!vhostFiles)
171
172val rewriteEnabled = ref false
173
174val () = Env.containerV_one "vhost"
175 ("host", Env.string)
176 (fn (env, host) =>
177 let
178 val nodes = Env.env (Env.list Env.string) (env, "WebNodes")
179
180 val ssl = Env.env Env.bool (env, "SSL")
181 val user = Env.env Env.string (env, "User")
182 val group = Env.env Env.string (env, "Group")
183 val docroot = Env.env Env.string (env, "DocumentRoot")
184 val sadmin = Env.env Env.string (env, "ServerAdmin")
185
186 val fullHost = host ^ "." ^ Domain.currentDomain ()
187 val confFile = fullHost ^ (if ssl then ".vhost_ssl" else ".vhost")
188 in
189 rewriteEnabled := false;
190 vhostFiles := map (fn node =>
191 let
192 val file = Domain.domainFile {node = node,
193 name = confFile}
194 in
195 TextIO.output (file, "<VirtualHost ");
196 TextIO.output (file, Domain.nodeIp node);
197 TextIO.output (file, ":");
198 TextIO.output (file, if ssl then
199 "443"
200 else
201 "80");
202 TextIO.output (file, ">\n");
203 file
204 end)
205 nodes;
206 write "\tSuexecUserGroup ";
207 write user;
208 write " ";
209 write group;
210 write "\n\tDocumentRoot ";
211 write docroot;
212 write "\n\tServerAdmin ";
213 write sadmin;
214 write "\n"
215 end,
216 fn () => (write "</VirtualHost>\n";
217 app TextIO.closeOut (!vhostFiles)))
218
219val () = Env.container_one "location"
220 ("prefix", Env.string)
221 (fn prefix =>
222 (write "\t<Location ";
223 write prefix;
224 write ">\n"),
225 fn () => write "\t</Location>\n")
226
227val () = Env.container_one "directory"
228 ("directory", Env.string)
229 (fn directory =>
230 (write "\t<Directory ";
231 write directory;
232 write ">\n"),
233 fn () => write "\t</Directory>\n")
234
235fun checkRewrite () =
236 if !rewriteEnabled then
237 ()
238 else
239 (write "\tRewriteEngine on\n";
240 rewriteEnabled := true)
241
242val () = Env.action_three "localProxyRewrite"
243 ("from", Env.string, "to", Env.string, "port", Env.int)
244 (fn (from, to, port) =>
245 (checkRewrite ();
246 write "\tRewriteRule\t";
247 write from;
248 write "\thttp://localhost:";
249 write (Int.toString port);
250 write "/";
251 write to;
252 write " [P]\n"))
253
254val () = Env.action_two "proxyPass"
255 ("from", Env.string, "to", Env.string)
256 (fn (from, to) =>
257 (write "\tProxyPass\t";
258 write from;
259 write "\t";
260 write to;
261 write "\n"))
262
263val () = Env.action_two "proxyPassReverse"
264 ("from", Env.string, "to", Env.string)
265 (fn (from, to) =>
266 (write "\tProxyPassReverse\t";
267 write from;
268 write "\t";
269 write to;
270 write "\n"))
271
272val () = Env.action_three "rewriteRule"
273 ("from", Env.string, "to", Env.string, "flags", Env.list flag)
274 (fn (from, to, flags) =>
275 (checkRewrite ();
276 write "\tRewriteRule\t";
277 write from;
278 write "\t";
279 write to;
280 case flags of
281 [] => ()
282 | flag::rest => (write " [";
283 write flag;
284 app (fn flag => (write ",";
285 write flag)) rest;
286 write "]");
287 write "\n"))
288
289val () = Env.action_three "rewriteCond"
290 ("test", Env.string, "pattern", Env.string, "flags", Env.list cond_flag)
291 (fn (from, to, flags) =>
292 (checkRewrite ();
293 write "\tRewriteCond\t";
294 write from;
295 write "\t";
296 write to;
297 case flags of
298 [] => ()
299 | flag::rest => (write " [";
300 write flag;
301 app (fn flag => (write ",";
302 write flag)) rest;
303 write "]");
304 write "\n"))
305
306val () = Env.action_two "alias"
307 ("from", Env.string, "to", Env.string)
308 (fn (from, to) =>
309 (write "\tAlias\t";
310 write from;
311 write " ";
312 write to;
313 write "\n"))
314
315val () = Env.action_two "scriptAlias"
316 ("from", Env.string, "to", Env.string)
317 (fn (from, to) =>
318 (write "\tScriptAlias\t";
319 write from;
320 write " ";
321 write to;
322 write "\n"))
323
324val () = Env.action_two "errorDocument"
325 ("code", Env.string, "handler", Env.string)
326 (fn (code, handler) =>
327 (write "\tErrorDocument\t";
328 write code;
329 write " ";
330 write handler;
331 write "\n"))
332
333
334end