ADD ajout du bannissement (pas fini)
authorGreg Burri <greg.burri@gmail.com>
Wed, 7 May 2008 19:06:50 +0000 (19:06 +0000)
committerGreg Burri <greg.burri@gmail.com>
Wed, 7 May 2008 19:06:50 +0000 (19:06 +0000)
ADD du statut de ekMaster (admin)
ADD limitation du nombre de register qu'il est possible de faire à la suite (anti flood)

css/1/pageMinichat.css
doc/TODO.txt
doc/protocole3.txt
js/euphorik.js
js/pageMinichat.js
modules/erl/euphorik_bd.erl
modules/erl/euphorik_protocole.erl
modules/erl/euphorik_requests.erl
modules/include/euphorik_bd.hrl
sessions/erl.session
sessions/js.session

index fac422e..d7cd580 100755 (executable)
    color: #76ff33;
 }
 
+#page.minichat div.message.ekMaster .pseudo {
+       color: #ffffff;
+}
+
 #page.minichat div.message .pseudo .login {
    margin-left: 2px;
    font-size: 8px;
index e9452f0..a36c000 100755 (executable)
@@ -16,7 +16,6 @@
 * 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
 * Finir le script de mise en production\r
    * Make des modules.\r
 [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). 
+[ok] Restructurer le code Erlang : déplacer certaines fonctions d'un module à l'autre (ev. créer des modules)
 
 
 === Bugs ===
index feae1f0..f0b0b38 100644 (file)
@@ -173,6 +173,7 @@ Le format de la date n'est pas formel.
                   "answer_to" : [\r
                      { "id" : 123, "nick" : "Pierre", "login" : "pierre_45" }\r
                   ]
+                  "ek_master" : true | false
                }\r
             ]\r
          }
@@ -229,7 +230,7 @@ c -> s
    {
       "action" : "ban",
       "cookie" : "LKJDLAKSJBFLKASN",
-      "duration" : 3, // en heure
+      "duration" : 15, // en minute
       "user_id" : 67
    }
    
index 3503c7e..2d64fe2 100755 (executable)
@@ -305,7 +305,8 @@ Formateur.prototype.traiterLiensConv = function(M)
 }
 \r
 /**\r
-  * FIXME : Cette méthode est attrocement lourde ! A optimiser.\r
+  * FIXME : Cette méthode est attrocement lourde ! A optimiser.
+  * moyenne su échantillon : 234ms\r
   */
 Formateur.prototype.traiterSmiles = function(M)
 {  
@@ -426,7 +427,7 @@ Client.prototype.resetDonneesPersonnelles = function()
    this.nickFormat = "nick"
    
    this.pagePrincipale = 1
-   this.ek_master = false
+   this.ekMaster = false
    
    // les conversations, une conversation est un objet possédant les attributs suivants :
    // - racine (entier)
@@ -707,7 +708,7 @@ Client.prototype.chargerDonnees = function(data)
       // les conversations
       thisClient.conversations = data["conversations"]
       
-      thisClient.ek_master = data["ek_master"]
+      thisClient.ekMaster = data["ek_master"]
    }
    this.dernierMessageErreur = data["error_message"]
 }
index c118c54..2be7ca5 100755 (executable)
@@ -250,6 +250,7 @@ function Message(id, date, pseudo, login, contenu)
    this.appartientAuClient = false
    this.clientARepondu = false
    this.estUneReponse = false
+   this.ekMaster = false
    
    this.systeme = false // est-ce un message 'système' ?
    
@@ -353,11 +354,11 @@ function Conversation(num, util, formateur, client)
 }
 
 /**
-  * Défini les fonctions (callback) appelées lorsque l'on change de page.
+  * Défini la page courante et s'il l'on se trouve sur la dernière page.
   * @pageCourante la page courante
   * @dernierePage true si c'est la dernière page sinon false
   */
-Conversation.prototype.eventsPage = function(pageCourante, dernierePage)
+Conversation.prototype.setPage = function(pageCourante, dernierePage)
 {
    jQuery("#conversations #" + this.getId() + " .numPage").text(pageCourante)
    jQuery("#conversations #" + this.getId() + " .next").css("display", pageCourante == 1 ? "none" : "inline")
@@ -430,6 +431,9 @@ Conversation.prototype.ajouterMessage = function(message)
       delete this.messagesParId[this.messages.shift().id]
 }
 
+/**
+  * FIXME : méthode très lourde. ne serait-ce pas mieux de virer d'un coup l'élément conversation et d'en remettre un vide ?
+  */
 Conversation.prototype.viderMessages = function()
 {
    this.messages = new Array()
@@ -440,6 +444,7 @@ Conversation.prototype.viderMessages = function()
 /**
   * Après l'ajout d'un ou plusieurs message cette méthode est appelée afin
   * d'afficher les messages non-affichés.
+  * FIXME : méthode super lourde, à optimiser.
   * @param funClickExtract fonction (fun(numMess)) appellée lors du clic sur un bouton "extraire"
   */
 Conversation.prototype.flush = function(funClickOuvrirConv)
@@ -475,6 +480,7 @@ Conversation.prototype.flush = function(funClickOuvrirConv)
                (this.messages[i].clientARepondu ? " repondu" : "") +
                (this.messages[i].estUneReponse ? " reponse" : "") +
                (this.messages[i].systeme ? " systeme" : "") +
+               (this.messages[i].ekMaster ? " ekMaster" : "") +
             "\">" +
                "<div class=\"extraire\">&gt;</div>" +
                "[<span class=\"date\">" + message.date + "</span>]" +
@@ -498,7 +504,7 @@ Conversation.prototype.flush = function(funClickOuvrirConv)
    if (nbMessagesAffiche > this.nbMessageMax)
       jQuery("#conversations #" + this.getId() + " .message").slice(this.nbMessageMax, nbMessagesAffiche).empty()
 
-   // Ajoute les événements liés à chaque message
+   // ajoute les événements liés à chaque nouveau message
    jQuery("#conversations #" + this.getId() + " .message").filter(function(){return parseInt(jQuery(this).attr("id"), 36) > thisConversation.idDernierMessageAffiche}).each(
       function()
       {
@@ -649,12 +655,24 @@ Messages.prototype.getJSONConversations = function()
   * Ajoute un ensemble de messages puis les affiches.
   * @param elements un tableau d'éléments JSON représentant les messages, voir protocole.txt
   * @param numConversation le numéro de la conversation auquel appartiennent les messages
+  * @return true si les messages on été ajoutés, false si la conversation n'existe pas et qu'il n'y a pas de message
   */
 Messages.prototype.ajouterMessages = function(elements, numConversation)
 {
+   if (elements["messages"].length == 0 && typeof(this.conversations[numConversation]) == "undefined")
+      return false
+
    for (var i = 0; i < elements["messages"].length; i++)
       this.ajouterMessage(elements["messages"][i], numConversation)
    this.flush(numConversation)
+   
+   // renseigne la conversation sur la page courante et si c'est la dernière
+   this.conversations[numConversation].setPage(
+      numConversation == 0 ? this.client.pagePrincipale : this.client.conversations[numConversation - 1].page,
+      elements["last_page"]
+   )
+   
+   return true
 }
 
 /**
@@ -670,10 +688,6 @@ Messages.prototype.ajouterMessage = function(element, numConversation)
    // pas d'utilisation de jquery pour des raisons de performance
    var id = element["id"]
    
-   // Obsolète
-   /*if (this.idDernierMessage == null || id > this.idDernierMessage)
-      this.idDernierMessage = id*/
-   
    var message = new Message(
       id,      
       element["date"],
@@ -687,6 +701,7 @@ Messages.prototype.ajouterMessage = function(element, numConversation)
    message.estUneReponse = element["is_a_reply"]
    message.systeme = element["system"] 
    message.setRepondA(element["answer_to"])
+   message.ekMaster = element["ek_master"]
    
    if (this.conversations[numConversation] == null)
    {
@@ -700,7 +715,7 @@ Messages.prototype.ajouterMessage = function(element, numConversation)
          {
                thisPage.util.replaceSelection(
                   jQuery("form input.message")[0],
-                  "{" + thisMessages.client.conversations[num-1].root + "}"
+                  "{" + thisMessages.client.conversations[num-1].root.toString(36) + "}"
                )
          }
       )
@@ -857,16 +872,14 @@ Messages.prototype.rafraichirMessages = function(vider)
             // ajoute les messages reçus à leur conversation respective
             for (var numConv = 0; numConv < data["conversations"].length; numConv++)
             {
-               // ya pas de nouveaux message -> on passe à la prochaine conversation
-               if (data["conversations"][numConv]["messages"].length == 0) continue
-               
-               thisMessages.ajouterMessages(data["conversations"][numConv], numConv)
+               // ya pas de nouveaux message -> on passe à la prochaine conversation FIXME : marche pas
+               //if (data["conversations"][numConv]["messages"].length == 0) continue
                
-               // définit les événements liés à la conversation
-               thisMessages.conversations[numConv].eventsPage(
-                  numConv == 0 ? thisMessages.client.pagePrincipale : thisMessages.client.conversations[numConv - 1].page,
-                  data["conversations"][numConv]["last_page"]
-               )
+               if (! thisMessages.ajouterMessages(data["conversations"][numConv], numConv))
+               {
+                  thisMessages.util.messageDialogue("La conversation {" + thisMessages.client.conversations[numConv -1].root.toString(36) + "} n'existe pas")
+                  thisMessages.client.supprimerConversation(numConv - 1) 
+               }
             }
             
             // rappel de la fonction dans 100 ms
index e114b52..678505b 100755 (executable)
@@ -17,6 +17,7 @@
    nouveau_user/3,
    set_profile/9,
    update_date_derniere_connexion/1,
+   update_ip/2,
    update_pseudo_user/2,
    users/0,
    user_by_cookie/1,   
@@ -24,6 +25,7 @@
    user_by_login/1,
    user_by_login_password/2,
    user_by_mess/1,
+   toggle_ek_master/1,
    
    % messages :
    nouveau_message/3,
    a_repondu_a_message/2,
    possede_message/2,
    
+   % ip :
+   ip_table/0,
+   ban/2,
+   
+   % versions :
+   update_version/1,
+   
    % utiles :
    can_register/1,
    resultat_transaction/1\r
@@ -172,10 +181,10 @@ set_profile(Cookie, Login, Password, Pseudo, Email, Css, Nick_format, Page_princ
 
 
 % Met à jour la date de la dernière connexion d'un utilisateur à maintenant
-update_date_derniere_connexion(UserId) ->
+update_date_derniere_connexion(User_id) ->
    mnesia:transaction(
       fun() ->
-         case mnesia:wread({user, UserId}) of
+         case mnesia:wread({user, User_id}) of
             [User] ->
                mnesia:write(User#user{date_derniere_connexion = now()});
             _ ->
@@ -183,7 +192,21 @@ update_date_derniere_connexion(UserId) ->
           end
       end
    ).   
-  
+
+
+% Met à jour l'ip d'un user
+update_ip(User_id, IP) ->
+   mnesia:transaction(
+      fun() ->
+         case mnesia:wread({user, User_id}) of
+            [User] ->
+               mnesia:write(User#user{last_ip = IP});
+            _ ->
+               mnesia:abort("update_ip: User inconnu")
+          end
+      end
+   ).   
+   
   
 % Met à jour le pseudo du user
 update_pseudo_user(UserId, Pseudo) ->
@@ -244,6 +267,19 @@ user_by_login(Login) ->
    )).\r
    
    
+toggle_ek_master(User_id) ->
+   resultat_transaction(mnesia:transaction(
+      fun() ->
+         Users = qlc:e(qlc:q([E || E <- mnesia:table(user), E#user.id =:= User_id])),
+         case Users of
+            [User] ->
+               mnesia:write(User#user{ek_master = not User#user.ek_master});
+            _ -> erreur
+         end
+      end
+   )).
+   
+   
 user_by_login_password(Login, Password) ->
    resultat_transaction(mnesia:transaction(
       fun() ->
@@ -439,6 +475,37 @@ possede_message(Id_user, Id_mess) ->
       _ -> false
    end.
    
+
+ip_table() ->
+   resultat_transaction(mnesia:transaction(
+      fun() ->
+         qlc:e(qlc:q([IP || IP <- mnesia:table(ip_table)]))
+      end
+   )).
+
+
+% Bannie une ip pour un certain temps (en minute).
+ban(IP, Duration) ->
+   mnesia:transaction(
+      fun() ->
+         case mnesia:wread({ip_table, IP}) of
+            [IP_tuple] ->
+               mnesia:write(IP_tuple#ip_table{ban = now(), ban_duration = Duration});
+            _ ->
+               mnesia:write(#ip_table{ip = IP, ban = now(), ban_duration = Duration})
+          end
+      end
+   ).
+   
+   
+update_version(1) ->
+   mnesia:transform_table(
+      ip_table,
+      fun() -> null end,
+      record_info(fields, ip_table),
+      ip_table
+   ).
+   
    
 % 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).
index 1f5faf3..194961e 100755 (executable)
@@ -6,11 +6,12 @@
 -module(euphorik_protocole).
 -export([
    register/2,
-   login/1,
+   login/2,
    logout/1,
    profile/1,
    wait_event/1,
    put_message/1,
+   ban/1,
    erreur/1
 ]).\r
 
@@ -28,6 +29,7 @@ register([{login, Login}, {password, Password}], IP) ->
                erreur("Login déjà existant");
             _ ->
                User = euphorik_bd:nouveau_user(Login, Password, generer_cookie()),
+               euphorik_bd:update_ip(User#user.id, IP),
                json_reponse_login_ok(User)
          end;
       true ->
@@ -38,6 +40,7 @@ register([], IP) ->
    Can_register = euphorik_bd:can_register(IP),
    if Can_register ->
          User = euphorik_bd:nouveau_user("<nick>", generer_cookie()),
+         euphorik_bd:update_ip(User#user.id, IP),
          json_reponse_login_ok(User);
       true ->
          erreur_register_flood()
@@ -48,16 +51,17 @@ erreur_register_flood() ->
    
 \r
 % Un utilisateur se logge (avec un couple {login, mot de passe})
-login([{login, Login}, {password, Password}]) ->
-   loginUser(euphorik_bd:user_by_login_password(Login, Password));
+login([{login, Login}, {password, Password}], IP) ->
+   loginUser(euphorik_bd:user_by_login_password(Login, Password), IP);
 % Un utilisateur se logge (avec un cookie)
-login([{cookie, Cookie}]) ->
-   loginUser(euphorik_bd:user_by_cookie(Cookie)).
+login([{cookie, Cookie}], IP) ->
+   loginUser(euphorik_bd:user_by_cookie(Cookie), IP).
    
-loginUser({ok, User}) ->
+loginUser({ok, User}, IP) ->
+   euphorik_bd:update_ip(User#user.id, IP),
    euphorik_bd:update_date_derniere_connexion(User#user.id),
    json_reponse_login_ok(User);
-loginUser(_) ->
+loginUser(_, _) ->
    % ajoute un délais d'attente (TODO : un autre moyen plus élégant ?)
    receive after 1000 ->
       erreur("Erreur login")
@@ -153,25 +157,26 @@ wait_event(Data) ->
                                     {ok, U2} = euphorik_bd:user_by_id(Mess#minichat.auteur_id),
                                     U2
                               end,
-                              {struct, [
-                                 {id, Mess#minichat.id},
-                                 {date, format_date(Mess#minichat.date)},
-                                 {system, Mess#minichat.auteur_id =:= 0},
-                                 {owner, Est_proprietaire},
-                                 {answered, A_repondu_a_message},
-                                 {is_a_reply, Est_une_reponse_a_user},
-                                 {nick, Mess#minichat.pseudo},
-                                 {login, if User_mess =:= inconnu -> Mess#minichat.pseudo; true -> User_mess#user.login end},
-                                 {content, Mess#minichat.contenu},
-                                 {answer_to, {array, lists:map(
-                                    fun(Id_mess) ->                   
-                                       {ok, M} = euphorik_bd:message_by_id(Id_mess),
-                                       {ok, User_reponse} = euphorik_bd:user_by_mess(M#minichat.id),
-                                       {struct, [{id, M#minichat.id}, {nick, M#minichat.pseudo}, {login, User_reponse#user.login}]}
-                                    end,
-                                    Repond_a
-                                 )}}
-                              ]}
+                           {struct, [
+                              {id, Mess#minichat.id},
+                              {date, format_date(Mess#minichat.date)},
+                              {system, Mess#minichat.auteur_id =:= 0},
+                              {owner, Est_proprietaire},
+                              {answered, A_repondu_a_message},
+                              {is_a_reply, Est_une_reponse_a_user},
+                              {nick, Mess#minichat.pseudo},
+                              {login, if User_mess =:= inconnu -> Mess#minichat.pseudo; true -> User_mess#user.login end},
+                              {content, Mess#minichat.contenu},
+                              {answer_to, {array, lists:map(
+                                 fun(Id_mess) ->                   
+                                    {ok, M} = euphorik_bd:message_by_id(Id_mess),
+                                    {ok, User_reponse} = euphorik_bd:user_by_mess(M#minichat.id),
+                                    {struct, [{id, M#minichat.id}, {nick, M#minichat.pseudo}, {login, User_reponse#user.login}]}
+                                 end,
+                                 Repond_a
+                              )}},
+                              {ek_master, User_mess#user.ek_master}
+                           ]}
                         end,
                         Conv
                      )
@@ -217,6 +222,27 @@ put_message(
       end.
 
 
+% bannissement d'un utilisateur (son ip est bannie)
+ban(
+   [
+      {cookie, Cookie},
+      {duration, Duration},
+      {user_id, User_id}
+   ]) ->
+   % controle que l'utilisateur est un admin
+   case euphorik_bd:user_by_cookie(Cookie) of
+      {ok, User = #user{ek_master = true}} ->
+         case euphorik_bd:user_by_id(User_id) of
+            {ok, User} ->
+               euphorik_bd:ban(User#user.last_ip, Duration);
+            _ ->
+               erreur("Utilisateur à bannir inconnu")
+         end;
+      _ ->
+         erreur("Utilisateur inconnu ou non ek master")
+   end.
+      
+
 % Construit une erreur
 erreur(Message) ->
    {
@@ -280,6 +306,7 @@ json_reponse_login_ok(User) ->
                   User#user.conversations
                )
             }
-         }
+         },
+      {ek_master, User#user.ek_master}
       ]
    }.
index 6562e52..4af2f7e 100755 (executable)
@@ -20,10 +20,14 @@ tester() ->
 
 \r
 out(A) ->
-   %io:format("~p~n~n", [A]),\r
+   %io:format("~p~n~n", [A]),
+   IP = case inet:peername(A#arg.clisock) of
+         {ok, {Adresse, _Port}} -> Adresse;
+      _ -> inconnue
+   end,\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, 1),
+   Ret = traiter_donnees(Contenu, IP),
    {content, "application/json", Ret}.\r
 
 \r
@@ -37,8 +41,8 @@ traiter_donnees(Contenu, IP) ->
    
 
 % authentification d'un client
-traiter_action("authentification", JSON, _) ->
-   euphorik_protocole:login(JSON);
+traiter_action("authentification", JSON, IP) ->
+   euphorik_protocole:login(JSON, IP);
 % un client s'enregistre (pseudo + password)
 traiter_action("register", JSON, IP) ->
    euphorik_protocole:register(JSON, IP);
@@ -50,5 +54,7 @@ traiter_action("wait_event", JSON, _) ->
    euphorik_protocole:wait_event(JSON);
 % un utilisateur envoie un message
 traiter_action("put_message", JSON, _) ->
-   euphorik_protocole:put_message(JSON).
+   euphorik_protocole:put_message(JSON);
+traiter_action("ban", JSON, _) ->
+   euphorik_protocole:ban(JSON).
  
\ No newline at end of file
index 6c6cb65..3a8da91 100755 (executable)
@@ -45,7 +45,7 @@
       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
       ek_master = false,
-      last_ip % integer(), undefined si inconnu\r
+      last_ip = undefined % integer(), undefined si inconnu\r
    }). 
 
 
@@ -53,7 +53,8 @@
 -record(ip_table,
    {
       ip, % integer()
-      ban = false,
+      ban = undefined, % la date du dernier bannissement
+      ban_duration = 0, % le temps de ban en minute
       nb_try_register = 0,
       nb_try_login = 0, % pour l'instant pas utilisé
       date_last_try_register,
index 5dda940..dfe47b4 100755 (executable)
@@ -1,20 +1,20 @@
 # SciTE session file
 
-buffer.1.path=/home/gburri/projets/euphorik/modules/erl/euphorik_minichat.erl
-buffer.1.position=360
-buffer.1.current=1
+buffer.1.path=/home/gburri/projets/euphorik/modules/erl/euphorik_minichat_conversation.erl
+buffer.1.position=580
 
-buffer.2.path=/home/gburri/projets/euphorik/modules/erl/euphorik_minichat_conversation.erl
+buffer.2.path=/home/gburri/projets/euphorik/modules/erl/euphorik_protocole.erl
 buffer.2.position=1
 
-buffer.3.path=/home/gburri/projets/euphorik/modules/erl/euphorik_protocole.erl
+buffer.3.path=/home/gburri/projets/euphorik/modules/erl/euphorik_requests.erl
 buffer.3.position=1
 
-buffer.4.path=/home/gburri/projets/euphorik/modules/erl/euphorik_requests.erl
+buffer.4.path=/home/gburri/projets/euphorik/modules/include/euphorik_bd.hrl
 buffer.4.position=1
 
-buffer.5.path=/home/gburri/projets/euphorik/modules/include/euphorik_bd.hrl
-buffer.5.position=1
+buffer.5.path=/home/gburri/projets/euphorik/modules/include/euphorik_defines.hrl
+buffer.5.position=336
 
-buffer.6.path=/home/gburri/projets/euphorik/modules/include/euphorik_defines.hrl
-buffer.6.position=336
+buffer.6.path=/home/gburri/projets/euphorik/modules/erl/euphorik_bd.erl
+buffer.6.position=1
+buffer.6.current=1
index 4b4bf8a..14853bd 100755 (executable)
@@ -5,6 +5,7 @@ buffer.1.position=22818
 
 buffer.2.path=/home/gburri/projets/euphorik/js/pageMinichat.js
 buffer.2.position=7496
+buffer.2.current=1
 
 buffer.3.path=/home/gburri/projets/euphorik/js/pageProfile.js
 buffer.3.position=1
@@ -14,4 +15,15 @@ buffer.4.position=1
 
 buffer.5.path=/home/gburri/projets/euphorik/js/debug.js
 buffer.5.position=1
-buffer.5.current=1
+
+buffer.6.path=/home/gburri/projets/euphorik/css/1/euphorik.css
+buffer.6.position=1
+
+buffer.7.path=/home/gburri/projets/euphorik/css/1/pageAbout.css
+buffer.7.position=1
+
+buffer.8.path=/home/gburri/projets/euphorik/css/1/pageMinichat.css
+buffer.8.position=1
+
+buffer.9.path=/home/gburri/projets/euphorik/css/1/pageProfileRegister.css
+buffer.9.position=164