% coding: utf-8 % Ce module gére les différents message envoyé par le client (javascript) via AJAX. % Par exemple le client peut demander les derniers messages du minichat. % Les messages sont au format XML, la plus part des fonctions accepte un xmlDocument() et renvoie un string() % qui est la réponse XML. % Example XML : http://www.erlang.org/doc/apps/xmerl/xmerl_ug.html. % @author G.Burri -module(euphorik_protocole). -export([ nouveau_user_login/1, login/1, logout/1, profile/1, refreshMessage/1, message/1 ]). -include_lib("xmerl/include/xmerl.hrl"). -include("../include/euphorik_bd.hrl"). -include("../include/euphorik_defines.hrl"). % Une utilisateur s'enregistre avec un tuple {Login, Password}. % @spec nouveau_user_login(xmerl:xmlElement()) -> string() nouveau_user_login(Action) -> {Login, Password, Login_deja_pris} = case {xmerl_xpath:string("login", Action), xmerl_xpath:string("password", Action)} of {[#xmlElement{content = [#xmlText{value = L}]}], [#xmlElement{content = [#xmlText{value = P}]}]} -> {L, P, case euphorik_minichat:user_by_login(L) of {ok, _} -> true; _ -> false end}; _ -> {[], [], false} end, simple_xml_to_string( if Login_deja_pris-> xml_reponse_login_pas_ok("Login déjà pris"); true -> Cookie = generer_cookie(), User = euphorik_minichat:nouveau_user(Login, Password, Cookie), xml_reponse_login_ok(User) end ). % Un utilisateur se logge. login(Action) -> case xmerl_xpath:string("cookie", Action) of [#xmlElement{content = [#xmlText{value = Cookie}]}] -> loginUser(euphorik_minichat:user_by_cookie(Cookie)); _ -> case {xmerl_xpath:string("login", Action), xmerl_xpath:string("password", Action)} of {[#xmlElement{content = [#xmlText{value = Login}]}], [#xmlElement{content = [#xmlText{value = Password}]}]} -> loginUser(euphorik_minichat:user_by_login_password(Login, Password)); _ -> simple_xml_to_string(xml_reponse_login_pas_ok("XML malformé")) end end. loginUser({ok, User}) -> euphorik_minichat:update_date_derniere_connexion(User#user.id), simple_xml_to_string(xml_reponse_login_ok(User)); loginUser(_) -> simple_xml_to_string(xml_reponse_login_pas_ok("Erreur de login")). % Renvoie un string() représentant un cookie en base 36. Il y a 10^32 possibillités. generer_cookie() -> {A1,A2,A3} = now(), random:seed(A1, A2, A3), erlang:integer_to_list(random:uniform(math:pow(10, 32)), 36). % Un utilisateur se délogge. logout(_) -> do_nothing. % Modification du profile. profile(Action) -> simple_xml_to_string( case xmerl_xpath:string("cookie", Action) of [#xmlElement{content = [#xmlText{value = Cookie}]}] -> Login = case xmerl_xpath:string("login", Action) of [#xmlElement{content = [#xmlText{value = L}]}] -> L; _ -> undefined end, Password = case xmerl_xpath:string("password", Action) of [#xmlElement{content = [#xmlText{value = P}]}] -> P; _ -> undefined end, Pseudo = case xmerl_xpath:string("pseudo", Action) of [#xmlElement{content = [#xmlText{value = P2}]}] -> P2; _ -> Login end, Email = case xmerl_xpath:string("email", Action) of [#xmlElement{content = [#xmlText{value = E}]}] -> E; _ -> undefined end, Css = case xmerl_xpath:string("css", Action) of [#xmlElement{content = [#xmlText{value = C}]}] -> C; _ -> undefined end, Page_principale = case xmerl_xpath:string("pagePrincipale", Action) of [#xmlElement{content = [#xmlText{value = P3}]}] -> list_to_integer(P3); _ -> undefined end, Conversations = lists:map( fun(Conv) -> [#xmlElement{content = [#xmlText{value = Id_racine_str}]}] = xmerl_xpath:string("racine", Conv), [#xmlElement{content = [#xmlText{value = Page_conv_str}]}] = xmerl_xpath:string("page", Conv), {erlang:list_to_integer(Id_racine_str, 36), list_to_integer(Page_conv_str)} end, xmerl_xpath:string("conversation", Action) ), case euphorik_minichat:set_profile(Cookie, Login, Password, Pseudo, Email, Css, Page_principale, Conversations) of ok -> xml_reponse_profile_ok(); login_deja_pris -> xml_reponse_profile_pas_ok("Login déjà pris"); _ -> xml_reponse_profile_pas_ok("Impossible de mettre à jour le profile") end; _ -> xml_reponse_profile_pas_ok("XML malformé") end ). % Renvoie les messages appropriés. refreshMessage(Action) -> simple_xml_to_string( case xmerl_xpath:string("nombreMessage", Action) of % le nombre de message qu'affiche le client [#xmlElement{content = [#xmlText{value = Nb_message_str}]}] -> Nb_message = list_to_integer(Nb_message_str), Dernier_id = case xmerl_xpath:string("dernierMessageId", Action) of % l'id du dernier message que connait le client [#xmlElement{content = [#xmlText{value = D}]}] -> erlang:list_to_integer(D, 36); _ -> 0 end, User = case xmerl_xpath:string("cookie", Action) of [#xmlElement{content = [#xmlText{value = Cookie}]}] -> case euphorik_minichat:user_by_cookie(Cookie) of {ok, U} -> U; _ -> inconnu end; _ -> inconnu end, % extraction des conversations en [{id, page}, ..] % Obsolète : obtenu depuis la table 'user' %~ Conversations = lists:map( %~ fun(Conv) -> %~ [#xmlElement{content = [#xmlText{value = Id_racine_str}]}] = xmerl_xpath:string("racine", Conv), %~ [#xmlElement{content = [#xmlText{value = Page_conv_str}]}] = xmerl_xpath:string("page", Conv), %~ {erlang:list_to_integer(Id_racine_str, 36), erlang:list_to_integer(Page_conv_str)} %~ end, %~ xmerl_xpath:string("conversation", Action) %~ ), % accrochez-vous ca va siouxer ;) [{reponse, [{name, "refreshMessages"}], lists:map( fun({Conv, Plus}) -> {conversation, [], [{autresPages, [], [atom_to_list(Plus)]} | lists:map( fun({Mess, Repond_a}) -> Est_proprietaire = User =/= inconnu andalso User#user.id =:= Mess#minichat.auteur_id, A_repondu_a_message = User =/= inconnu andalso euphorik_minichat:a_repondu_a_message(User#user.id, Mess#minichat.id), Est_une_reponse_a_user = User =/= inconnu andalso euphorik_minichat:est_une_reponse_a_user(User#user.id, Mess#minichat.id), User_mess = if Mess#minichat.auteur_id =:= 0 -> inconnu; true -> {ok, U2} = euphorik_minichat:user_by_id(Mess#minichat.auteur_id), U2 end, {message, [{id, erlang:integer_to_list(Mess#minichat.id, 36)}], [ {date, [], [format_date(Mess#minichat.date)]}, {systeme, [], [atom_to_list(Mess#minichat.auteur_id =:= 0)]}, {proprietaire, [], [atom_to_list(Est_proprietaire)]}, {repondu, [], [atom_to_list(A_repondu_a_message)]}, {reponse, [], [atom_to_list(Est_une_reponse_a_user)]}, {pseudo, [], [Mess#minichat.pseudo]}, {login, [], [if User_mess =:= inconnu -> Mess#minichat.pseudo; true -> User_mess#user.login end]}, {contenu, [], [Mess#minichat.contenu]}, {repondA, [], xml_repond_a(Repond_a)} ] } end, Conv ) ] } end, euphorik_minichat_conversation:conversations(User#user.conversations, Nb_message, Dernier_id, User#user.page_principale) ) }]; _ -> [{reponse, [{name, "refreshMessages"}], [{erreur, [], ["erreur"]}]}] end ). % Prend une liste de xml text node et en resort un string() % xmerl : "test & test" devient deux fragments de texte : "test " et "& test", il faut donc rassembler les morceaux... defragmenter(Text_nodes) -> lists:foldl(fun(Node, Acc) -> #xmlText{value = V} = Node, Acc ++ V end, [], Text_nodes). % Un utilisateur envoie un message message(Action) -> simple_xml_to_string( case { xmerl_xpath:string("cookie", Action), xmerl_xpath:string("pseudo", Action), xmerl_xpath:string("contenu", Action) } of { [#xmlElement{content = [#xmlText{value = Cookie}]}], [#xmlElement{content = Pseudo_fragments}], [#xmlElement{content = Contenu_fragments}] } -> case euphorik_minichat:user_by_cookie(Cookie) of {ok, U} -> Pseudo = defragmenter(Pseudo_fragments), Contenu = defragmenter(Contenu_fragments), % met à jour le pseudo du user euphorik_minichat:update_pseudo_user(U#user.id, Pseudo), Reponses = case xmerl_xpath:string("reponses", Action) of [#xmlElement{content = C}] -> lists:map( fun (Reponse) -> #xmlElement{attributes = [#xmlAttribute{name = id, value = Id_reponse}]} = Reponse, erlang:list_to_integer(Id_reponse, 36) end , C); _ -> [] end, Contenu_strip = string:strip(Contenu), if Contenu_strip =:= [] -> xml_reponse_message(pas_ok); true -> case euphorik_minichat:nouveau_message(Contenu, U#user.id, Reponses) of erreur -> xml_reponse_message(pas_ok); _ -> xml_reponse_message(ok) end end; _ -> xml_reponse_message(pas_ok) end; _ -> xml_reponse_message(pas_ok) end ). % Formatage d'une heure % local_time() -> string format_date(Date) -> DateLocal = calendar:now_to_local_time(Date), DateNowLocal = calendar:local_time(), {{Annee, Mois, Jour}, {Heure, Minute, Seconde}} = DateLocal, {{AnneeNow, _, _}, {_, _, _}} = DateNowLocal, Hier = calendar:date_to_gregorian_days(element(1, DateLocal)) =:= calendar:date_to_gregorian_days(element(1, DateNowLocal)) - 1, if element(1, DateLocal) =:= element(1, DateNowLocal) -> ""; Hier -> "Hier "; Annee =:= AnneeNow -> io_lib:format("~2.10.0B/~2.10.0B ", [Jour, Mois]); true -> io_lib:format("~2.10.0B/~2.10.0B/~B ", [Jour, Mois, Annee]) end ++ io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [Heure, Minute, Seconde]). %%%%%%%%% %%%%%%%%% simple_xml_to_string(XML) -> lists:flatten(xmerl:export_simple(XML, xmerl_xml, [{prolog, ["\n"]}])). % Construit une réponse positive à un login % si Enregistre vaut true alors cela veut dire que la personne s'est enregistré (elle possède au moins un login et un password) xml_reponse_login_ok(User) -> [{reponse, [{name, "login"}], [ {statut, [if (User#user.password =/= []) and (User#user.login =/= []) -> "enregistre"; true -> "identifie" end]}, {cookie, [User#user.cookie]}, {id, [erlang:integer_to_list(User#user.id, 36)]}, {pseudo, [User#user.pseudo]}, {login, [User#user.login]}, {email, [User#user.email]}, {css, [User#user.css]}, {pagePrincipale, [integer_to_list(User#user.page_principale)]} ] ++ lists:map( fun(C) -> {conversation, [ {racine, [erlang:integer_to_list(element(1, C), 36)]}, {page, [integer_to_list(element(2, C))]} ] } end, User#user.conversations ) }]. % Construit un réponse négative à un login xml_reponse_login_pas_ok(Message) -> [{reponse, [{name, "login"}], [ {statut, ["erreur"]}, {information, [Message]} ] }]. xml_reponse_profile_ok() -> [{reponse, [{name, "profile"}], [ {statut, ["ok"]} ] }]. xml_reponse_profile_pas_ok(Message) -> [{reponse, [{name, "profile"}], [ {statut, ["pas ok"]}, {information, [Message]} ] }]. % Pas utilisé %~ xml_conversation(Mess_id, Nb) -> %~ {Mess_id, Conversation} = minichat:conversation(Mess_id, Nb), %~ xml_conversation(Conversation). %~ xml_conversation([]) -> []; %~ xml_conversation(Liste_id) -> %~ lists:map( %~ fun({Id, Sous_liste}) -> %~ {id, [{id, erlang:integer_to_list(Id, 36)}], xml_conversation(Sous_liste)} %~ end, %~ Liste_id %~ ). % Renvoie un element XML representant une liste de messages auquel le message M_id repond xml_repond_a(Reponses) -> lists:map( fun(Id_mess) -> {ok, M} = euphorik_minichat:message_by_id(Id_mess), {ok, User} = euphorik_minichat:user_by_mess(Id_mess), {id, [{id, erlang:integer_to_list(M#minichat.id, 36)}, {pseudo, M#minichat.pseudo}, {login, User#user.login}], []} end, Reponses ). xml_reponse_message(Ok) -> [ {reponse, [{name, "message"}], [ {statut, [], [case Ok of ok -> "ok"; pas_ok -> "pas ok" end]} ] } ]. %%%%%%%%% %%%%%%%%%