* Un kick : l'utilisateur (ip) est kické et bannis pour 15 min\r
* Un ban : l'utilisateur (ip) est kické et bannis pour 3 jours
* Modification de la BD -> ajouter une relation "banned_ip"\r
-* Traiter les tags TODO et FIXME dans le code\r
+* Traiter les tags TODO et FIXME dans le code
+ * Mettre les constantes au niveau du serveur dans euphorik_defines.hrl (par exemple les temps lié au flood)\r
* Cleaner le code (erl, js, xhtml, css) et eventuellement profiler un peu (le refresh est lent sous opera)\r
* Restructurer le code Erlang : déplacer certaines fonctions d'un module à l'autre (ev. créer des modules)\r
* Choisir une licence et la mettre un peu partout dans les sources, voir : http://www.gnu.org/licenses/gpl-howto.fr.html\r
-* Trouver un moyen pour éviter la création à la suite de plusieurs comptes (via register). \r
* Finir le script de mise en production\r
* Make des modules.\r
* Compactage des js lors de la mise en production (afin d'optimiser la bande passante lors de l'accès au site), regarder comment fait jQuery.\r
[ok] Tester avec des caractères accentués sur Firefox, Safari, Opera et IE7. Les messages doivent être envoyés en UTF8.
[ok] Tester avec des caractères exotiques (jap, coréen, etc..)
[ok] Modifier la syntaxe des smiles actuels (pour pas qu'ils entre en conflit avec totoz)
+[ok] Trouver un moyen pour éviter la création à la suite de plusieurs comptes (via register).
+
=== Bugs ===
1 : Critique
"racine" : 123,
"page" : 1
}
- ]\r
+ ],
+ "ek_master" : true | false\r
}
\r
"message_id" : 123,\r
"contenu" : "Salut +++ poulpe"\r
}
-ou\r
- {\r
- "reply" : "error",\r
- "error_message" : "blabla"\r
- }
+ou
+ <error>
\r
\r
=== Envoie d'un troll ===
"answer_to" : [ 345, 532, ... ]\r
}
-s -> c\r
- {\r
- "reply" : "ok" | "error",\r
- "error_message" : "blabla"\r
+s -> c
+ <ok>
+ou
+ <error>
+
+
+=== Bannissement ===
+
+c -> s
+ {
+ "action" : "ban",
+ "cookie" : "LKJDLAKSJBFLKASN",
+ "duration" : 3, // en heure
+ "user_id" : 67
}
- \r
+
+s -> c
+ <ok>
+ou
+ <error>\r
\r
=== Ajout d'une correction d'un messages ===\r
Le client envoie un correctif sous la forme de texte supplémentaire à appondre au dernier messages.\r
this.nickFormat = "nick"
this.pagePrincipale = 1
+ this.ek_master = false
// les conversations, une conversation est un objet possédant les attributs suivants :
// - racine (entier)
// les conversations
thisClient.conversations = data["conversations"]
+ thisClient.ek_master = data["ek_master"]
}
this.dernierMessageErreur = data["error_message"]
}
{attributes, record_info(fields, user)},
{index, [cookie, login]},\r
{disc_copies, [node()]}\r
+ ]),
+ mnesia:create_table(ip_table, [
+ {attributes, record_info(fields, ip_table)},
+ {disc_copies, [node()]}
+ ]),
+ mnesia:create_table(troll, [
+ {attributes, record_info(fields, troll)},
+ {disc_copies, [node()]}
]).
end,
record_info(fields, user),
user
- ).
+ );
+% Ajout de la table 'ip_table'
+% Ajout du statut 'ek_master' pour les users
+vers_version(7) ->
+ mnesia:transform_table(
+ user,
+ fun({user, Id, Cookie, Pseudo, Login, Password, Email, Date_creation, Date_derniere_connexion, Css, Nick_format, Indice_flood, Page_Principale, Conversations}) ->
+ {user, Id, Cookie, Pseudo, Login, Password, Email, Date_creation, Date_derniere_connexion, Css, Nick_format, Indice_flood, Page_Principale, Conversations, false, undefined}
+ end,
+ record_info(fields, user),
+ user
+ ),
+ mnesia:create_table(ip_table, [
+ {attributes, record_info(fields, ip_table)},
+ {disc_copies, [node()]}
+ ]),
+ mnesia:create_table(troll, [
+ {attributes, record_info(fields, troll)},
+ {disc_copies, [node()]}
+ ]);
+vers_version(8) ->
+ mnesia:transform_table(
+ ip_table,
+ fun() -> null end,
+ record_info(fields, ip_table),
+ ip_table
+ ).
+
% exemple de peuplage de la BD, utilisé pour les tests
nouveau_message/3,
reset/0,
% reservé :
+ can_register/1,
resultat_transaction/1\r
]).\r
\r
--include("../include/euphorik_bd.hrl").\r
+-include("../include/euphorik_bd.hrl").
+-include("../include/euphorik_defines.hrl").\r
-include_lib("stdlib/include/qlc.hrl").\r
% Un message est considéré comme du spam s'il est posté 1 seconde ou moins après le dernier posté
E#reponse_minichat.repondant =:= M_id,
M#minichat.id =:= E#reponse_minichat.cible]))
end
- )).\r
- \r
+ )).
+
+
+% Si deux enregistrements consequtifs de la même IP sont fait en moins d'une seconde alors
+% ip_table.nb_try_register est incrémenté de 1 sinon il est décrémenté de 1 (jusqu'a 0).
+% Si ip_table.nb_try_register vaut 5 alors l'ip ne peux plus s'enregistrer pour une heure.\r
+can_register(IP) ->
+ resultat_transaction(mnesia:transaction(
+ fun() ->
+ case qlc:e(qlc:q([I || I <- mnesia:table(ip_table), I#ip_table.ip =:= IP])) of
+ [] ->
+ mnesia:write(#ip_table{ip = IP, date_last_try_register = now()}),
+ true;
+ [T] ->
+ Delta = delta_date_ms(T#ip_table.date_last_try_register, now()),
+ if T#ip_table.nb_try_register =:= ?NB_MAX_FLOOD_REGISTER, Delta < ?TEMPS_BAN_FLOOD_REGISTER ->
+ false;
+ true ->
+ mnesia:write(#ip_table{
+ ip = IP,
+ date_last_try_register = now(),
+ nb_try_register = T#ip_table.nb_try_register + if Delta < ?TEMPS_FLOOD_REGISTER -> 1; T#ip_table.nb_try_register > 0 -> -1; true -> 0 end
+ }),
+ true
+ end
+ end
+ end
+ )).
+
% Renvoie le résultat d'une transaction (en décomposant le tuple fournit)\r
resultat_transaction({_, T}) ->
-module(euphorik_protocole).
-export([
- register/1,
+ register/2,
login/1,
logout/1,
profile/1,
% Une utilisateur s'enregistre avec un tuple {Login, Password}.
-register([{login, Login}, {password, Password}]) ->
- case euphorik_minichat:user_by_login(Login) of
- {ok, _} ->
- erreur("Login déjà existant");
- _ ->
- User = euphorik_minichat:nouveau_user(Login, Password, generer_cookie()),
- json_reponse_login_ok(User)
+register([{login, Login}, {password, Password}], IP) ->
+ Can_register = euphorik_minichat:can_register(IP),
+ if Can_register ->
+ case euphorik_minichat:user_by_login(Login) of
+ {ok, _} ->
+ erreur("Login déjà existant");
+ _ ->
+ User = euphorik_minichat:nouveau_user(Login, Password, generer_cookie()),
+ json_reponse_login_ok(User)
+ end;
+ true ->
+ erreur_register_flood()
end;
% Enregistrement sans {Login, Password}
-register([]) ->
- User = euphorik_minichat:nouveau_user("<nick>", generer_cookie()),
- json_reponse_login_ok(User).
+register([], IP) ->
+ Can_register = euphorik_minichat:can_register(IP),
+ if Can_register ->
+ User = euphorik_minichat:nouveau_user("<nick>", generer_cookie()),
+ json_reponse_login_ok(User);
+ true ->
+ erreur_register_flood()
+ end.
+
+erreur_register_flood() ->
+ erreur("Trop de register (flood)").
+
\r
% Un utilisateur se logge (avec un couple {login, mot de passe})
que_dal.\r
\r
-% il faut catcher toutes les exceptions possibles\r
-out(A) ->\r
+out(A) ->
+ %io:format("~p~n~n", [A]),\r
%inet:setopts(A#arg.clisock, inet:getopts(A#arg.clisock, [active])),\r
{value, {_, Contenu}} = lists:keysearch("action", 1, yaws_api:parse_post(A)),\r
- Ret = traiter_donnees(Contenu),
+ Ret = traiter_donnees(Contenu, 1),
{content, "application/json", Ret}.\r
\r
-traiter_donnees(Contenu) ->
+traiter_donnees(Contenu, IP) ->
case json:decode_string(Contenu) of
{ok, {struct, [{action, Action}| Reste]}} ->
- json:encode(traiter_action(Action, Reste));
+ json:encode(traiter_action(Action, Reste, IP));
_ ->
error
end.
% authentification d'un client
-traiter_action("authentification", JSON) ->
+traiter_action("authentification", JSON, _) ->
euphorik_protocole:login(JSON);
% un client s'enregistre (pseudo + password)
-traiter_action("register", JSON) ->
- euphorik_protocole:register(JSON);
+traiter_action("register", JSON, IP) ->
+ euphorik_protocole:register(JSON, IP);
% modification du profile
-traiter_action("set_profile", JSON) ->
+traiter_action("set_profile", JSON, _) ->
euphorik_protocole:profile(JSON);
% un utilisateur attend un événement (par exemple l'arrivé d'un nouveau message)
-traiter_action("wait_event", JSON) ->
+traiter_action("wait_event", JSON, _) ->
euphorik_protocole:wait_event(JSON);
% un utilisateur envoie un message
-traiter_action("put_message", JSON) ->
+traiter_action("put_message", JSON, _) ->
euphorik_protocole:put_message(JSON).
\ No newline at end of file
nick_format = nick, %atom(), peut valoir 'nick', 'login' ou 'nick_login'
indice_flood = 0, % integer() est incrémenté lorsque l'utilisateur envoie trop rapidement des messages.
page_principale = 1, % la page de la conversation principale
- conversations = [] % [{integer(), integer()}], la liste des messages correspondant au conversation ainsi que la page affichée\r
+ conversations = [], % [{integer(), integer()}], la liste des messages correspondant au conversation ainsi que la page affichée
+ ek_master = false,
+ last_ip % integer(), undefined si inconnu\r
}).
+
+
+% identificateur : (ip)
+-record(ip_table,
+ {
+ ip, % integer()
+ ban = false,
+ nb_try_register = 0,
+ nb_try_login = 0, % pour l'instant pas utilisé
+ date_last_try_register,
+ date_last_try_login % pour l'instant pas utilisé
+ }).
+
+
+-record(troll,
+ {
+ id,
+ id_user,
+ date, % erlang:now()
+ contenu, % chaine de caractère
+ date_choosen % la date à laquelle le troll est mis sur la page principale
+ }).
+
\ No newline at end of file
+
+
+
+
+
+% le temps qu'une ip est bannie après avoir voulu s'etre enregistré trop de fois trop rapidement
+-define(TEMPS_BAN_FLOOD_REGISTER, 60 * 60 * 1000). % 1 heure : en ms
+% le temps entre deux tentatives de register pour compter un flood
+-define(TEMPS_FLOOD_REGISTER, 1500). % 1500 ms
+% après 5 flood l'ip fautive est considérée comme bannie
+-define(NB_MAX_FLOOD_REGISTER, 5).