Skip to content

Instantly share code, notes, and snippets.

@smiler
Created February 29, 2012 12:22
Show Gist options
  • Save smiler/1940454 to your computer and use it in GitHub Desktop.
Save smiler/1940454 to your computer and use it in GitHub Desktop.
SML web chat
(**************************************************************************
* File : inl2.sml
* Project : Inlämningsuppgift 2
* Author : Christian Axelsson
* Created : 2005-12-27
* Description : Webchat
**************************************************************************)
(*
load "TextIO"; load "Random"; load "Time"; load "Mosmlcgi"; load "Mosmlcookie";
*)
(*
Konfiguration
=============
*)
val sessionLength = 3600 (* Hur länge en session varar. *)
val userfile = "users.db" (* Var användare lagras. *)
val sessionfile = "sessions.db" (* Var sessioner lagras. *)
val postfile = "posts.db" (* Var meddelanden lagras. *)
val maxposts = 10 (* Maximalt antal poster som skall sparas. *)
(*
==================
Slut konfiguration
*)
open Mosmlcgi;
open Mosmlcookie;
(* TYPE: inputType
Dom olika alternativen för type-fältet i XHTMLs <input>-tag.
Se standarden för XHTML för mer info.
*)
datatype inputTypes = TEXT
| PASSWORD
| TEXTAREA
| SUBMIT
| HIDDEN;
(* TYPE: session
Beskriver en session för användaren user som utgår efter expire med
identifieraren id.
*)
type session = { user : string, expire : Date.date, id : string };
(* TYPE: user
Beskriver en användare med användarnamnet name och lösenordet password.
*)
type user = { name : string, password : string };
(* TYPE: post
Beskriver ett meddelande i chatten där user är användaren som postat, time
är tidpunkten då meddelandet postades, ip är den IP-address användaren hade
när han postade och message är meddelandet.
*)
type post = { user : string, time : Date.date, ip : string, message : string }
(* printPage (title, header, body)
TYPE : string option * string * string * string -> unit
PRE : (inget)
POST : (inget)
SIDE-EFFECTS : En HTML-sida med titeln title, överskriften header och
kroppen body. Om extraheaders inte är NONE skickas
valOf(extraheaders) med som HTTP-headers.
*)
fun printPage (extraheaders, title, header, body) =
print((if extraheaders = NONE then
"Content-type: text/html\n\n"
else
valOf extraheaders ^ "\nContent-type: text/html\n\n") ^
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>" ^
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" ^
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" ^
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"sv\">\n" ^
" <head>\n" ^
" <title>" ^ title ^ "</title>" ^
" </head>\n" ^
" <body>\n" ^
" <div id=\"header\">\n" ^
" <h1>" ^ header ^ "</h1>\n" ^
" </div>\n" ^
body ^ "\n" ^
" </body>\n" ^
"</html>\n");
(* makeErrorPage msg
TYPE : string -> string
PRE : (inget)
POST : HTML-kod som beskriver ett felmeddelande specifierat av msg
*)
fun makeErrorPage msg = "<div id=\"errorPage\">\n" ^
" <p>" ^ msg ^ "</p>\n" ^
"</div>";
(* makeForm (id, form, submit)
TYPE : string -> string
PRE : (inget)
POST : HTML-kod för form som ett formulär med id id som skickas
till submit
EXAMPLES : makeForm("form",
"<input type=\"text\" name=\"namn\">" ^
"<input type=\"submit\" value=\"Skicka\">", "") =
"<form method=\"post\" action=\"\">\n
<div id=\"form\">
<input type=\"text\" name=\"namn\">
<input type=\"submit\" value=\"Skicka\">
</div>
</form>\n"
*)
fun makeForm (id, form, submit) =
"<form method=\"post\" action=\"" ^ submit ^ "\">\n<div id=\"" ^ id ^ "\">"
^ form ^ "</div></form>\n";
(* makeInput (intype, name, value)
TYPE : string * string * string option -> string
PRE : name <= 1 tecken
POST : En inputbox med namnet name och som är av typen intype.
Om value är SOME({value}) får det inputboxen innehållet som
är specifierat av valOf(value) annars inget innehåll.
EXAMPLES : makeInput(TEXTAREA, "message", NONE) =
"<textarea name=\"message\" id=\"message\"
rows=\"5\" cols=\"35\">
</textarea>"
makeInput(HIDDEN, "secret", SOME("topsecret")) =
"<input type=\"hidden\" name=\"secret\"
id=\"secret\" value=\"topsecret\" />"
*)
fun makeInput (TEXTAREA, name, value) =
"<textarea name=\"" ^ name ^ "\" id=\"" ^ name ^
"\" rows=\"5\" cols=\"35\">" ^
(if Option.isSome value then
valOf value
else
"") ^ "</textarea>"
| makeInput (intype, name, value) =
let
val input = case intype of
TEXT => "text"
| PASSWORD => "password"
| SUBMIT => "submit"
| HIDDEN => "hidden"
| TEXTAREA => "Why cant mosml combine patternmatching" ^
"in fun and case? Crap :("
in
"<input type=\"" ^ input ^ "\" name=\"" ^ name ^ "\" " ^
"id=\"" ^ name ^ "\" " ^
(if Option.isSome value then
"value=\"" ^ valOf value ^ "\" />"
else
"/>")
end;
(* makeFrontPage ()
TYPE : unit -> string
PRE : (inget)
POST : HTML-kod för en sida där användare kan logga in.
*)
fun makeFrontPage () =
"<div id=\"body\">\n" ^
makeForm("loginForm",
"<label for=\"username\">Namn:</label>" ^
makeInput(TEXT, "username", NONE) ^ "<br />\n" ^
"<label for=\"password\">Lösenord:</label>" ^
makeInput(PASSWORD, "password", NONE) ^ "<br />\n" ^
makeInput(SUBMIT, "submit", SOME("Logga in")) ^ "\n" ^
"<!-- UGLY HACK BECAUSE MOSMLLIB DOSESN'T SUPPORT" ^
"PULLING INFO FROM GET AND POST AT THE SAME TIME -->\n"
^ makeInput(HIDDEN, "a", SOME("login")),
valOf cgi_script_name ^ "?a=login") ^
"<br />\n" ^ "Om du inte har någon användare klicka " ^
"<a href=\"" ^ valOf cgi_script_name ^ "?a=register\">här</a>"
^ "</div>";
(* makeRegistrationPage ()
TYPE : unit -> string
PRE : (inget)
POST : HTML-kod för ett forumlär där användare kan registrera sig.
*)
fun makeRegistrationPage () =
makeForm("registrationForm",
"<label for=\"username\">Namn:" ^
makeInput(TEXT, "username", NONE) ^ "<br />\n" ^
"<label for=\"password\">Lösenord:" ^
makeInput(PASSWORD, "password", NONE) ^ "<br />\n" ^
makeInput(SUBMIT, "submit", SOME("Registrera")) ^ "\n" ^
makeInput(HIDDEN, "a", SOME("register")),
valOf cgi_script_name ^ "?a=register");
(* makeRegisterdPage ()
TYPE : unit -> string
PRE : (inget)
POST : HTML-kod för en sida som talar om att användaren har blivit
registrerad.
*)
fun makeRegisterdPage () = "Klicka <a href=\"" ^ valOf cgi_script_name ^ "\">"
^ "här</a> för att logga in.";
(* makeWrongUserPassPage ()
TYPE : unit -> string
PRE : (inget)
POST : HTML-kod för en sida som talar om att användaren har skrivit
in en felaktig kombination av användarnamn och lösenord.
*)
fun makeWrongUserPassPage () =
makeErrorPage("Du skrev in ett felaktikt användarnamn eller lösenord.<br>\n"
^"Klicka <a href=\"" ^ valOf cgi_script_name ^ "\">här</a>" ^
" för att försöka igen eller " ^
"<a href=\"" ^ valOf cgi_script_name ^ "?a=register\">här</a>"
^ " om du vill registrera en ny användare.");
local
(* dbToList is
TYPE : instream -> string list list
PRE : (inget)
POST : Varje kommaseprarerad sträng i is på varje rad blir ett
element i den inre listan.
SIDE-EFFECTS : Alla tecken i is läses in. is stängs.
*)
(* VARIANT : Antal tecken i is *)
fun dbToList is =
let
(* readLine is
TYPE : instream -> string option
PRE : (inget)
POST : SOME(Första raden i is) utan "\n" ifall det finns
minst 1 tecken i is annars NONE.
SIDE-EFFECTS : Alla tecken fram tom."\n" eller EOF läses in från is.
Läspekaren i is flyttas fram lika många tecken som
lästs in ifrån is.
*)
fun readLine is = if TextIO.endOfStream is then
NONE
else
let
val s = TextIO.inputLine is
in
SOME(String.substring(s, 0,
String.size s - 1))
end;
val row = readLine is
in
if Option.isSome row then
String.fields (fn c => c = #",") (valOf row) :: dbToList is
else
(TextIO.closeIn is; [])
end
in
(* sessionsToList ()
TYPE : unit -> session list
PRE : Filen specifierad av sessionfile är läsbara.
POST : Alla sessioner i databasen sessionfile
SIDE-EFFECTS : Filen specifierad av sessionfile öppnas.
*)
fun sessionsToList () =
let
(* sessionsToList' l
TYPE : string list list -> session list
PRE : Varje element i l innehåller 3 element.
Det första elementet skall motsvara det första fältet
i sessionfile, det andra det andra fältet och det
trejde det tredje fältet.
POST : l som en lista av sessioner.
*)
fun sessionsToList' [] = []
| sessionsToList' (head::tail) = {id = List.nth(head, 2),
user = List.nth(head, 1),
expire = valOf(Date.fromString(
List.nth(head, 0)))} ::
sessionsToList' tail
in
sessionsToList'(dbToList(TextIO.openIn sessionfile))
end
(* usersToList ()
TYPE : unit -> user list
PRE : Filen specifierad av userfile är läsbar.
POST : Alla användare i databasen userfile.
SIDE-EFFECTS : Filen specifierad av userfile öppnas.
*)
fun usersToList () =
let
(* usersToList' l
TYPE : string list list -> user list
PRE : Varje element i l innehåller 2 element.
Det första elementet skall motsvara det första fältet
i userfile och det andra det andra fältet.
POST : l som en lista av användare
*)
fun usersToList' [] = []
| usersToList' (head::tail) = {name = List.nth(head, 0),
password = List.nth(head, 1)} ::
usersToList' tail
in
usersToList'(dbToList(TextIO.openIn userfile))
end
(* postsToList ()
TYPE : unit -> post list
PRE : Filen specifiead av postfile är läsbar.
POST : Alla poster i databasen postfile.
SIDE-EFFECTS : Filen specifierad av postfile öppnas.
*)
fun postsToList () =
let
(* postsToList' l
TYPE : string list list -> post list
PRE : Varje element i l innehåller 4 element.
Det första elementet skall motsvara det första fältet
i postsfile, det andra det andra fältet, det tredje
det treje fältet och det fjärde det fjärde fältet.
POST : l som en lista av poster
*)
fun postsToList' [] = []
| postsToList' (head::tail) = {user = List.nth(head, 1),
time = valOf(Date.fromString(
List.nth(head, 0))),
ip = List.nth(head, 2),
message = List.nth(head, 3)} ::
postsToList' tail
in
postsToList'(dbToList(TextIO.openIn postfile))
end
end;
(* writeSessions sessionList
TYPE : session list -> unit
PRE : Filen specifierad av sessionfile är skrivbar.
POST : (inget)
SIDE-EFFECTS : Filen specifierad av sessionfile öppnas, skrivs till och
stängs.
*)
fun writeSessions sessionList =
let
val os = TextIO.openOut sessionfile
in
((List.app (fn ({user : string, expire : Date.date, id : string}) =>
TextIO.output(os, Date.toString expire ^ "," ^
user ^ "," ^
id ^ "\n")) sessionList);
TextIO.flushOut os; TextIO.closeOut os)
end;
(* writeUsers userList
TYPE : user list -> unit
PRE : Filen specifierad av userfile är skrivbar.
POST : (inget)
SIDE-EFFECTS : Filen specifierad av userfile öppnas, skrivs till och stängs.
*)
fun writeUsers userList =
let
val os = TextIO.openOut userfile
in
((List.app (fn ({name : string, password : string}) =>
TextIO.output(os, name ^ "," ^ password ^ "\n"))
userList);
TextIO.flushOut os; TextIO.closeOut os)
end;
(* writePosts postList
TYPE : post list -> unit
PRE : Filen specifierad av postfile är skrivbar
POST : (inget)
SIDE-EFFECTS : Filen specifierad av postfile öppnas, skrivs till och stängs.
*)
fun writePosts postList =
let
val os = TextIO.openOut postfile
in
((List.app (fn ({user : string,
time : Date.date,
ip : string,
message : string}) =>
TextIO.output(os, Date.toString time ^ "," ^
user ^ "," ^
ip ^ "," ^
message ^ "\n")) postList);
TextIO.flushOut os; TextIO.closeOut os)
end;
(* getCookieUserValue ()
TYPE : unit -> string option
PRE : (inget)
POST : SOME("användarnamn") om det finns en cookie satt med namnet
"user" annars NONE
*)
fun getCookieUserValue () = getCookieValue("user");
(* getCookieSessionValue ()
TYPE : unit -> string option
PRE : (unit)
POST : SOME("sessionid") om det finns en cookie satt med namnet
"sessionid" annars NONE
*)
fun getCookieSessionValue () = getCookieValue("sessionid");
(* makeChatPage ()
TYPE : unit -> string
PRE : (inget)
POST : HTML-kod för att visa webchatten
*)
fun makeChatPage () =
let
val user = valOf(getCookieUserValue());
(* makeBody l
TYPE : string list list -> string
PRE : l är en lista som beskriver en post-databas
POST : En HTML-formatterad text som innehåller alla poster
i webchatten.
*)
(* VARIANT : Antal element i l *)
fun makeBody [] = ""
| makeBody ({ip : string, message : string, time : Date.date,
user : string} :: tail) =
"\n<h2>Postat av " ^ user ^ " (" ^ ip ^ ") " ^
Date.toString time ^ "</h2>\n<p>" ^ message ^ "\n</p>\n" ^
makeBody tail
in
let
val body = "<div id=\"posts\">" ^
makeBody(postsToList()) ^
"</div>"
val input = makeForm("postForm",
makeInput(TEXTAREA, "post",
SOME("Skriv ditt meddelande här"))
^ "<br />\n" ^ makeInput(SUBMIT, "Skicka",
SOME("Skicka"))
^ makeInput(HIDDEN, "a", SOME("post")),
valOf cgi_script_name ^ "?a=chat")
in
if body = "" then
"<hr />" ^ "Det finns inga poster." ^ "<hr />" ^ input
else
"<hr />" ^ body ^ "<hr />" ^ input
end
end;
(* cleanOldSessions ()
TYPE : unit -> unit
PRE : (inget)
POST : (inget)
*)
fun cleanOldSessions () =
let
(* validSessions sessionList
TYPE : session list -> session list
PRE : (inget)
POST : En lista med alla giltiga sessioner.
(För betydelsen av vad en giltig session är se
dokumentationen om sessionfile)
*)
(* VARIANT : Antal element i sessionList *)
fun validSessions [] = []
| validSessions ({user : string, expire : Date.date, id : string}
::tail) =
if Date.compare(expire, Date.fromTimeLocal(Time.now())) = LESS then
validSessions tail
else
{user = user , expire = expire, id = id} :: validSessions tail
in
writeSessions(validSessions(sessionsToList()))
end;
(* getSessionID user
TYPE : string -> string option
PRE : (inget)
POST : Om användaren user har en session i databasen returneras
SOME("sid") där "sid" är det sessionsid user har.
*)
fun getSessionID username =
let
fun getSessionID' [] = NONE
| getSessionID' ({user : string, id : string, expire : Date.date}
::tail) =
if username = user then
SOME(id)
else
getSessionID' tail
in
getSessionID'(sessionsToList())
end;
(* deleteSession username
TYPE : string -> unit
PRE : (inget)
POST : (inget)
SIDE-EFFECTS : Om användaren username finns i sessionfile tas denna rad bort
ur sessionfile.
*)
fun deleteSession username =
writeSessions(List.filter (fn ({user : string, expire : Date.date,
id : string}) => not(user = username))
(sessionsToList()))
(* updateSession username
TYPE : string -> unit
PRE : (inget)
POST : (inget)
SIDE-EFFECTS : Om användaren username finns i sessionfile uppdateras
expire-fältet i sessionfile till nu + en timme.
*)
fun updateSession username =
let
(* update sessionList
TYPE : session list -> session list
PRE : (inget)
POST : Varje element i sessionList med expire-fältet
uppdaterat till nu + en timme.
*)
fun update [] = []
| update ({user : string, expire : Date.date, id : string}::tail) =
if user = username then
{user = user,
expire = Date.fromTimeLocal(Time.+(Time.now(),
Time.fromSeconds
sessionLength)),
id = id} :: update tail
else
{user = user, expire = expire, id = id} :: update tail
in
writeSessions(update(sessionsToList()))
end
(* makeSession user
TYPE : string -> string option
PRE : Filen sessionfile finns och är läs- och skrivbar.
POST : SOME(En cookie som kan användas för att verifiera att
användaren user är inloggad) ifall det inte redan
existerar en session för användaren user, annars NONE.
SIDE-EFFECTS : En session för användaren user läggs till i filen
specifierad av userfile.
*)
fun makeSession user =
let
(* randomString n
TYPE : int -> string
PRE : (inget)
POST : n antal tecken som är slumpade i intervallen a-z,
A-Z och 0-9
*)
fun randomString n =
let
val gen = Random.newgen()
fun makeRandom 0 = ""
| makeRandom n = makeRandom(n - 1) ^
str(Char.chr(Random.range(List.nth(
[(48,57),
(65,90),
(97,122)],
Random.range(0, 3)
gen))
gen))
in
makeRandom n
end
val sid = (cleanOldSessions(); getSessionID user)
in
if Option.isSome sid andalso sid = getCookieSessionValue() then
(updateSession user; NONE)
else
let
val sessionid = randomString 16
val now = Time.now()
val ts = Time.fmt 0 now
val sessioncookie = setCookie{name = "sessionid",
value = sessionid,
expiry = NONE,
domain = NONE,
path = NONE,
secure = false}
val usercookie = setCookie{name = "user",
value = user,
expiry = NONE,
domain = NONE,
path = NONE,
secure = false}
val expire = Date.fromTimeLocal(Time.+(now,
Time.fromSeconds 3600))
in
(writeSessions({user = user, expire = expire, id = sessionid}
:: sessionsToList());
SOME(usercookie ^ "\n" ^ sessioncookie))
end
end;
(* checkSession (username, sessionid)
TYPE : string * string -> bool
PRE : (inget)
POST : true om användaren username som har en session i sessionfile
som har id sessionid annars false
SIDE-EFFECTS : Inaktuella sessioner rensas bort ur sessionfile.
*)
fun checkSession (username, sessionid) =
let
(* checkSession' sessionList
TYPE : session list -> bool
PRE : (inget)
POST : true om användaren username har en giltig session i
sessionList som har id sessionsid annars false.
*)
fun checkSession' [] = false
| checkSession' ({user : string, expire : Date.date, id : string}
::tail) = if user = username andalso
sessionid = id then
true
else
checkSession' tail
in
(cleanOldSessions(); checkSession'(sessionsToList()))
end;
(* checkAuth (user, pass)
TYPE : string * string -> bool
PRE : (inget)
POST : true om användaren user har lösenordet pass i userfile annars
false.
*)
fun checkAuth (user, pass) =
Option.isSome(List.find (fn ({name : string, password : string}) =>
user = name andalso pass = password)
(usersToList()))
(* doRegistration (user, pass)
TYPE : string * string -> string option
PRE : user innehåller >= 3 tecken
pass innehåller >= 6 tecken
user och pass innehåller bara tecknen a-z, A-Z och 0-9
POST : NONE ifall registreringen lyckades.
SOME("Användarnamnet är för kort") ifall användarnamnet
user < 3 tecken.
SOME("Lösenordet för kort") ifall pass < 6 tecken.
SOME("Användarnamnet och lösenordet får bara innehålla
a-z, A-Z och 0-9") ifall user eller pass innehåller
något felaktigt tecken.
SIDE-EFFECTS : Om POST är NONE läggs användaren user till i filen
specifierad av userfile med lösenordet pass.
*)
fun doRegistration (user, pass) =
let
(* isAlNum s
TYPE : string -> bool
PRE : (inget)
POST : true om s bara innehåller tecknen a-z, A-Z eller 0-9
*)
fun isAlNum "" = true
| isAlNum s = if Char.isAlphaNum(String.sub(s, String.size s - 1))
then
isAlNum(String.substring(s, 0, String.size s - 1))
else
false
val currentUsers = usersToList()
in
if (String.size user) < 3 then
SOME("Användarnamnet är för kort")
else if (String.size pass) < 6 then
SOME("Lösenordet är för kort")
else if not(isAlNum user) orelse not(isAlNum pass) then
SOME("Användarnamnet och lösenordet får bara innehålla " ^
"a-z, A-Z och 0-9")
else if Option.isSome(List.find (fn ({name : string,
password : string}) =>
name = user)
currentUsers) then
SOME("Användarnamnet " ^ user ^ " är redan upptaget")
else
(writeUsers({name = user, password = pass} :: currentUsers); NONE)
end;
(* doPost (user, ip, message)
TYPE : string * string -> unit
PRE : user är en giltig användare, ip är IP-numret användaren user
har.
POST : (inget)
SIDE-EFFECTS : En post av användaren user, ip-numret IP och med meddelandet
message, läggs till i filen specifierad av postfile.
Om det finns fler poster i filen specifierad av postfile än
maxfiles trunkeras dom sista posterna tills det finns exakt
maxfiles poster kvar.
*)
fun doPost (user, ip, message) =
let
(* escapeString s
TYPE : string -> string
PRE : (inget)
POST : s med alla förekomster av:
"<" utbytt mot "&lt;"
">" utbytt mot "&gt;"
"&" utbytt mot "&amp;"
"\"" utbytt mot "&quot;"
"'" utbytt mot "&#39;"
"å" utbytt mot "&aring;"
"Å" utbytt mot "&Aring;"
"ä" utbytt mot "&auml;"
"Ä" utbytt mot "&Auml;"
"ö" utbytt mot "&ouml;"
"Ö" utbytt mot "&Ouml;"
"\n" utbytt mot "<br />"
"," utbytt mot "&#44;"
*)
(* VARIANT : Antal tecken i s *)
fun escapeString "" = ""
| escapeString s =
(case String.sub(s, 0) of
#"<" => "&lt;"
| #">" => "&gt;"
| #"&" => "&amp;"
| #"\"" => "&quot;"
| #"'" => "&#39;"
| #"å" => "&aring;"
| #"Å" => "&Aring;"
| #"ä" => "&auml;"
| #"Ä" => "&Auml;"
| #"ö" => "&ouml;"
| #"Ö" => "&Ouml;"
| #"\n" => "<br />" (* Bevara datastrukturen i postfile *)
| #"," => "&#44;" (* Samma som ovanstående *)
| _ => String.substring(s, 0, 1)) ^
escapeString(String.substring(s, 1, String.size s - 1))
(* nthFirst (n, l)
TYPE : int * 'a list -> 'a list
PRE : n <= 0
POST : Dom n första elementen i l. Om n <= än antal element
i l returneras hela l.
*)
(* VARIANT : n och antal element i l *)
fun nthFirst (0, _) = []
| nthFirst (_, []) = []
| nthFirst (n, head::tail) = head :: nthFirst(n - 1, tail)
in
writePosts({user = user, ip = ip,
time = Date.fromTimeLocal(Time.now()),
message = escapeString message} ::
nthFirst(maxposts, postsToList()))
end;
(* main ()
TYPE : unit -> unit
PRE : (inget)
POST : (inget)
SIDE-EFFECTS : Visar en lämplig HTML-sida beroende på användarens input
*)
fun main () =
let
val action = cgi_field_string("a");
in
if Option.isSome action then
case valOf action of
"register" => let
val username = cgi_field_string("username")
val password = cgi_field_string("password")
in
if Option.isSome username andalso
Option.isSome password then
let
val status = doRegistration(
valOf username,
valOf password)
in
if Option.isSome status then
printPage(
NONE,
"WebChat -> Error",
"Ett fel har uppstått",
makeErrorPage(valOf status))
else
printPage(
NONE,
"WebChat -> Registrerad",
"Registreringen lyckades",
makeRegisterdPage())
end
else
printPage(NONE,
"WebChat -> Registrering",
"Registrering",
makeRegistrationPage())
end
| "login" => let
val username = cgi_field_string("username")
val password = cgi_field_string("password")
in
if Option.isSome username andalso
Option.isSome password then
let
val user = valOf username
val pass = valOf password
in
if checkAuth(user, pass) then
printPage(makeSession(user),
"WebChat -> Chat",
"Välkommen " ^ user,
makeChatPage())
else
printPage(
NONE,
"WebChat -> Inloggningen " ^
"misslyckades",
"Felaktigt användarnamn och/eller"
^ "lösenord",
makeWrongUserPassPage())
end
else
printPage(
NONE,
"WebChat -> Inloggningen misslyckades",
"Felaktigt användarnamn och/eller " ^
"lösenord",
makeWrongUserPassPage())
end
| "chat" => let
val user = getCookieUserValue()
val sessionid = getCookieSessionValue()
in
if Option.isSome user andalso
Option.isSome sessionid then
let
val usr = valOf user
in
if checkSession(usr, valOf sessionid)
then
printPage(NONE,
"WebChat -> Chat",
"Välkommen " ^ usr,
makeChatPage())
else
printPage(
NONE,
"WebChat -> Error",
"Ett fel har uppstått",
makeErrorPage("Du är inte " ^
"inloggad."))
end
else
printPage(
NONE,
"WebChat -> Error",
"Ett fel har uppstått",
makeErrorPage("Din session är inte" ^
"giltigt, var god logga " ^
"in på nytt"))
end
| "post" => let
val user = getCookieUserValue()
val sessionid = getCookieSessionValue()
val message = cgi_field_string("post")
in
if Option.isSome user andalso
Option.isSome sessionid andalso
checkSession(valOf user, valOf sessionid)
then
if Option.isSome message then
let
val msg = valOf message
val usr = valOf user
in
if String.size msg >= 2 then
(doPost(
usr,
valOf cgi_remote_addr,
msg);
printPage(NONE,
"WebChat -> Chat",
"Välkommen " ^ usr,
makeChatPage()))
else
printPage(
NONE,
"WebChat -> Error",
"Ett fel har uppstått",
makeErrorPage(
"Ditt meddelande är " ^
"för kort. " ^
"Gå tillbaka och " ^
"försök igen."))
end
else
printPage(
NONE,
"WebChat -> Error",
"Ett fel har uppstått",
makeErrorPage("Felaktigt " ^
"meddelande. " ^
"Gå tillbaka, " ^
"kontrollera och " ^
"försök igen."))
else
printPage(
NONE,
"WebChat -> Error",
"Ett fel har uppstått",
makeErrorPage("Din session är inte" ^
"giltig, var god logga" ^
"in på nytt"))
end
| _ => printPage(NONE,
"WebChat -> Inloggning",
"Logga in för att använda webchatten",
makeFrontPage())
else
printPage(NONE,
"WebChat -> Inloggning",
"Logga in för att använda webchatten",
makeFrontPage())
end;
val _ = main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment