1 structure Init
:> INIT
=
7 fun nullableFromSql f x
=
12 fun nullableToSql f x
=
17 exception Access
of string
20 fun conn () = C
.conn dbstring
23 type user
= {id
: int, name
: string, rname
: string, bal
: int, joined
: C
.timestamp
,
24 app
: int, shares
: int, paypal
: string option
, checkout
: string option
}
26 val db
= ref (NONE
: C
.conn option
)
27 val user
= ref (NONE
: user option
)
35 fun rowError (tab
, vs
) = raise Fail ("Bad " ^ tab ^
"row: " ^ makeSet fromSql vs
)
37 fun getDb () = valOf (!db
)
39 fun mkUserRow
[id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
] =
40 {id
= C
.intFromSql id
, name
= C
.stringFromSql name
, rname
= C
.stringFromSql rname
,
41 bal
= C
.intFromSql bal
, joined
= C
.timestampFromSql joined
,
42 app
= C
.intFromSql app
, shares
= C
.intFromSql shares
,
43 paypal
= nullableFromSql C
.stringFromSql paypal
,
44 checkout
= nullableFromSql C
.stringFromSql checkout
}
45 | mkUserRow row
= rowError ("user", row
)
55 case Web
.getCgi
"REMOTE_USER" of
56 NONE
=> raise Fail
"Not logged in"
60 if String.isSuffix kerberosSuffix name
then
61 String.substring (name
, 0, size name
- size kerberosSuffix
)
65 case C
.oneOrNoRows
c ($`SELECT id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
67 WHERE name
=^
(C
.stringToSql name
)`
) of
68 NONE
=> raise Fail
"User not found"
74 case C
.oneOrNoRows
c ($`SELECT ipaddr
76 WHERE id
= ^
(C
.intToSql (#app r
))
77 AND ipaddr IS NOT NULL`
) of
79 if Web
.getParam
"agree" = "on" then
80 (case Web
.getCgi
"REMOTE_ADDR" of
81 NONE
=> raise Fail
"REMOTE_ADDR not set"
83 ignore (C
.dml
c ($`UPDATE MemberApp
84 SET ipaddr
= ^
(C
.stringToSql ra
),
85 applied
= CURRENT_TIMESTAMP
86 WHERE id
= ^
(C
.intToSql (#app r
))`
)))
102 fun getUser () = valOf (!user
)
103 fun getUserId () = #
id (getUser ())
104 fun getUserName () = #
name (getUser ())
107 mkUserRow (C
.oneRow (getDb ()) ($`SELECT id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
109 WHERE id
= ^
(C
.intToSql id
)`
))
112 C
.map (getDb ()) mkUserRow ($`SELECT id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
116 fun listActiveUsers () =
117 C
.map (getDb ()) mkUserRow ($`SELECT id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
121 fun nextSeq (db
, seq
) =
122 case C
.oneRow
db ($`SELECT
nextval('^
(seq
)')`
) of
123 [id
] => C
.intFromSql id
124 | _
=> raise Fail
"Bad next sequence val"
126 fun addUser (name
, rname
, bal
, app
, shares
) =
129 val id
= nextSeq (db
, "WebUserSeq")
131 C
.dml
db ($`INSERT INTO
WebUser (id
, name
, rname
, bal
, joined
, app
, shares
)
132 VALUES (^
(C
.intToSql id
), ^
(C
.stringToSql name
), ^
(C
.stringToSql rname
), ^
(C
.intToSql bal
),
133 CURRENT_TIMESTAMP
, ^
(C
.intToSql app
), ^
(C
.intToSql shares
))`
);
137 fun modUser (user
: user
) =
141 ignore (C
.dml
db ($`UPDATE WebUser SET
142 name
= ^
(C
.stringToSql (#name user
)), rname
= ^
(C
.stringToSql (#rname user
)),
143 bal
= ^
(C
.intToSql (#bal user
)), app
= ^
(C
.intToSql (#app user
)),
144 shares
= ^
(C
.intToSql (#shares user
)),
145 paypal
= ^
(nullableToSql C
.stringToSql (#paypal user
)),
146 checkout
= ^
(nullableToSql C
.stringToSql (#checkout user
))
147 WHERE id
= ^
(C
.intToSql (#id user
))`
))
151 C
.map (getDb ()) mkUserRow ($`SELECT id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
154 ORDER BY shares DESC
, name`
)
157 C
.dml (getDb ()) ($`DELETE FROM WebUser WHERE id
= ^
(C
.intToSql id
)`
)
159 fun validUsername name
=
161 andalso size name
> 0
162 andalso Char.isLower (String.sub (name
, 0))
163 andalso CharVector
.all
Char.isAlphaNum name
165 fun userNameToId name
=
166 case C
.oneOrNoRows (getDb ()) ($`SELECT id FROM WebUser WHERE name
= ^
(C
.stringToSql name
)`
) of
167 SOME
[id
] => SOME (C
.intFromSql id
)
171 case C
.oneRow (getDb ()) "SELECT CURRENT_DATE" of
172 [d
] => C
.stringFromSql d
173 | r
=> rowError ("dateString", r
)
175 fun grandfatherUsers () =
179 fun mkApp
[id
, name
, rname
] =
181 val id
= C
.intFromSql id
182 val name
= C
.stringFromSql name
183 val rname
= C
.stringFromSql rname
185 val aid
= nextSeq (db
, "MemberAppSeq")
187 ignore (C
.dml
db ($`INSERT INTO
MemberApp (id
, name
, rname
, gname
, email
, forward
, uses
, other
,
188 passwd
, status
, applied
, confirmed
, decided
, msg
)
189 VALUES (^
(C
.intToSql aid
), ^
(C
.stringToSql name
), ^
(C
.stringToSql rname
),
190 NULL
, '^name^
(emailSuffix
)', FALSE
, 'GRANDFATHERED
', 'GRANDFATHERED
',
191 'GRANDFATHERED
', 4, CURRENT_TIMESTAMP
, CURRENT_TIMESTAMP
,
192 CURRENT_TIMESTAMP
, 'GRANDFATHERED
')`
));
193 ignore (C
.dml
db ($`UPDATE WebUser SET app
= ^
(C
.intToSql aid
) WHERE id
= ^
(C
.intToSql id
)`
))
196 C
.app db mkApp
"SELECT id, name, rname FROM WebUser WHERE app IS NULL"
199 type node
= {id
: int, name
: string, descr
: string, debian
: string}
201 fun mkNodeRow
[id
, name
, descr
, debian
] =
202 {id
= C
.intFromSql id
, name
= C
.stringFromSql name
, descr
= C
.stringFromSql descr
,
203 debian
= C
.stringFromSql debian
}
204 | mkNodeRow row
= rowError ("node", row
)
207 C
.map (getDb ()) mkNodeRow ($`SELECT id
, name
, descr
, debian
212 case C
.oneRow (getDb ()) ($`SELECT name
214 WHERE id
= ^
(C
.intToSql id
)`
) of
215 [name
] => C
.stringFromSql name
216 | row
=> rowError ("nodeName", row
)
219 case C
.oneRow (getDb ()) ($`SELECT debian
221 WHERE id
= ^
(C
.intToSql id
)`
) of
222 [debian
] => C
.stringFromSql debian
223 | row
=> rowError ("nodeDebian", row
)
227 OS
.SysErr (name
, sop
) =>
228 "System error: " ^ name ^
231 | SOME syserr
=> ": " ^ OS
.errorName syserr ^
": " ^ OS
.errorMsg syserr
)
236 val proc
= Unix
.execute ("/usr/bin/tokens", [])
237 val inf
= Unix
.textInstreamOf proc
240 case TextIO.inputLine inf
of
241 NONE
=> String.concat (rev acc
)
242 | SOME s
=> reader (s
:: acc
)
245 before (TextIO.closeIn inf
;
246 ignore (Unix
.reap proc
))
249 fun tokensForked () =
250 case Posix
.Process
.fork () of
251 NONE
=> (OS
.Process
.system
"/usr/bin/tokens >/tmp/tokens.child";
252 OS
.Process
.exit OS
.Process
.success
)
253 | _
=> ignore (OS
.Process
.system
"/usr/bin/tokens >/tmp/tokens.parent")
255 fun unmigratedUsers () =
256 List.filter (fn user
=>
257 (ignore (Posix
.SysDB
.getpwnam (#name user
));
259 handle OS
.SysErr _
=> true) (listActiveUsers ())
261 fun usersDiff (ls1
, ls2
) =
262 {onlyInFirst
= List.filter (fn x
=> not (Util
.mem (x
, ls2
))) ls1
,
263 onlyInSecond
= List.filter (fn x
=> not (Util
.mem (x
, ls1
))) ls2
}
265 fun listUsernames () = C
.map (getDb ())
266 (fn [name
] => C
.stringFromSql name
267 | row
=> rowError ("listUsernames", row
))
268 "SELECT name FROM WebUserActive ORDER BY name"
271 fun explore (dir
, level
, acc
) =
276 val dr
= Posix
.FileSys
.opendir dir
279 case Posix
.FileSys
.readdir dr
of
283 val dir
' = OS
.Path
.joinDirFile
{dir
= dir
,
286 val acc
= explore (dir
', level
+1, acc
)
292 before Posix
.FileSys
.closedir dr
295 val acc
= explore ("/afs/hcoop.net/user", 0, [])
297 List.map OS
.Path
.file acc
300 fun searchPaypal paypal
=
301 C
.map (getDb ()) mkUserRow ($`SELECT id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
303 WHERE paypal
= ^
(C
.stringToSql paypal
)
306 fun searchCheckout checkout
=
307 C
.map (getDb ()) mkUserRow ($`SELECT id
, name
, rname
, bal
, joined
, app
, shares
, paypal
, checkout
309 WHERE checkout
= ^
(C
.stringToSql checkout
)