MOD maj TODO
[euphorik.git] / modules / erl / euphorik_protocole.erl
1 % coding: utf-8
2 % Ce module gére les différents messages envoyés par le client (javascript) via AJAX.
3 % Les messages donnés ainsi que les réponses sont au format JSON.
4 % @author G.Burri
5
6 -module(euphorik_protocole).
7 -export([
8 register/2,
9 login/2,
10 logout/1,
11 profile/1,
12 wait_event/1,
13 put_message/1,
14 ban/1,
15 erreur/1
16 ]).
17
18 -include_lib("xmerl/include/xmerl.hrl").
19 -include("../include/euphorik_bd.hrl").
20 -include("../include/euphorik_defines.hrl").
21
22
23 % Une utilisateur s'enregistre avec un tuple {Login, Password}.
24 register([{login, Login}, {password, Password}], IP) ->
25 Can_register = euphorik_bd:can_register(IP),
26 if Can_register ->
27 case euphorik_bd:user_by_login(Login) of
28 {ok, _} ->
29 erreur("Login déjà existant");
30 _ ->
31 User = euphorik_bd:nouveau_user(Login, Password, generer_cookie()),
32 euphorik_bd:update_ip(User#user.id, IP),
33 json_reponse_login_ok(User)
34 end;
35 true ->
36 erreur_register_flood()
37 end;
38 % Enregistrement sans {Login, Password}
39 register([], IP) ->
40 Can_register = euphorik_bd:can_register(IP),
41 if Can_register ->
42 User = euphorik_bd:nouveau_user("<nick>", generer_cookie()),
43 euphorik_bd:update_ip(User#user.id, IP),
44 json_reponse_login_ok(User);
45 true ->
46 erreur_register_flood()
47 end.
48
49 erreur_register_flood() ->
50 erreur("Trop de register (flood)").
51
52
53 % Un utilisateur se logge (avec un couple {login, mot de passe})
54 login([{login, Login}, {password, Password}], IP) ->
55 loginUser(euphorik_bd:user_by_login_password(Login, Password), IP);
56 % Un utilisateur se logge (avec un cookie)
57 login([{cookie, Cookie}], IP) ->
58 loginUser(euphorik_bd:user_by_cookie(Cookie), IP).
59
60 loginUser({ok, User}, IP) ->
61 euphorik_bd:update_ip(User#user.id, IP),
62 euphorik_bd:update_date_derniere_connexion(User#user.id),
63 json_reponse_login_ok(User);
64 loginUser(_, _) ->
65 % ajoute un délais d'attente (TODO : un autre moyen plus élégant ?)
66 receive after 1000 ->
67 erreur("Erreur login")
68 end.
69
70
71 % Renvoie un string() représentant un cookie en base 36. Il y a 10^32 possibillités.
72 generer_cookie() ->
73 {A1,A2,A3} = now(),
74 random:seed(A1, A2, A3),
75 erlang:integer_to_list(random:uniform(math:pow(10, 32)), 36).
76
77
78 % Un utilisateur se délogge.
79 logout(_) ->
80 do_nothing.
81
82
83 % Modification du profile.
84 profile(
85 [
86 {cookie, Cookie},
87 {login, Login},
88 {password, Password},
89 {nick, Pseudo},
90 {email, Email},
91 {css, Css},
92 {nick_format, Nick_format_str},
93 {main_page, Main_page},
94 {conversations, {array, Conversations_json}}
95 ]
96 ) ->
97 % est-ce que les messages auquel on répond existent ?
98 Conversations = lists:foldr(
99 fun({struct, [{root, Root}, {page, Page}]}, Acc) ->
100 Message_existe = euphorik_bd:message_existe(Root),
101 if Message_existe ->
102 [{Root, Page} | Acc];
103 true ->
104 Acc
105 end
106 end,
107 [],
108 Conversations_json
109 ),
110 case euphorik_bd:set_profile(Cookie, Login, Password, Pseudo, Email, Css, list_to_atom(Nick_format_str), Main_page, Conversations) of
111 ok ->
112 json_reponse_ok();
113 login_deja_pris ->
114 erreur("Login déjà pris");
115 _ ->
116 erreur("Impossible de mettre à jour le profile")
117 end.
118
119
120 % Renvoie les messages appropriés.
121 % last_message id et cookie sont facultatifs
122 wait_event(Data) ->
123 Cookie = case lists:keysearch(cookie, 1, Data) of {value, {_, C}} -> C; _ -> inconnu end,
124 Last_message_id = case lists:keysearch(last_message_id, 1, Data) of {value, {_, Id}} -> Id; _ -> 0 end,
125 {value, {_, Message_count}} = lists:keysearch(message_count, 1, Data),
126 Main_page = case lists:keysearch(main_page, 1, Data) of {value, {_, P}} -> P; _ -> 1 end,
127 {value, {_, {array, Conversations_json}}} = lists:keysearch(conversations, 1, Data),
128 Conversations = lists:map(
129 fun({struct, [{root, Racine}, {page, Page} | Reste]}) ->
130 Last_mess_conv = case Reste of [{last_message_id, L}] -> L; _ -> 0 end,
131 {Racine, Page, Last_mess_conv}
132 end,
133 Conversations_json
134 ),
135 User = case euphorik_bd:user_by_cookie(Cookie) of
136 {ok, U} -> U;
137 _ -> inconnu
138 end,
139 {struct, [
140 {reply, "new_message"},
141 {conversations, {array,
142 % accrochez-vous ca va siouxer ;)
143 lists:map(
144 fun({Conv, Plus}) ->
145 {struct, [
146 {last_page, not Plus},
147 {messages, {array,
148 lists:map(
149 fun({Mess, Repond_a}) ->
150 Est_proprietaire = User =/= inconnu andalso User#user.id =:= Mess#minichat.auteur_id,
151 A_repondu_a_message = User =/= inconnu andalso euphorik_bd:a_repondu_a_message(User#user.id, Mess#minichat.id),
152 Est_une_reponse_a_user = User =/= inconnu andalso euphorik_bd:est_une_reponse_a_user(User#user.id, Mess#minichat.id),
153 User_mess =
154 if Mess#minichat.auteur_id =:= 0 ->
155 inconnu;
156 true ->
157 {ok, U2} = euphorik_bd:user_by_id(Mess#minichat.auteur_id),
158 U2
159 end,
160 {struct, [
161 {id, Mess#minichat.id},
162 {date, format_date(Mess#minichat.date)},
163 {system, Mess#minichat.auteur_id =:= 0},
164 {owner, Est_proprietaire},
165 {answered, A_repondu_a_message},
166 {is_a_reply, Est_une_reponse_a_user},
167 {nick, Mess#minichat.pseudo},
168 {login, if User_mess =:= inconnu -> Mess#minichat.pseudo; true -> User_mess#user.login end},
169 {content, Mess#minichat.contenu},
170 {answer_to, {array, lists:map(
171 fun(Id_mess) ->
172 {ok, M} = euphorik_bd:message_by_id(Id_mess),
173 {ok, User_reponse} = euphorik_bd:user_by_mess(M#minichat.id),
174 {struct, [{id, M#minichat.id}, {nick, M#minichat.pseudo}, {login, User_reponse#user.login}]}
175 end,
176 Repond_a
177 )}},
178 {ek_master, User_mess#user.ek_master}
179 ]}
180 end,
181 Conv
182 )
183 }}
184 ]}
185 end,
186 euphorik_minichat_conversation:conversations(
187 Conversations,
188 Message_count,
189 Last_message_id,
190 Main_page
191 )
192 )
193 }}
194 ]}.
195
196
197 % Un utilisateur envoie un message
198 put_message(
199 [
200 {cookie, Cookie},
201 {nick, Nick},
202 {content, Content},
203 {answer_to, {array, Answer_to}}
204 ]
205 ) ->
206 case euphorik_bd:user_by_cookie(Cookie) of
207 {ok, User} ->
208 Strip_content = string:strip(Content),
209 if (Strip_content =:= []) ->
210 erreur("Message vide");
211 true ->
212 % TODO : non-atomique (update_pseudo+nouveau_message)
213 euphorik_bd:update_pseudo_user(User#user.id, Nick),
214 case euphorik_bd:nouveau_message(Strip_content, User#user.id, Answer_to) of
215 erreur -> erreur("Impossible d'ajouter un nouveau message");
216 _ ->
217 json_reponse_ok()
218 end
219 end;
220 _ ->
221 erreur("Utilisateur inconnu")
222 end.
223
224
225 % bannissement d'un utilisateur (son ip est bannie)
226 ban(
227 [
228 {cookie, Cookie},
229 {duration, Duration},
230 {user_id, User_id}
231 ]) ->
232 % controle que l'utilisateur est un admin
233 case euphorik_bd:user_by_cookie(Cookie) of
234 {ok, User = #user{ek_master = true}} ->
235 case euphorik_bd:user_by_id(User_id) of
236 {ok, User} ->
237 euphorik_bd:ban(User#user.last_ip, Duration);
238 _ ->
239 erreur("Utilisateur à bannir inconnu")
240 end;
241 _ ->
242 erreur("Utilisateur inconnu ou non ek master")
243 end.
244
245
246 % Construit une erreur
247 erreur(Message) ->
248 {
249 struct, [
250 {reply, "error"},
251 {error_message, Message}
252 ]
253 }.
254
255
256 % Formatage d'une heure
257 % local_time() -> string
258 format_date(Date) ->
259 DateLocal = calendar:now_to_local_time(Date),
260 DateNowLocal = calendar:local_time(),
261 {{Annee, Mois, Jour}, {Heure, Minute, Seconde}} = DateLocal,
262 {{AnneeNow, _, _}, {_, _, _}} = DateNowLocal,
263 Hier = calendar:date_to_gregorian_days(element(1, DateLocal)) =:= calendar:date_to_gregorian_days(element(1, DateNowLocal)) - 1,
264 lists:flatten(
265 if element(1, DateLocal) =:= element(1, DateNowLocal) ->
266 "";
267 Hier ->
268 "Hier ";
269 Annee =:= AnneeNow ->
270 io_lib:format("~2.10.0B/~2.10.0B ", [Jour, Mois]);
271 true ->
272 io_lib:format("~2.10.0B/~2.10.0B/~B ", [Jour, Mois, Annee])
273 end ++
274 io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [Heure, Minute, Seconde])
275 ).
276
277
278 json_reponse_ok() ->
279 {struct, [{reply, "ok"}]}.
280
281
282 json_reponse_login_ok(User) ->
283 {
284 struct, [
285 {reply, "login"},
286 {status, if (User#user.password =/= []) and (User#user.login =/= []) -> "auth_registered"; true -> "auth_not_registered" end},
287 {cookie, User#user.cookie},
288 {id, User#user.id},
289 {nick, User#user.pseudo},
290 {login, User#user.login},
291 {email, User#user.email},
292 {css, User#user.css},
293 {nick_format, atom_to_list(User#user.nick_format)},
294 {main_page, User#user.page_principale},
295 {conversations,
296 {array,
297 lists:map(
298 fun(C) ->
299 {struct,
300 [
301 {root, element(1, C)},
302 {page, element(2, C)}
303 ]
304 }
305 end,
306 User#user.conversations
307 )
308 }
309 },
310 {ek_master, User#user.ek_master}
311 ]
312 }.