'Users module file
#INCLUDE "modules.bi"
CONST AS STRING ModuleName = "users"
DIM SHARED ModulePath AS STRING
ModulePath = "modules" + PATH_DELIMITER + ModuleName + PATH_DELIMITER
'ArrayIndex = UBOUND(CompiledModules) + 1
'REDIM PRESERVE CompiledModules(ArrayIndex)
'CompiledModules(ArrayIndex) = "users"
DIM SHARED AS STRING SessionContent, UserData
CONST AS STRING SessionName = "iguana-token"
'Load the session content into a variable
SessionContent = ReadFile(TempPath + "sessions" + PATH_DELIMITER + SHA256(GetCookie(SessionName)) + ".txt")
FUNCTION ReadSession(Key AS STRING) AS STRING
DIM Result AS STRING
IF SessionContent <> "" THEN
Result = SplitVar(SessionContent, Key, LINE_ENDING)
END IF
IF Result = "" THEN
Result = Key
END IF
ReadSession = Result
'Clean memory
Result = ""
END FUNCTION
'Load the user file content into a variable
UserData = ReadFile(DataPath + "users" + PATH_DELIMITER + ReadSession("session_userfile"))
FUNCTION IsAuth() AS SHORT
IF SHA256(GetCookie(SessionName)) = ReadSession("session_token") THEN
IsAuth = -1
ELSE
IsAuth = 0
END IF
END FUNCTION
SUB LoadUsersInterface()
DIM AS STRING Action, FileContent, TempContent, SessionFile, Result
DIM AS STRING StoredPassword, Salt, NewSalt, UserAlias, UserEmail, UserToken, UserFile
DIM AS SHORT SaltLen, TokenLen
IF CINT(Settings("fancy_url")) THEN
IF UriPart(1) <> "" THEN
Action = UriPart(1)
END IF
ELSE
IF QueryString("action") <> "" THEN
Action = QueryString("action")
END IF
END IF
IF Action <> "" THEN
SELECT CASE Action
CASE "login"
SiteTitle = Language("module_users_login")
Result = LoadTplFile(ModulePath, "login.html")
UserAlias = ValidateChar(Post("user-alias"))
IF UserAlias <> "" THEN
UserFile = DataPath + "users" + PATH_DELIMITER + UserAlias + ".txt"
FileContent = ReadFile(UserFile)
IF FileContent <> "" THEN
'Retrieve stored password
StoredPassword = SplitVar(FileContent, "user_password", LINE_ENDING)
'Get the password salt
Salt = LEFT(StoredPassword, INSTR(StoredPassword, "+") - 1)
IF SHA256(Salt + Post("user-password")) = MID(StoredPassword, INSTR(StoredPassword, "+") + 1) THEN
'Password match
IF CINT(SplitVar(FileContent, "user_status", LINE_ENDING)) THEN
'User is active
TokenLen = 32
'Create random session ID
DIM AS STRING SessionToken = RandomString(TokenLen)
'Create session file
TempContent = "" + _
"session_userfile=" + LCASE(UserAlias) + ".txt" + LINE_ENDING + _
"session_token=" + SHA256(SessionToken) + LINE_ENDING + _
"session_ipaddr=" + ENVIRON("REMOTE_ADDR") + LINE_ENDING + _
"session_useragent=" + ENVIRON("HTTP_USER_AGENT") + LINE_ENDING + _
"session_logintime=" + SystemDate() + "-" + SystemTime() + LINE_ENDING + _
"session_validuntil=" + "" + LINE_ENDING
WriteFile(TempPath + "sessions" + PATH_DELIMITER + SHA256(SessionToken) + ".txt", TempContent)
'Clean memory
TempContent = ""
'Create cookie
IF CINT(Post("user-remember")) THEN
PRINT SetCookie(SessionName, SessionToken, "", "/", "", 3600 * 24 * 30, 0, 1)
ELSE
PRINT SetCookie(SessionName, SessionToken, "", "/", "", 3600 * 24, 0, 1)
END IF
'Redirect to user panel
PRINT "Refresh: 10;url=" + Settings("site_url") + CreateURL("module_users")
Result = LoadTplFile(ModulePath, "logged.html")
ELSE
'The user is inactive or banned
Result = Replace(Result, "error_message", Language("error_user_not_active"))
END IF
ELSE
'Password does not match
Result = Replace(Result, "error_message", Language("error_wrong_password"))
END IF
'Clean memory
FileContent = ""
ELSE
'User file does not exists
Result = Replace(Result, "error_message", Language("error_user_not_registered"))
END IF
ELSE
IF IsAuth() THEN
'Redirect to user panel
PRINT "Refresh: 10;url=" + Settings("site_url") + CreateURL("module_users")
Result = "403"
ELSE
Result = Replace(Result, "error_message", "")
END IF
END IF
CASE "logout"
IF IsAuth() THEN
SessionFile = TempPath + "sessions" + PATH_DELIMITER + ReadSession("session_token") + ".txt"
IF FileExists(SessionFile) THEN
DeleteFile(SessionFile)
SiteTitle = Language("module_users_control_panel")
Result = LoadTplFile(ModulePath, "logout.html")
'Cookie deletion
PRINT SetCookie(SessionName, GetCookie(SessionName), "", "/", "Thu, 01 Jan 1970 00:00:01 GMT", 0, 0, 1)
PRINT "Refresh: 10;url=" + Settings("site_url")
ELSE
SiteTitle = Language("module_users_login")
Result = LoadTplFile(ModulePath, "login.html")
Result = Replace(Result, "error_message", Language("error_session_not_exists"))
END IF
ELSE
Result = "403"
END IF
CASE "register"
SiteTitle = Language("module_users_register")
Result = LoadTplFile(ModulePath, "register.html")
IF Post("user-captcha") <> "" AND Post("captcha-number") <> "" THEN
IF ValidateCaptcha(Post("user-captcha"), Post("captcha-number")) THEN
UserAlias = ValidateChar(Post("user-alias"))
UserEmail = ValidateChar(LCASE(Post("user-email")))
IF UserAlias <> "" AND UserEmail <> "" THEN
UserFile = DataPath + "users" + PATH_DELIMITER + LCASE(UserAlias) + ".txt"
'Check if the user alias is already registered
IF NOT FileExists(UserFile) THEN
SaltLen = 8
Salt = RandomString(SaltLen)
TokenLen = 16
UserToken = RandomString(TokenLen)
FileContent = "" + _
"user_alias=" + UserAlias + LINE_ENDING + _
"user_password=" + Salt + "+" + SHA256(Salt + Post("user-password")) + LINE_ENDING + _
"user_regdate=" + SystemDate() + LINE_ENDING + _
"user_email=" + UserEmail + LINE_ENDING + _
"user_token=" + UserToken + LINE_ENDING + _
"user_status=0" + LINE_ENDING + _
"user_group=0"
'"user_lastsession="
'Save user data
WriteFile(UserFile, FileContent)
'Clean memory
FileContent = ""
'Parse email template
TempContent = LoadTplFile(ModulePath, "activate-user-mail.html")
TempContent = Replace(TempContent, "user_alias", UserAlias)
TempContent = Replace(TempContent, "user_token", UserToken)
'Send activation link
'SendMail(UserEmail, UserAlias, Settings("site_email"), Settings("site_title"), SiteTitle, TempContent)
Result = LoadTplFile(ModulePath, "registered.html")
ELSE
Result = Replace(Result, "error_message", Language("error_user_already_registered"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_user_name_empty"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_captcha_wrong_answer"))
END IF
ELSE
Result = Replace(Result, "error_message", "")
END IF
CASE "change-password"
IF IsAuth() THEN
SiteTitle = Language("module_users_change_password")
Result = LoadTplFile(ModulePath, "change-password.html")
SessionFile = DataPath + "users/" + ReadSession("session_userfile")
FileContent = ReadFile(SessionFile)
IF Post("new-user-password") <> "" AND Post("current-user-password") <> "" THEN
'Form sent
'Retrieve stored password
StoredPassword = SplitVar(FileContent, "user_password", LINE_ENDING)
'This string contains the salt + hashed password
'Get the password salt
Salt = LEFT(StoredPassword, INSTR(StoredPassword, "+") - 1)
IF SHA256(Salt + Post("current-user-password")) = MID(StoredPassword, INSTR(StoredPassword, "+") + 1) THEN
'Password match, replace old password with the new one
'Generate new salt
SaltLen = 8
NewSalt = RandomString(SaltLen)
'FIXME: Verify new password not empty
FileContent = Replace(FileContent, StoredPassword, NewSalt + "+" + SHA256(NewSalt + Post("new-user-password")))
WriteFile(SessionFile, FileContent)
'Clean memory
FileContent = ""
Result = Replace(Result, "error_message", Language("success_password_changed"))
ELSE
'Password does not match
Result = Replace(Result, "error_message", Language("error_wrong_password"))
END IF
ELSE
Result = Replace(Result, "error_message", "")
END IF
ELSE
Result = "403"
END IF
CASE "reset-password"
SiteTitle = Language("module_users_reset_password")
Result = LoadTplFile(ModulePath, "reset-password.html")
IF QueryString("username") <> "" THEN
'Resetting password
UserAlias = ValidateChar(LCASE(QueryString("username")))
IF UserAlias <> "" THEN
UserFile = DataPath + "users" + PATH_DELIMITER + UserAlias + ".txt"
FileContent = ReadFile(UserFile)
IF FileContent <> "" THEN
'Check the received token against the stored token
IF ValidateChar(LCASE(QueryString("token"))) = SplitVar(FileContent, "user_token", LINE_ENDING) THEN
IF Post("new-user-password") <> "" THEN
'Generate new salt
SaltLen = 8
NewSalt = RandomString(SaltLen)
'FIXME: Verify new password not empty
FileContent = Replace(FileContent, SplitVar(FileContent, "user_password", LINE_ENDING), NewSalt + "+" + SHA256(NewSalt + Post("new-user-password")))
WriteFile(UserFile, FileContent)
'Clean memory
FileContent = ""
Result = LoadTplFile(ModulePath, "reset-password-changed.html")
ELSE
'Show password change form
Result = LoadTplFile(ModulePath, "reset-password-change-form.html")
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_wrong_user_token"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_user_not_registered"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_user_name_empty"))
END IF
ELSE
IF Post("user-captcha") <> "" AND Post("captcha-number") <> "" THEN
IF ValidateCaptcha(Post("user-captcha"), Post("captcha-number")) THEN
UserAlias = ValidateChar(LCASE(Post("user-alias")))
IF UserAlias <> "" THEN
UserFile = DataPath + "users" + PATH_DELIMITER + UserAlias + ".txt"
FileContent = ReadFile(UserFile)
IF FileContent <> "" THEN
'Generate new token
TokenLen = 16
UserToken = RandomString(TokenLen)
'Get user email and alias from file
UserEmail = SplitVar(FileContent, "user_email", LINE_ENDING)
UserAlias = SplitVar(FileContent, "user_alias", LINE_ENDING)
'Update token in user file
FileContent = Replace(FileContent, SplitVar(FileContent, "user_token", LINE_ENDING), UserToken)
WriteFile(UserFile, FileContent)
'Clean memory
FileContent = ""
'Parse email template
TempContent = LoadTplFile(ModulePath, "reset-password-mail.html")
TempContent = Replace(TempContent, "user_alias", UserAlias)
TempContent = Replace(TempContent, "user_token", UserToken)
'Send new activation link
'SendMail(UserEmail, UserAlias, Settings("site_email"), Settings("site_title"), SiteTitle, TempContent)
'Hide user email
DIM AS STRING HiddenEmail, char
DIM AS SHORT i
FOR i = 1 TO LEN(UserEmail)
char = MID(UserEmail, i, 1)
SELECT CASE i
CASE 4 TO LEN(UserEmail) - 8
IF NOT char = "@" THEN
char = "*"
END IF
HiddenEmail = HiddenEmail + char
CASE ELSE
HiddenEmail = HiddenEmail + char
END SELECT
NEXT i
Result = LoadTplFile(ModulePath, "reset-password-request-sent.html")
Result = Replace(Result, "hidden_email", HiddenEmail)
ELSE
Result = Replace(Result, "error_message", Language("error_user_not_registered"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_user_name_empty"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_captcha_wrong_answer"))
END IF
ELSE
Result = Replace(Result, "error_message", "")
END IF
END IF
CASE "activate"
SiteTitle = Language("module_users_login")
Result = LoadTplFile(ModulePath, "login.html")
UserAlias = ValidateChar(LCASE(QueryString("username")))
UserFile = DataPath + "users" + PATH_DELIMITER + UserAlias + ".txt"
FileContent = ReadFile(UserFile)
IF FileContent <> "" THEN
'Check received token against stored token
IF ValidateChar(LCASE(QueryString("token"))) = SplitVar(FileContent, "user_token", LINE_ENDING) THEN
'The user must not be active
IF SplitVar(FileContent, "user_status", LINE_ENDING) = "0" THEN
FileContent = Replace(FileContent, "user_status=0", "user_status=-1")
WriteFile(UserFile, FileContent)
'Clean memory
FileContent = ""
Result = Replace(Result, "error_message", Language("success_user_activated"))
ELSE
Result = Replace(Result, "error_message", Language("error_user_already_active"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_wrong_user_token"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_user_not_registered"))
END IF
END SELECT
ELSE
IF GetCookie(SessionName) <> "" THEN
SiteTitle = Language("module_users_login")
Result = LoadTplFile(ModulePath, "login.html")
IF IsAuth() THEN
IF UserData <> "" THEN
IF CINT(SplitVar(UserData, "user_status", LINE_ENDING)) THEN
SiteTitle = Language("module_users_control_panel")
Result = LoadTplFile(ModulePath, "user.html")
ELSE
Result = Replace(Result, "error_message", Language("error_user_not_active"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_user_not_registered"))
END IF
ELSE
Result = Replace(Result, "error_message", Language("error_session_not_exists"))
END IF
ELSE
SiteTitle = Language("module_users_login")
Result = LoadTplFile(ModulePath, "login.html")
Result = Replace(Result, "error_message", "")
END IF
END IF
SiteContent = Result
'Clean memory
Result = ""
END SUB
FUNCTION ReadUserData(Key AS STRING) AS STRING
DIM AS STRING Query, Value
IF Key <> "" THEN
Query = MID(Key, 1, INSTR(Key, "_") - 1)
Value = MID(Key, INSTR(Key, "_") + 1)
SELECT CASE Query
CASE "session"
ReadUserData = ReadSession("session_" + Value)
CASE "user"
ReadUserData = SplitVar(UserData, "user_" + Value, LINE_ENDING)
CASE ELSE
ReadUserData = Key
END SELECT
END IF
END FUNCTION