muc.vala
changeset 2 4e050075fab9
parent 1 76caf6a3f413
child 3 dd7a02c6d476
equal deleted inserted replaced
1:76caf6a3f413 2:4e050075fab9
     8     }
     8     }
     9     public abstract string name();
     9     public abstract string name();
    10 }
    10 }
    11 
    11 
    12 class ModuleMuc : Module {
    12 class ModuleMuc : Module {
       
    13     public static const string NS_MUC_USER = "http://jabber.org/protocol/muc#user";
       
    14 
    13     public enum Affiliation {
    15     public enum Affiliation {
    14         NONE,
    16         NONE,
    15         OUTCAST,
    17         OUTCAST,
    16         MEMBER,
    18         MEMBER,
    17         ADMIN,
    19         ADMIN,
    64     public signal void on_join(Conference conf, Occupant occupant);
    66     public signal void on_join(Conference conf, Occupant occupant);
    65     public signal void on_part(Conference conf, Occupant occupant);
    67     public signal void on_part(Conference conf, Occupant occupant);
    66     public signal void on_role(Conference conf, Occupant occupant, Role prev);
    68     public signal void on_role(Conference conf, Occupant occupant, Role prev);
    67     public signal void on_affil(Conference conf, Occupant occupant, Affiliation prev);
    69     public signal void on_affil(Conference conf, Occupant occupant, Affiliation prev);
    68     public signal void on_nick(Conference conf, Occupant occupant, string prev);
    70     public signal void on_nick(Conference conf, Occupant occupant, string prev);
       
    71     public signal void on_message(Conference conf, Occupant occupant, Lm.MessageNode message, string body);
    69     public signal void on_subject(Conference conf);
    72     public signal void on_subject(Conference conf);
    70 
    73 
    71     public class Conference : Object {
    74     public class Conference : Object {
    72         public string jid;
    75         public string jid;
    73         public string nick;
    76         public string nick;
    74         public string desired_nick;
    77         public string desired_nick;
       
    78         public bool enabled;
    75         public string subject;
    79         public string subject;
    76 
    80 
    77         protected State _state;
    81         protected State _state;
    78         public Time time;
    82         public Time time;
    79         public State state { get { return _state; } }
    83         public State state { get { return _state; } }
    85             var old_state = this._state;
    89             var old_state = this._state;
    86             if (old_state != new_state) {
    90             if (old_state != new_state) {
    87                 this._state = new_state;
    91                 this._state = new_state;
    88                 stderr.printf("MUC State changed %s -> %s : %s\n",old_state.to_string(), new_state.to_string(), description);
    92                 stderr.printf("MUC State changed %s -> %s : %s\n",old_state.to_string(), new_state.to_string(), description);
    89                 module.state_changed(this, old_state, new_state, description);
    93                 module.state_changed(this, old_state, new_state, description);
       
    94                 if (new_state == State.DISCONNECTED)
       
    95                     occupants.clear();
    90             } else
    96             } else
    91                 stderr.printf("MUC State not changed %s : %s\n",old_state.to_string(), description);
    97                 stderr.printf("MUC State not changed %s : %s\n",old_state.to_string(), description);
    92         }
    98         }
    93 
    99 
    94         public Conference(ModuleMuc module, string jid) {
   100         public Conference(ModuleMuc module, string jid) {
    95             this.module = module;
   101             this.module = module;
    96             var section = "muc "+jid;
   102             var section = "muc "+jid;
    97             this.desired_nick = module.cfg[section,"nick"];
   103             this.desired_nick = module.cfg.get_default(section,"nick",module.cfg.get_default("muc","nick",Random.next_int().to_string()));
    98             this.nick = this.desired_nick;
   104             this.nick = this.desired_nick;
    99             this.jid = jid;
   105             this.jid = jid;
   100             this._state = State.DISCONNECTED;
   106             this._state = State.DISCONNECTED;
   101             this.occupants = new Gee.HashMap<string,Occupant>();
   107             this.occupants = new Gee.HashMap<string,Occupant>();
   102         }
   108         }
   106                 presence_join_id = jid+"_"+Random.next_int().to_string();
   112                 presence_join_id = jid+"_"+Random.next_int().to_string();
   107                 prs.node.set_attribute("id",presence_join_id);
   113                 prs.node.set_attribute("id",presence_join_id);
   108                 prs.node.add_child("x",null).set_attribute("xmlns","http://jabber.org/protocol/muc");
   114                 prs.node.add_child("x",null).set_attribute("xmlns","http://jabber.org/protocol/muc");
   109                 module.conn.cn.send(prs);
   115                 module.conn.cn.send(prs);
   110                 nick = desired_nick;
   116                 nick = desired_nick;
   111                 _change_state(State.CONNECTING, "requested to join");
   117                 _change_state(State.CONNECTING, "requested to join: "+desc);
   112             }
   118             }
   113         }
   119         }
   114         
   120         
   115         public Lm.HandlerResult muc_handler(Lm.MessageHandler handler, Lm.Connection connection, Lm.Message message) {
   121         public Lm.HandlerResult muc_handler(Lm.MessageHandler handler, Lm.Connection connection, Lm.Message message) {
   116             var node = message.node;
   122             var node = message.node;
   121                 switch (type) {
   127                 switch (type) {
   122                     case null:
   128                     case null:
   123                     case "unavailable":
   129                     case "unavailable":
   124                         if (from.length==2) {
   130                         if (from.length==2) {
   125                             var statuses = new Gee.HashSet<int>();
   131                             var statuses = new Gee.HashSet<int>();
   126 
   132                             var occupant = occupants[from[1]];
   127                             var x = node.get_child("x");
   133                             var affil = Affiliation.NONE;
   128                             var child = x!=null ? x.children : null;
   134                             var role = Role.NONE;
   129                             while (child != null) {
   135                             string prs_nick = null;
   130                                 stdout.printf("Child %s %s\n", child.name, child.get_attribute("code"));
   136 
   131                                 if (child.name == "status") {
   137                             var chn = node.children;
   132                                     var code = child.get_attribute("code");
   138                             while (chn != null) {
   133                                     if (code!=null)
   139                                 switch (chn.name) {
   134                                         statuses.add(code.to_int());
   140                                     
       
   141                                     case "x":
       
   142                                         switch (chn.get_attribute("xmlns")) {
       
   143                                             case NS_MUC_USER:
       
   144                                                 var child = chn.children;
       
   145                                                 while (child != null) {
       
   146                                                     stdout.printf("Child %s %s\n", child.name, child.get_attribute("code"));
       
   147                                                     if (child.name == "status") {
       
   148                                                         var code = child.get_attribute("code");
       
   149                                                         if (code!=null)
       
   150                                                             statuses.add(code.to_int());
       
   151                                                     }
       
   152                                                     child = child.next;
       
   153                                                 }
       
   154                                                 var item = chn.get_child("item");
       
   155                                                 if (item == null)
       
   156                                                     log("Muc", LogLevelFlags.LEVEL_ERROR, "Your MUC server is shit. No role and affiliation info in presences: %s", node.to_string());
       
   157                                                 affil = affil_from_string(item.get_attribute("affiliation"));
       
   158                                                 role = role_from_string(item.get_attribute("role")); 
       
   159                                                 prs_nick = item.get_attribute("nick");
       
   160                                                 break;
       
   161                                         }
       
   162                                         break;
   135                                 }
   163                                 }
   136                                 child = child.next;
   164                                 chn = chn.next;
   137                             }
   165                             }
   138 
   166 
   139                             if (statuses.contains(110) && (_state == State.CONNECTED))
   167                             if (statuses.contains(110) && (_state == State.CONNECTED))
   140                                 log("Muc", LogLevelFlags.LEVEL_INFO, "Joined a room which I was already in: %s", from[0]);
   168                                 log("Muc", LogLevelFlags.LEVEL_INFO, "Joined a room which I was already in: %s", from[0]);
   141 
   169 
   142                             var occupant = occupants[from[1]];
       
   143                             var item = x != null ? x.get_child("item") : null;
       
   144                             var affil = Affiliation.NONE;
       
   145                             var role = Role.NONE;
       
   146                             string nick = null;
       
   147                             if (item != null) {
       
   148                                 affil = affil_from_string(item.get_attribute("affiliation"));
       
   149                                 role = role_from_string(item.get_attribute("role")); 
       
   150                                 nick = item.get_attribute("nick");
       
   151                             } else
       
   152                                 log("Muc", LogLevelFlags.LEVEL_ERROR, "Your MUC server is shit. No role and affiliation info in presences: %s", node.to_string());
       
   153                             if (occupant != null) {
   170                             if (occupant != null) {
   154                                 if (role == Role.NONE) {
   171                                 if (role == Role.NONE) {
   155                                     occupants.unset(from[1]);
   172                                     occupants.unset(from[1]);
   156                                     log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has parted.", this.jid, from[1]);
   173                                     log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has parted.", this.jid, from[1]);
   157                                     module.on_part(this, occupant);
   174                                     module.on_part(this, occupant);
   158                                     if (from[1]==this.nick) {
   175                                     if (occupant.isme) {
   159                                         _change_state(State.DISCONNECTED, "we became unavailable");
   176                                         _change_state(State.DISCONNECTED, "we became unavailable");
   160                                         occupants.clear();
       
   161                                     }
   177                                     }
   162                                 } else {
   178                                 } else {
   163                                     if (affil != occupant.affil) {
   179                                     if (affil != occupant.affil) {
   164                                         var prev = occupant.affil;
   180                                         var prev = occupant.affil;
   165                                         occupant.affil = affil;
   181                                         occupant.affil = affil;
   168                                     if (role != occupant.role) {
   184                                     if (role != occupant.role) {
   169                                         var prev = occupant.role;
   185                                         var prev = occupant.role;
   170                                         occupant.role = role;
   186                                         occupant.role = role;
   171                                         module.on_role(this, occupant, prev);
   187                                         module.on_role(this, occupant, prev);
   172                                     }
   188                                     }
   173                                     if (statuses.contains(303) && (nick!=null)) {
   189                                     if (statuses.contains(303) && (prs_nick!=null)) {
   174                                         occupants[nick] = occupant;
   190                                         occupants[prs_nick] = occupant;
   175                                         occupants.unset(from[1]);
   191                                         occupants.unset(from[1]);
   176                                         occupant.nick = nick;
   192                                         occupant.nick = prs_nick;
   177                                         module.on_nick(this, occupant, from[1]);
   193                                         module.on_nick(this, occupant, from[1]);
   178                                     }
   194                                     }
   179                                 }
   195                                 }
   180                             } else {
   196                             } else {
   181                                 assert( role!= Role.NONE);
   197                                 assert( role!= Role.NONE);
   184                                 occupant.affil = affil;
   200                                 occupant.affil = affil;
   185                                 occupant.nick = from[1];
   201                                 occupant.nick = from[1];
   186                                 occupants[from[1]] = occupant;
   202                                 occupants[from[1]] = occupant;
   187                                 occupant.isme = statuses.contains(110);
   203                                 occupant.isme = statuses.contains(110);
   188                                 log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has joined as %s/%s.", this.jid, from[1], affil.to_string(), role.to_string());
   204                                 log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has joined as %s/%s.", this.jid, from[1], affil.to_string(), role.to_string());
   189                                 if (statuses.contains(110))
   205                                 if (statuses.contains(110)) {
       
   206                                     this.nick = from[1];
   190                                     _change_state(State.CONNECTED, "got own presence. we are "+this.nick);
   207                                     _change_state(State.CONNECTED, "got own presence. we are "+this.nick);
       
   208                                 }
   191                                 module.on_join(this, occupant);
   209                                 module.on_join(this, occupant);
   192                             }
   210                             }
   193                             
   211                             
   194                             stdout.printf("User list: ");
   212                             stdout.printf("User list: ");
   195                             foreach (var a in occupants.keys)
   213                             foreach (var a in occupants.keys)
   212                 if (subj != null) {
   230                 if (subj != null) {
   213                     if (subj != this.subject) {
   231                     if (subj != this.subject) {
   214                         this.subject = subj;
   232                         this.subject = subj;
   215                         module.on_subject(this);
   233                         module.on_subject(this);
   216                     }
   234                     }
       
   235                 } else {
       
   236                     var occupant = (from.length > 1) ? occupants[from[1]] : null;
       
   237                     var body = node.find_child("body");
       
   238                     module.on_message(this, occupant, node, (body!=null) ? body.get_value() : null);
   217                 }
   239                 }
   218             }
   240             }
   219             return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
   241             return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
   220         }
   242         }
   221         public void part(string desc) {
   243         public void part(string desc) {
       
   244             if (_state != State.DISCONNECTED) {
       
   245                 var prs = new Lm.Message(jid+"/"+desired_nick, Lm.MessageType.PRESENCE);
       
   246                 presence_join_id = jid+"_"+Random.next_int().to_string();
       
   247                 prs.node.set_attribute("type","unavailable");
       
   248                 prs.node.add_child("x",null).set_attribute("xmlns","http://jabber.org/protocol/muc");
       
   249                 module.conn.cn.send(prs);
       
   250                 nick = desired_nick;
       
   251             }
       
   252         }
       
   253 
       
   254         public void connection_lost() {
       
   255             _change_state(State.DISCONNECTED, "connection lost");
   222         }
   256         }
   223     }
   257     }
   224 
   258 
   225     public override string name() { return "muc"; }
   259     public override string name() { return "muc"; }
   226 
   260 
   239                     return rooms[froms[0]].muc_handler(handler, connection, message);
   273                     return rooms[froms[0]].muc_handler(handler, connection, message);
   240                 }
   274                 }
   241             }
   275             }
   242             return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
   276             return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
   243         }, null);
   277         }, null);
       
   278         var names = cfg.get_default("muc","mucs","").split(" ");
       
   279         foreach (var name in names) {
       
   280             var room = new Conference(this, name); //, "Ζαλυπα");
       
   281             //room.join("Oh hai");
       
   282             room.enabled = true;
       
   283             this.rooms[room.jid] = room;
       
   284         }
   244         conn.cn.register_message_handler(muc_handler, Lm.MessageType.MESSAGE, Lm.HandlerPriority.NORMAL);
   285         conn.cn.register_message_handler(muc_handler, Lm.MessageType.MESSAGE, Lm.HandlerPriority.NORMAL);
   245         conn.cn.register_message_handler(muc_handler, Lm.MessageType.PRESENCE, Lm.HandlerPriority.NORMAL);
   286         conn.cn.register_message_handler(muc_handler, Lm.MessageType.PRESENCE, Lm.HandlerPriority.NORMAL);
   246         conn.cn.register_message_handler(muc_handler, Lm.MessageType.IQ, Lm.HandlerPriority.NORMAL);
   287         conn.cn.register_message_handler(muc_handler, Lm.MessageType.IQ, Lm.HandlerPriority.NORMAL);
   247         conn.state_changed.connect( (olds, news, desc) => {
   288         conn.state_changed.connect( (olds, news, desc) => {
   248             if (news == Connection.State.CONNECTED) {
   289             switch (news) {
   249                 var room = new Conference(this, "говнохост@conference.blasux.ru"); //, "Ζαλυπα");
   290                 case Connection.State.CONNECTED:
   250                 room.join("Oh hai");
   291                     break;
   251                 this.rooms[room.jid] = room;
   292                 case Connection.State.DISCONNECTED:
       
   293                     foreach(var room in rooms.values)
       
   294                         room.connection_lost();
       
   295                     break;
   252             }
   296             }
   253         });
   297         });
       
   298         conn.check_time.connect( () => {
       
   299             if (conn.state == Connection.State.CONNECTED)
       
   300                 foreach (var room in rooms.values) {
       
   301                     if (room.enabled) {
       
   302                         if (room.state == State.DISCONNECTED)
       
   303                             room.join("Reconnect");
       
   304                     } else {
       
   305                         if (room.state == State.CONNECTED)
       
   306                             room.part("Disabled");
       
   307                     }
       
   308                 }
       
   309         });
   254     }
   310     }
   255 }
   311 }