+ case mnesia:subscribe({table, minichat, detailed}) of\r
+ {error, E} -> E;\r
+ _ ->\r
+ % attente d'événements\r
+ R = wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id, Main_page),\r
+ mnesia:unsubscribe({table, minichat, detailed}),\r
+ R\r
+ end;\r
+% Attend un événement pour la page "Admin"\r
+wait_event([{"page", "admin"}]) ->\r
+ case wait_event_page_admin() of\r
+ banned_ips_refresh ->\r
+ {struct,\r
+ [\r
+ {"reply", "banned_ips_refresh"}\r
+ ]\r
+ };\r
+ _ ->\r
+ erreur(60)\r
+ end;\r
+wait_event(_) ->\r
+ erreur(70).\r
+\r
+\r
+% Attend un événement pour la page "Chat" et renvoie les messages manquants au client.\r
+wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id, Main_page) ->\r
+ % est-ce qu'il y a des nouveaux messages ?\r
+ case euphorik_minichat_conversation:conversations(Racines_conversations, Message_count, Last_message_id, Main_page) of\r
+ vide ->\r
+ wait_event_bd_page_chat(),\r
+ wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id, Main_page);\r
+ Conversations ->\r
+ % Accrochez-vous ca va siouxer ;).\r
+ {struct, [\r
+ {"reply", "new_messages"},\r
+ {"conversations", {array,\r
+ lists:map(\r
+ fun({Racine, {Conv, Plus}}) ->\r
+ {struct, [\r
+ {"last_page", not Plus},\r
+ {"first", % le premier message de la conversation\r
+ if Racine =:= undefined orelse Conv =:= [] ->\r
+ null;\r
+ true ->\r
+ {Racine_id, _, _} = Racine,\r
+ case euphorik_bd:message_by_id(Racine_id) of\r
+ {ok, Mess} ->\r
+ json_message(Mess, euphorik_bd:parents_id(Racine_id), User);\r
+ _ ->\r
+ null\r
+ end\r
+ end\r
+ },\r
+ {messages, {array,\r
+ lists:map(\r
+ fun({Mess, Repond_a}) ->\r
+ json_message(Mess, Repond_a, User)\r
+ end,\r
+ Conv\r
+ )\r
+ }}\r
+ ]}\r
+ end,\r
+ % on ajoute un 'undefined' correspondant à la premier conversation qui ne possède pas de racine\r
+ % TODO : peut être à revoir car un peu lourd est compliqué\r
+ aggregation_racines_conversations([undefined | Racines_conversations], Conversations)\r
+ )\r
+ }}\r
+ ]}\r
+ end.\r
+\r
+\r
+aggregation_racines_conversations(L1, L2) ->\r
+ aggregation_racines_conversations(L1, L2, []).\r
+aggregation_racines_conversations([], [], L) -> lists:reverse(L);\r
+aggregation_racines_conversations([E1|R1], [E2|R2], L) ->\r
+ aggregation_racines_conversations(R1, R2, [{E1, E2} | L]).\r
+\r
+\r
+\r
+% Attend un événement lié à la page 'chat'.\r
+wait_event_bd_page_chat() ->\r
+ receive % attente d'un post\r
+ {mnesia_table_event, {write, minichat, _Message, [], _}} ->\r
+ ok;\r
+ {tcp_closed, _} ->\r
+ exit(normal);\r
+ _ ->\r
+ wait_event_bd_page_chat()\r
+ % 60 minutes de timeout (on ne sais jamais)\r
+ % Après 60 minutes de connexion, le client doit donc reétablir une connexion\r
+ after 1000 * 60 * 60 ->\r
+ timeout\r
+ end.\r
+\r
+\r
+% Attent un événement concernant la page admin\r
+% banned_ips_refresh\r
+% ou timeout\r
+wait_event_page_admin() ->\r
+ case mnesia:subscribe({table, ip_table, detailed}) of\r
+ {error, E} -> E;\r
+ _ ->\r
+ R = receive\r
+ {mnesia_table_event, {write, ip_table, IP, [Old_IP | _], _}}\r
+ when Old_IP#ip_table.ban =/= IP#ip_table.ban; Old_IP#ip_table.ban_duration =/= IP#ip_table.ban_duration ->\r
+ banned_ips_refresh;\r
+ {tcp_closed, _} ->\r
+ exit(normal);\r
+ _ ->\r
+ wait_event_page_admin()\r
+ % 60 minutes de timeout (on ne sais jamais)\r
+ % Après 60 minutes de connexion, le client doit donc reétablir une connexion\r
+ after 1000 * 60 * 60 ->\r
+ timeout\r
+ end,\r
+ mnesia:unsubscribe({table, ip_table, detailed}),\r
+ R\r
+ end.\r
+\r
+\r
+% Un utilisateur envoie un message\r
+% Answer_to est une liste d'id (int)\r
+put_message(\r
+ [\r
+ {"cookie", Cookie},\r
+ {"nick", Nick},\r
+ {"content", Content},\r
+ {"answer_to", {array, Answer_to}}\r
+ ]\r
+) ->\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User} ->\r
+ case euphorik_bd:est_banni(User#user.id) of\r
+ {true, Temps_restant} ->\r
+ erreur(80, [format_minutes(Temps_restant)]);\r
+ _ ->\r
+ Strip_content = string:strip(Content),\r
+ if Strip_content =:= [] ->\r
+ erreur(90);\r
+ true ->\r
+ % attention : non-atomique (update_pseudo+nouveau_message)\r
+ euphorik_bd:update_pseudo_user(User#user.id, Nick),\r
+ case euphorik_bd:nouveau_message(Strip_content, User#user.id, Answer_to) of\r
+ {erreur, R} -> erreur(100, [R]);\r
+ _ ->\r
+ json_reponse_ok()\r
+ end\r
+ end\r
+ end;\r
+ _ ->\r
+ erreur(110)\r
+ end.\r
+\r
+\r
+% bannissement d'un utilisateur (son ip est bannie)\r
+ban(\r
+ [\r
+ {"cookie", Cookie},\r
+ {"duration", Duration},\r
+ {"user_id", User_id},\r
+ {"reason", Reason}\r
+ ]) ->\r
+ % controle que l'utilisateur est un admin\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User1 = #user{ek_master = true}} ->\r
+ case euphorik_bd:user_by_id(User_id) of\r
+ {ok, User1} ->\r
+ erreur(120);\r
+ {ok, #user{ek_master = false, profile = Profile2} = User2} ->\r
+ euphorik_bd:ban(User2#user.last_ip, Duration),\r
+ euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("\"~s~s\" est ~s pour ~s.~s",\r
+ [\r
+ Profile2#profile.pseudo,\r
+ if User2#user.login =:= [] -> ""; true -> " (" ++ User2#user.login ++ ")" end,\r
+ if Duration =< 15 -> "kické"; true -> "banni" end,\r
+ format_minutes(Duration),\r
+ if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end ++ "."\r
+ ]\r
+ ))),\r
+ json_reponse_ok();\r
+ {ok, _} ->\r
+ erreur(130);\r
+ _ ->\r
+ erreur(140)\r
+ end;\r
+ _ ->\r
+ erreur(150)\r
+ end.\r
+\r
+\r
+% slapage d'un user (avertissement)\r
+slap(\r
+ [\r
+ {"cookie", Cookie},\r
+ {"user_id", User_id},\r
+ {"reason", Reason}\r
+ ]) ->\r
+ % controle que l'utilisateur est un admin\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User1 = #user{ek_master = true, profile = Profile1}} ->\r
+ case euphorik_bd:user_by_id(User_id) of\r
+ {ok, User1} ->\r
+ euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("~s s'auto slap~s.",\r
+ [\r
+ Profile1#profile.pseudo,\r
+ if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end\r
+ ]\r
+ ))),\r
+ json_reponse_ok();\r
+ {ok, #user{ek_master = false, profile = Profile2}} ->\r
+ euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("~s se fait slaper par ~s.~s",\r
+ [\r
+ Profile2#profile.pseudo,\r
+ Profile1#profile.pseudo,\r
+ if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end ++ "."\r
+ ]\r
+ ))),\r
+ json_reponse_ok();\r
+ {ok, _} ->\r
+ erreur(130);\r
+ _ ->\r
+ erreur(160)\r
+ end;\r
+ _ ->\r
+ erreur(170)\r
+ end.\r
+\r
+\r
+unban_ip(\r
+ [\r
+ {"cookie", Cookie},\r
+ {"ip", IP}\r
+ ]\r
+) ->\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, #user{ek_master = true}} ->\r
+ euphorik_bd:deban(euphorik_common:unserialize_ip(IP)),\r
+ json_reponse_ok();\r
+ _ ->\r
+ erreur(230)\r
+ end.\r
+\r
+\r
+list_banned_ips(\r
+ [\r
+ {"cookie", Cookie}\r
+ ]\r
+) ->\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, #user{ek_master = true}} ->\r
+ {\r
+ struct,\r
+ [\r
+ {"reply", "list_banned_ips"},\r
+ {"list", {array, lists:map(\r
+ fun({IP, T, Users}) ->\r
+ {struct,\r
+ [\r
+ {"ip", euphorik_common:serialize_ip(IP)},\r
+ {"remaining_time", format_minutes(T)},\r
+ {"users", {array, lists:map(\r
+ fun({Pseudo, Login}) ->\r
+ {struct,\r