Initial revision
[hcoop/zz_old/domtool.git] / src / apache / apache.sml
CommitLineData
182a2654
AC
1(*
2Domtool (http://hcoop.sf.net/)
3Copyright (C) 2004 Adam Chlipala
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License
7as published by the Free Software Foundation; either version 2
8of the License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18*)
19
20(* Apache vhost management module, with Webalizer support *)
21
22structure Apache :> APACHE =
23struct
24 open Config ApacheConfig Util
25
26 val vhosts = ref (NONE : TextIO.outstream option)
27
28 fun init () = vhosts := SOME (TextIO.openOut (scratchDir ^ "/vhosts.conf"))
29 fun finish () = (TextIO.closeOut (valOf (!vhosts));
30 vhosts := NONE)
31
32 fun handler {path, domain, parent, vars, paths, users, groups} =
33 let
34 val _ = Domtool.dprint ("Reading host " ^ path ^ " for " ^ domain ^ "....")
35
36 val vhosts = valOf (!vhosts)
37
38 val hf = TextIO.openIn path
39 val rewrite = ref false
40
41 val conf = TextIO.openOut (wblConfDir ^ "/" ^ domain ^ ".conf")
42 val _ = TextIO.output (conf, "LogFile\t" ^ logDir ^ domain ^ "-access.log\n" ^
43 "OutputDir\t" ^ wblDocDir ^ "/" ^ domain ^ "\n" ^
44 "HostName\t" ^ domain ^ "\n" ^
45 "HideSite\t" ^ domain ^ "\n" ^
46 "HideReferrer\t" ^ domain ^ "\n")
47
48 val dir = wblDocDir ^ "/" ^ domain
49 val _ =
50 if Posix.FileSys.access (dir, []) then
51 ()
52 else
53 Posix.FileSys.mkdir (dir, Posix.FileSys.S.flags [Posix.FileSys.S.ixoth, Posix.FileSys.S.irwxu,
54 Posix.FileSys.S.irgrp, Posix.FileSys.S.iwgrp])
55
56 val htac = TextIO.openOut (dir ^ "/.htaccess")
57 val user = ref defaultUser
58 val group = ref defaultGroup
59
60 fun loop (line, ()) =
61 (case String.tokens Char.isSpace line of
62 [] => ()
63 | ["User", user'] =>
64 if StringSet.member (users, user') then
65 user := user'
66 else
67 Domtool.error (path, "not authorized to run as " ^ user')
68 | ["Group", group'] =>
69 if StringSet.member (groups, group') then
70 group := group'
71 else
72 Domtool.error (path, "not authorized to run as group " ^ group')
73 | ["ServerAdmin", email] => TextIO.output (vhosts, "\tServerAdmin " ^ email ^ "\n")
74 | ["UserDir"] => TextIO.output (vhosts, "\tUserDir public_html\n\t<Directory /home/*/public_html/cgi-bin>\n\t\tAllowOverride None\n\t\tOptions ExecCGI\n\t\tAllow from all\n\t\tSetHandler cgi-script\n\t</Directory>\n\tScriptAliasMatch ^/~(.*)/cgi-bin/(.*) /home/$1/public_html/cgi-bin/$2\n")
75 | ["DocumentRoot", p] =>
76 if checkPath (paths, p) then
77 TextIO.output (vhosts, "\tDocumentRoot " ^ p ^ "\n")
78 else
79 print (path ^ ": not authorized to use " ^ p ^ "\n")
80 | "RewriteRule" :: args =>
81 (if not (!rewrite) then
82 (rewrite := true;
83 TextIO.output (vhosts, "\tRewriteEngine on\n"))
84 else
85 ();
86 TextIO.output (vhosts, foldl (fn (a, s) => s ^ " " ^ a) "\tRewriteRule" args ^ "\n"))
87 | ["Alias", from, to] =>
88 if checkPath (paths, to) then
89 TextIO.output (vhosts, "\tAlias " ^ from ^ " " ^ to ^ "\n")
90 else
91 Domtool.error (path, "not authorized to use " ^ to)
92 | "ErrorDocument" :: code :: rest =>
93 TextIO.output (vhosts, foldl (fn (a, s) => s ^ " " ^ a) ("\tErrorDocument " ^ code) rest ^ "\n")
94 | ["ScriptAlias", from, to] =>
95 if checkPath (paths, to) then
96 TextIO.output (vhosts, "\tScriptAlias " ^ from ^ " \"" ^ to ^ "\"\n")
97 else
98 Domtool.error (path, "not authorized to use " ^ to)
99 | ["SSI"] =>
100 TextIO.output (vhosts, "\t<Location />\n\t\tOptions +Includes +IncludesNOEXEC\n\t</Location>\n")
101 | ["ServerAlias", dom] =>
102 if validDomain dom then
103 let
104 val file = foldr (fn (c, s) => s ^ "/" ^ c) dataDir (String.fields (fn ch => ch = #".") dom) ^ ".aliased"
105 in
106 if Posix.FileSys.access (file, []) then
107 (TextIO.output (vhosts, "\tServerAlias " ^ dom ^ "\n");
108 TextIO.output (conf, "HideSite\t" ^ dom ^ "\n" ^
109 "HideReferrer\t" ^ dom ^ "\n"))
110 else
111 Domtool.error (path, "not authorized to ServerAlias " ^ dom)
112 end
113 else
114 Domtool.error (path, "bad host: " ^ dom)
115 | "WebalizerUsers" :: users =>
116 TextIO.output (htac, "AuthType Basic\n" ^
117 "AuthName \"Abulafia web account\"\n" ^
118 "AuthUserFile " ^ passwdFile ^ "\n" ^
119 foldl (fn (u, s) => s ^ " " ^ u) "Require user" users ^ "\n")
120 | ["AbuPrivate"] => TextIO.output (vhosts,
121 "\t<Location />\n" ^
122 "\t\tAuthName \"Abulafia web account\"\n" ^
123 "\t\tAuthType basic\n" ^
124 "\t\tAuthUserFile " ^ passwdFile ^ "\n" ^
125 "\t\tRequire valid-user\n" ^
126 "\t\tOrder Deny,Allow\n" ^
127 "\t\tDeny from all\n" ^
128 "\t\tAllow from 127.0.0.1\n" ^
129 "\t\tAllow from 63.246.10.45\n" ^
130 "\t\tSatisfy any\n" ^
131 "\t</Location>\n")
132 | ["Default"] => (TextIO.output (vhosts, "\tServerAlias " ^ parent ^ "\n");
133 TextIO.output (conf, "HideSite\t" ^ parent ^ "\n" ^
134 "HideReferrer\t" ^ parent ^ "\n"))
135 | ["CGI", p] =>
136 if checkPath (paths, p) then
137 TextIO.output (vhosts, "\t<Directory " ^ p ^ ">\n" ^
138 "\t\tOptions ExecCGI\n" ^
139 "\t\tSetHandler cgi-script\n" ^
140 "\t</Directory>\n")
141 else
142 Domtool.error (path, "not authorized to use " ^ p)
143 | ["HTML", p] =>
144 if checkPath (paths, p) then
145 TextIO.output (vhosts, "\t<Directory " ^ p ^ ">\n" ^
146 "\t\tForceType text/html\n" ^
147 "\t</Directory>\n")
148 else
149 Domtool.error (path, "not authorized to use " ^ p)
150 | cmd::_ => Domtool.error (path, "unknown option: " ^ cmd))
151 in
152 TextIO.output (vhosts, "<VirtualHost *>\n" ^
153 "\tServerName " ^ domain ^ "\n" ^
154 "\tErrorLog " ^ logDir ^ domain ^ "-error.log\n" ^
155 "\tCustomLog " ^ logDir ^ domain ^ "-access.log combined\n" ^
156 "\tIndexOptions FancyIndexing FoldersFirst\n");
157 ioLoop (fn () => Domtool.inputLine hf) loop ();
158 TextIO.output (vhosts, "\tUser ");
159 TextIO.output (vhosts, !user);
160 TextIO.output (vhosts, "\n\tGroup ");
161 TextIO.output (vhosts, !group);
162 TextIO.output (vhosts, "\n</VirtualHost>\n\n");
163 TextIO.closeIn hf;
164 TextIO.closeOut conf;
165 TextIO.closeOut htac
166 end handle Io => Domtool.error (path, "IO error")
167
168 fun publish () =
169 if OS.Process.isSuccess (OS.Process.system
170 (diff ^ " " ^ scratchDir ^ "/vhosts.conf " ^ dataFile)) then
171 OS.Process.success
172 else if not (OS.Process.isSuccess (OS.Process.system
173 (cp ^ " " ^ scratchDir ^ "/vhosts.conf " ^ dataFile))) then
174 (print "Error copying vhosts.conf\n";
175 OS.Process.failure)
176 else if OS.Process.isSuccess (OS.Process.system pubCommand) then
177 OS.Process.success
178 else
179 (print "Error publishing vhosts.conf\n";
180 OS.Process.failure)
181
182 fun mkdom _ = OS.Process.success
183
184 val _ = Domtool.setVhostHandler {init = init,
185 file = handler,
186 finish = finish,
187 publish = publish,
188 mkdom = mkdom}
189end
190