Propagate domain validity check code to app
[hcoop/zz_old/portal.git] / app / app.sml
CommitLineData
20a679fc 1structure App :> APP =
2struct
3
4val baseUrl = "http://join.hcoop.net/join/"
fd650826 5val portalUrl = "https://members.hcoop.net/portal/"
20a679fc 6
7open Sql
8
9structure C = PgClient
10
11val db = ref (NONE : C.conn option)
12
13val rnd = ref (Random.rand (0, 0))
14
15fun init () =
16 let
fd650826 17 val c = C.conn "dbname='hcoop_hcoop'"
20a679fc 18 in
19 db := SOME c;
20 C.dml c "BEGIN";
21 rnd := Random.rand (SysWord.toInt (Posix.Process.pidToWord (Posix.ProcEnv.getpid ())),
22 SysWord.toInt (Posix.Process.pidToWord (Posix.ProcEnv.getpid ())))
23 end
24
25fun getDb () = valOf (!db)
26
27fun done () =
28 let
29 val c = getDb ()
30 in
31 C.dml c "COMMIT";
32 C.close c;
33 db := NONE
34 end
35
5146e435 36fun readFile fname =
37 let
38 val inf = TextIO.openIn fname
39
40 fun readLines lines =
41 case TextIO.inputLine inf of
42 NONE => String.concat (List.rev lines)
43 | SOME line => readLines (line :: lines)
44 in
45 readLines []
46 before TextIO.closeIn inf
47 end
48
fd650826 49fun readTosBody () = readFile "/home/hcoop/public_html/tos.body.html"
50fun readTosAgree () = readFile "/home/hcoop/public_html/tos.agree.html"
51fun readTosMinorAgree () = readFile "/home/hcoop/public_html/tos.agree.minor.html"
5146e435 52
20a679fc 53fun sendMail (to, subj, intro, footer, id) =
54 let
57215bc3 55 val (name, rname, gname, email, forward, uses, other) =
56 case C.oneOrNoRows (getDb ()) ($`SELECT name, rname, gname, email, forward, uses, other FROM MemberApp WHERE id = ^(C.intToSql id)`) of
57 SOME [name, rname, gname, email, forward, uses, other] =>
5146e435 58 (C.stringFromSql name, C.stringFromSql rname,
59 if C.isNull gname then NONE else SOME (C.stringFromSql gname),
57215bc3 60 C.stringFromSql email,
5146e435 61 C.boolFromSql forward, C.stringFromSql uses,
62 C.stringFromSql other)
20a679fc 63 | _ => raise Fail "Bad sendMail row"
64
65 val proc = Unix.execute ("/usr/sbin/exim4", ["-t"])
66 fun mwrite s = TextIO.output (Unix.textOutstreamOf proc, s)
67 in
453d7579 68 mwrite ("From: Hcoop Application System <join@hcoop.net>\nTo: ");
20a679fc 69 mwrite (to);
70 mwrite ("\nSubject: ");
71 mwrite subj;
72 mwrite ("\n\n");
73 mwrite intro;
74 mwrite ("\n\nUsername: ");
75 mwrite (name);
5146e435 76 mwrite ("\nMember real name: ");
20a679fc 77 mwrite (rname);
5146e435 78 case gname of
79 NONE => ()
80 | SOME gname => (mwrite "\nLegal guardian name: ";
81 mwrite gname);
57215bc3 82 mwrite ("\nE-mail address: ");
83 mwrite email;
20a679fc 84 mwrite ("\nForward e-mail: ");
85 mwrite (if forward then "yes" else "no");
86 mwrite ("\n\nDesired uses:\n");
87 mwrite (uses);
88 mwrite ("\n\nOther information:\n");
89 mwrite (other);
90 mwrite ("\n\n");
91 footer mwrite;
92 OS.Process.isSuccess (Unix.reap proc)
93 end
94
5146e435 95type application = { name : string, rname : string, gname : string option, email : string,
20a679fc 96 forward : bool, uses : string, other : string }
97
5146e435 98fun apply {name, rname, gname, email, forward, uses, other} =
20a679fc 99 let
100 val db = getDb ()
101 in
102 case C.oneRow db ($`SELECT nextval('MemberAppSeq')`) of
103 [id] =>
104 let
105 val id = C.intFromSql id
106 val passwd = Int.toString (Int.abs (Random.randInt (!rnd)))
107 in
5146e435 108 C.dml db ($`INSERT INTO MemberApp (id, name, rname, gname, email, forward, uses, other, passwd, status, applied, msg)
20a679fc 109 VALUES (^(C.intToSql id), ^(C.stringToSql name), ^(C.stringToSql rname),
5146e435 110 ^(case gname of NONE => "NULL" | SOME gname => C.stringToSql gname),
20a679fc 111 ^(C.stringToSql email), ^(C.boolToSql forward), ^(C.stringToSql uses),
453d7579 112 ^(C.stringToSql other), ^(C.stringToSql passwd), 0, CURRENT_TIMESTAMP, '')`);
20a679fc 113 sendMail (email, "Confirm membership application",
114 "We've received a request to join the Internet Hosting Cooperative (hcoop.net) with this e-mail address.",
115 fn mwrite => (mwrite ("To confirm this application, visit ");
116 mwrite (baseUrl);
117 mwrite ("confirm?id=");
118 mwrite (Int.toString id);
119 mwrite ("&p=");
120 mwrite (passwd);
121 mwrite ("\n")),
122 id)
123 end
124 | _ => raise Fail "Bad next sequence val"
125 end
126
4e630cf8 127fun isIdent ch = Char.isLower ch orelse Char.isDigit ch orelse ch = #"-"
20a679fc 128
129fun validHost s =
130 size s > 0 andalso size s < 20 andalso List.all isIdent (String.explode s)
131
132fun validDomain s =
133 size s > 0 andalso size s < 100 andalso List.all validHost (String.fields (fn ch => ch = #".") s)
134
135fun validUser s =
136 size s > 0 andalso size s < 50 andalso List.all
137 (fn ch => isIdent ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-" orelse ch = #"+")
138 (String.explode s)
139
140fun validEmailUser s =
141 size s > 0 andalso size s < 50 andalso List.all
142 (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-" orelse ch = #"+")
143 (String.explode s)
144
145fun validEmail s =
146 (case String.fields (fn ch => ch = #"@") s of
147 [user, host] => validEmailUser user andalso validDomain host
148 | _ => false)
149
150fun userExists name =
151 (Posix.SysDB.getpwnam name; true) handle OS.SysErr _ => false
152
153fun confirm (id, passwd) =
154 let
155 val db = getDb ()
156 in
157 case C.oneOrNoRows db ($`SELECT id FROM MemberApp WHERE id = ^(C.intToSql id) AND passwd = ^(C.stringToSql passwd) AND status = 0`) of
158 SOME _ =>
159 (C.dml db ($`UPDATE MemberApp SET status = 1, confirmed = CURRENT_TIMESTAMP WHERE id = ^(C.intToSql id)`);
ce7b516a 160 sendMail ("board@hcoop.net",
20a679fc 161 "New membership application",
162 "We've received a new request to join hcoop.",
163 fn mwrite => (mwrite ("Open applications: ");
164 mwrite (portalUrl);
165 mwrite ("apps")),
166 id))
167 | NONE => false
168 end
169
fd650826 170end