%% The client database -module(cldb). -export([init/0, all/0, find/1, login/2, dec_vote/1, inc_vote/1, register/3, check_rights/1, update_votes/0, logout/1, set_client_pid/2, get_votes/1]). -record(user, {name, passwd, votes, logged_in, pid, rights}). %% Initialisation of the mnesia database init() -> mnesia:create_schema([node()]), mnesia:start(), mnesia:create_table(user, [{attributes, record_info(fields, user)}]), io:format("Userdb up and running \n"). %% The basic search operations on the database find(User) when is_list(User) -> find(User, '_'); find(User) -> find(User#user.name, '_'). find(User, Pwd) -> F = fun() -> mnesia:match_object({user, User, Pwd, '_', '_', '_', '_'}) end, mnesia:transaction(F). find_logged_in_user() -> F = fun() -> mnesia:match_object({user, '_', '_', '_', true, '_', '_'}) end, case mnesia:transaction(F) of {atomic, List} -> {ok, List}; _ -> {error} end. find_user_by_pid(Pid) -> F = fun() -> mnesia:match_object({user, '_', '_', '_', '_', Pid, '_'}) end, case mnesia:transaction(F) of {atomic, List} -> {ok, List}; _ -> {error} end. %% Retrieve all entries all() -> F = fun() -> mnesia:match_object({user, '_', '_', '_', '_', '_', '_'}) end, case mnesia:transaction(F) of {atomic, List} -> {ok, List}; _ -> {error} end. %% Perform the login operation of a user into the database. login(User, Pwd) when is_list(User) and is_list(Pwd) -> case find(User, Pwd) of {atomic, [UserRow|_]} -> %% check if user is not already logged in case UserRow#user.logged_in of false -> NewUserRow = UserRow#user{logged_in = true}, F = fun() -> mnesia:write(NewUserRow) end, case mnesia:transaction(F) of {atomic, ok} -> {ok, NewUserRow}; {atomic, Why} -> {error, Why} end; _ -> {error, already_logged_in} end; _-> {error} end; login(_, _) -> {error}. %% In order to log out the client after its process has died. set_client_pid(User, Pid) -> case find(User) of {atomic, [UserRow|_]} -> NewUserRow = UserRow#user{pid = Pid}, F = fun() -> mnesia:write(NewUserRow) end, case mnesia:transaction(F) of {atomic, ok} -> {ok, pid_updated}; {atomic, Why} -> {error, Why} end; _ -> {error, user_not_found} end. %% Change the database to log out a user by PID logout(Pid) -> case find_user_by_pid(Pid) of {ok, [UserRow|_]} -> F = fun() -> New = UserRow#user{logged_in = false}, mnesia:write(New) end, case mnesia:transaction(F) of {atomic, ok} -> {ok, logged_out}; {atomic, Why} -> {error, Why} end; _ -> {error, invalid_user} end. %% Adds an entry into the database for this user. %% If he is already registered, this will return an error. %% In order to vote, users arer required to log in after registering. register(User, Pwd, Root) when is_list(User) and is_list(Pwd) -> case find(User) of {atomic, []} -> F = fun() -> mnesia:write(#user{name = User, passwd = Pwd, votes = 5, logged_in = false, pid = undef, rights = Root}) end, case mnesia:transaction(F) of {atomic, ok} -> io:format("User created: ~s~n", [User]), {ok, user_created}; {atomic, Why} -> {error, Why} end; _ -> {error, duplicated_user} end; register(_,_,_) -> {error, invalid_username}. %% functions to de- and increment the amount of votes a user has left. dec_vote(User) when is_list(User) -> {atomic, [Head|_]} = find(User), if Head#user.votes > 0 -> F = fun() -> Votes = Head#user.votes - 1, New = Head#user{votes = Votes}, mnesia:write(New) end, mnesia:transaction(F), {ok}; true -> {error} end; dec_vote(User) -> dec_vote(User#user.name). inc_vote(User) -> F = fun() -> {atomic, [Head|_]} = find(User), Votes = Head#user.votes + 1, New = Head#user{votes = Votes}, mnesia:write(New) end, mnesia:transaction(F). %% Return rights of a user (admin or nan). check_rights(User) -> {atomic, [UserRow|_]} = find(User), UserRow#user.rights. update_votes() -> %% after a song every logged in client gets one more vote to spend case find_logged_in_user() of {ok, List} -> give_votes(List); _ -> error end. %% Increment the votes of every user. %% This is called when a song has ended. give_votes([User|Rest]) -> inc_vote(User#user.name), give_votes(Rest); give_votes([]) -> ok. %% Returns the amount of votes a specific user has. get_votes(User) -> case find(User) of {atomic, [UserRow|_]} -> {ok, UserRow#user.votes}; _ -> {error, user_not_found} end.