diff mcabber/src/xmpp_muc.c @ 1598:a087125d8fc8

Replace libjabber with loudmouth
author franky
date Sun, 11 Oct 2009 15:38:32 +0200
parents mcabber/src/jabglue.c@1802b926e3fa
children dcd5d4c75199
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/src/xmpp_muc.c	Sun Oct 11 15:38:32 2009 +0200
@@ -0,0 +1,678 @@
+/* See xmpp.c file for copyright and license details. */
+
+static void decline_invitation(event_muc_invitation *invitation, char *reason)
+{
+  // cut and paste from xmpp_room_invite
+  LmMessage *m;
+  LmMessageNode *x, *y;
+
+  if (!invitation) return;
+  if (!invitation->to || !invitation->from) return;
+
+  m = lm_message_new(invitation->to, LM_MESSAGE_TYPE_MESSAGE);
+
+  x = lm_message_node_add_child(m->node, "x", NULL);
+  lm_message_node_set_attribute(x, "xmlns",
+                                "http://jabber.org/protocol/muc#user");
+
+  y = lm_message_node_add_child(x, "decline", NULL);
+  lm_message_node_set_attribute(y, "to", invitation->from);
+
+  if (reason)
+    lm_message_node_add_child(y, "reason", reason);
+
+  lm_connection_send(lconnection, m, NULL);
+  lm_message_unref(m);
+}
+
+static int evscallback_invitation(eviqs *evp, guint evcontext)
+{
+  event_muc_invitation *invitation = evp->data;
+
+  // Sanity check
+  if (!invitation) {
+    // Shouldn't happen.
+    scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
+    return 0;
+  }
+
+  if (evcontext == EVS_CONTEXT_TIMEOUT) {
+    scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.", evp->id);
+    goto evscallback_invitation_free;
+  }
+  if (evcontext == EVS_CONTEXT_CANCEL) {
+    scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id);
+    goto evscallback_invitation_free;
+  }
+  if (!(evcontext & EVS_CONTEXT_USER))
+    goto evscallback_invitation_free;
+  // Ok, let's work now.
+  // evcontext: 0, 1 == reject, accept
+
+  if (evcontext & ~EVS_CONTEXT_USER) {
+    char *nickname = default_muc_nickname(invitation->to);
+    xmpp_room_join(invitation->to, nickname, invitation->passwd);
+    g_free(nickname);
+  } else {
+    scr_LogPrint(LPRINT_LOGNORM, "Invitation to %s refused.", invitation->to);
+    decline_invitation(invitation, NULL);
+  }
+
+evscallback_invitation_free:
+  g_free(invitation->to);
+  g_free(invitation->from);
+  g_free(invitation->passwd);
+  g_free(invitation->reason);
+  g_free(invitation);
+  evp->data = NULL;
+  return 0;
+}
+
+// Join a MUC room
+void xmpp_room_join(const char *room, const char *nickname, const char *passwd)
+{
+  LmMessage *x;
+  LmMessageNode *y;
+  gchar *roomid;
+  GSList *room_elt;
+
+  if (!lm_connection_is_authenticated(lconnection) || !room) return;
+  if (!nickname)        return;
+
+  roomid = g_strdup_printf("%s/%s", room, nickname);
+  if (check_jid_syntax(roomid)) {
+    scr_LogPrint(LPRINT_NORMAL, "<%s/%s> is not a valid Jabber room", room,
+                 nickname);
+    g_free(roomid);
+    return;
+  }
+
+  room_elt = roster_find(room, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
+  // Add room if it doesn't already exist
+  if (!room_elt) {
+    room_elt = roster_add_user(room, NULL, NULL, ROSTER_TYPE_ROOM,
+                               sub_none, -1);
+  } else {
+    // Make sure this is a room (it can be a conversion user->room)
+    buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+  }
+  // If insideroom is TRUE, this is a nickname change and we don't care here
+  if (!buddy_getinsideroom(room_elt->data)) {
+    // We're trying to enter a room
+    buddy_setnickname(room_elt->data, nickname);
+  }
+
+  // Send the XML request
+  x = lm_message_new(roomid, LM_MESSAGE_TYPE_PRESENCE);
+
+  x = lm_message_new_presence(mystatus, roomid, mystatusmsg);
+  y = lm_message_node_add_child(x->node, "x", NULL);
+  lm_message_node_set_attribute(y, "xmlns", "http://jabber.org/protocol/muc");
+  if (passwd)
+    lm_message_node_add_child(y, "password", passwd);
+
+  lm_connection_send(lconnection, x, NULL);
+  lm_message_unref(x);
+  g_free(roomid);
+}
+
+// Invite a user to a MUC room
+// room syntax: "room@server"
+// reason can be null.
+void xmpp_room_invite(const char *room, const char *fjid, const char *reason)
+{
+  LmMessage *msg;
+  LmMessageNode *x, *y;
+
+  if (!lm_connection_is_authenticated(lconnection) || !room || !fjid) return;
+
+  msg = lm_message_new(room, LM_MESSAGE_TYPE_MESSAGE);
+
+  x = lm_message_node_add_child(msg->node, "x", NULL);
+  lm_message_node_set_attribute(x, "xmlns",
+                                "http://jabber.org/protocol/muc#user");
+
+  y = lm_message_node_add_child(x, "invite", NULL);
+  lm_message_node_set_attribute(y, "to", fjid);
+
+  if (reason)
+    lm_message_node_add_child(y, "reason", reason);
+
+  lm_connection_send(lconnection, msg, NULL);
+  lm_message_unref(msg);
+}
+
+int xmpp_room_setattrib(const char *roomid, const char *fjid,
+                        const char *nick, struct role_affil ra,
+                        const char *reason)
+{
+  LmMessage *iq;
+  LmMessageNode *query, *x;
+
+  if (!lm_connection_is_authenticated(lconnection) || !roomid) return 1;
+  if (!fjid && !nick) return 1;
+
+  if (check_jid_syntax((char*)roomid)) {
+    scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", roomid);
+    return 1;
+  }
+  if (fjid && check_jid_syntax((char*)fjid)) {
+    scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", fjid);
+    return 1;
+  }
+
+  if (ra.type == type_affil && ra.val.affil == affil_outcast && !fjid)
+    return 1; // Shouldn't happen (jid mandatory when banning)
+
+  iq = lm_message_new_with_sub_type(roomid, LM_MESSAGE_TYPE_IQ,
+                                    LM_MESSAGE_SUB_TYPE_SET);
+  query = lm_message_node_add_child(iq->node, "query", NULL);
+  lm_message_node_set_attribute(query, "xmlns",
+                                "http://jabber.org/protocol/muc#admin");
+  x = lm_message_node_add_child(query, "item", NULL);
+
+  if (fjid) {
+    lm_message_node_set_attribute(x, "jid", fjid);
+  } else { // nickname
+    lm_message_node_set_attribute(x, "nick", nick);
+  }
+
+  if (ra.type == type_affil)
+    lm_message_node_set_attribute(x, "affiliation", straffil[ra.val.affil]);
+  else if (ra.type == type_role)
+    lm_message_node_set_attribute(x, "role", strrole[ra.val.role]);
+
+  if (reason)
+    lm_message_node_add_child(x, "reason", reason);
+
+  lm_connection_send(lconnection, iq, NULL);
+  lm_message_unref(iq);
+
+  return 0;
+}
+
+// Unlock a MUC room
+// room syntax: "room@server"
+void xmpp_room_unlock(const char *room)
+{
+  LmMessageNode *y, *z;
+  LmMessage *iq;
+
+  if (!lm_connection_is_authenticated(lconnection) || !room) return;
+
+  iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ,
+                                    LM_MESSAGE_SUB_TYPE_SET);
+  lm_message_node_set_attribute(iq->node, "xmlns",
+                                "http://jabber.org/protocol/muc#owner");
+
+
+  y = lm_message_node_add_child(iq->node, "query", NULL);
+  z = lm_message_node_add_child(y, "x", NULL);
+  lm_message_node_set_attribute(z, "xmlns", "jabber:x:data");
+  lm_message_node_set_attribute(z, "type", "submit");
+
+  lm_connection_send(lconnection, iq, NULL);
+  lm_message_unref(iq);
+}
+
+// Destroy a MUC room
+// room syntax: "room@server"
+void xmpp_room_destroy(const char *room, const char *venue, const char *reason)
+{
+  LmMessage *iq;
+  LmMessageNode *query, *x;
+
+  if (!lm_connection_is_authenticated(lconnection) || !room) return;
+
+  iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ,
+                                    LM_MESSAGE_SUB_TYPE_SET);
+  query = lm_message_node_add_child(iq->node, "query", NULL);
+  lm_message_node_set_attribute(query, "xmlns",
+                                "http://jabber.org/protocol/muc#owner");
+  x = lm_message_node_add_child(query, "destroy", NULL);
+
+  if (venue && *venue)
+    lm_message_node_set_attribute(x, "jid", venue);
+
+  if (reason)
+    lm_message_node_add_child(x, "reason", reason);
+
+  lm_connection_send(lconnection, iq, NULL);
+  lm_message_unref(iq);
+}
+
+//  muc_get_item_info(...)
+// Get room member's information from xmlndata.
+// The variables must be initialized before calling this function,
+// because they are not touched if the relevant information is missing.
+static void muc_get_item_info(const char *from, LmMessageNode *xmldata,
+                              enum imrole *mbrole, enum imaffiliation *mbaffil,
+                              const char **mbjid, const char **mbnick,
+                              const char **actorjid, const char **reason)
+{
+  LmMessageNode *y, *z;
+  const char *p;
+
+  y = lm_message_node_find_child(xmldata, "item");
+  if (!y)
+    return;
+
+  p = lm_message_node_get_attribute(y, "affiliation");
+  if (p) {
+    if (!strcmp(p, "owner"))        *mbaffil = affil_owner;
+    else if (!strcmp(p, "admin"))   *mbaffil = affil_admin;
+    else if (!strcmp(p, "member"))  *mbaffil = affil_member;
+    else if (!strcmp(p, "outcast")) *mbaffil = affil_outcast;
+    else if (!strcmp(p, "none"))    *mbaffil = affil_none;
+    else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown affiliation \"%s\"",
+                      from, p);
+  }
+  p = lm_message_node_get_attribute(y, "role");
+  if (p) {
+    if (!strcmp(p, "moderator"))        *mbrole = role_moderator;
+    else if (!strcmp(p, "participant")) *mbrole = role_participant;
+    else if (!strcmp(p, "visitor"))     *mbrole = role_visitor;
+    else if (!strcmp(p, "none"))        *mbrole = role_none;
+    else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown role \"%s\"",
+                      from, p);
+  }
+  *mbjid = lm_message_node_get_attribute(y, "jid");
+  *mbnick = lm_message_node_get_attribute(y, "nick");
+  // For kick/ban, there can be actor and reason tags
+  *reason = lm_message_node_get_child_value(y, "reason");
+  z = lm_message_node_find_child(y, "actor");
+  if (z)
+    *actorjid = lm_message_node_get_attribute(z, "jid");
+}
+
+//  muc_handle_join(...)
+// Handle a join event in a MUC room.
+// This function will return the new_member value TRUE if somebody else joins
+// the room (and FALSE if _we_ are joining the room).
+static bool muc_handle_join(const GSList *room_elt, const char *rname,
+                            const char *roomjid, const char *ournick,
+                            enum room_printstatus printstatus,
+                            time_t usttime, int log_muc_conf)
+{
+  bool new_member = FALSE; // True if somebody else joins the room (not us)
+  gchar *mbuf;
+
+  if (!buddy_getinsideroom(room_elt->data)) {
+    // We weren't inside the room yet.  Now we are.
+    // However, this could be a presence packet from another room member
+
+    buddy_setinsideroom(room_elt->data, TRUE);
+    // Set the message flag unless we're already in the room buffer window
+    scr_setmsgflag_if_needed(roomjid, FALSE);
+    // Add a message to the tracelog file
+    mbuf = g_strdup_printf("You have joined %s as \"%s\"", roomjid, ournick);
+    scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
+    g_free(mbuf);
+    mbuf = g_strdup_printf("You have joined as \"%s\"", ournick);
+
+    // The 1st presence message could be for another room member
+    if (strcmp(ournick, rname)) {
+      // Display current mbuf and create a new message for the member
+      // Note: the usttime timestamp is related to the other member,
+      //       so we use 0 here.
+      scr_WriteIncomingMessage(roomjid, mbuf, 0,
+                               HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+      if (log_muc_conf)
+        hlog_write_message(roomjid, 0, -1, mbuf);
+      g_free(mbuf);
+      if (printstatus != status_none)
+        mbuf = g_strdup_printf("%s has joined", rname);
+      else
+        mbuf = NULL;
+      new_member = TRUE;
+    }
+  } else {
+    mbuf = NULL;
+    if (strcmp(ournick, rname)) {
+      if (printstatus != status_none)
+        mbuf = g_strdup_printf("%s has joined", rname);
+      new_member = TRUE;
+    }
+  }
+
+  if (mbuf) {
+    guint msgflags = HBB_PREFIX_INFO;
+    if (!settings_opt_get_int("muc_flag_joins"))
+      msgflags |= HBB_PREFIX_NOFLAG;
+    scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0);
+    if (log_muc_conf)
+      hlog_write_message(roomjid, 0, -1, mbuf);
+    g_free(mbuf);
+  }
+
+  return new_member;
+}
+
+static void handle_muc_presence(const char *from, LmMessageNode *xmldata,
+                                const char *roomjid, const char *rname,
+                                enum imstatus ust, const char *ustmsg,
+                                time_t usttime, char bpprio)
+{
+  LmMessageNode *y;
+  const char *p;
+  char *mbuf;
+  const char *ournick;
+  enum imrole mbrole = role_none;
+  enum imaffiliation mbaffil = affil_none;
+  enum room_printstatus printstatus;
+  enum room_autowhois autowhois;
+  const char *mbjid = NULL, *mbnick = NULL;
+  const char *actorjid = NULL, *reason = NULL;
+  bool new_member = FALSE; // True if somebody else joins the room (not us)
+  guint statuscode = 0;
+  guint nickchange = 0;
+  GSList *room_elt;
+  int log_muc_conf;
+  guint msgflags;
+
+  log_muc_conf = settings_opt_get_int("log_muc_conf");
+
+  room_elt = roster_find(roomjid, jidsearch, 0);
+  if (!room_elt) {
+    // Add room if it doesn't already exist
+    // It shouldn't happen, there is probably something wrong (server or
+    // network issue?)
+    room_elt = roster_add_user(roomjid, NULL, NULL, ROSTER_TYPE_ROOM,
+                               sub_none, -1);
+    scr_LogPrint(LPRINT_LOGNORM, "Strange MUC presence message");
+  } else {
+    // Make sure this is a room (it can be a conversion user->room)
+    buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+  }
+
+  // Get room member's information
+  muc_get_item_info(from, xmldata, &mbrole, &mbaffil, &mbjid, &mbnick,
+                    &actorjid, &reason);
+
+  // Get our room nickname
+  ournick = buddy_getnickname(room_elt->data);
+
+  if (!ournick) {
+    // It shouldn't happen, probably a server issue
+    mbuf = g_strdup_printf("Unexpected groupchat packet!");
+
+    scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
+    scr_WriteIncomingMessage(roomjid, mbuf, 0, HBB_PREFIX_INFO, 0);
+    g_free(mbuf);
+    // Send back an unavailable packet
+    xmpp_setstatus(offline, roomjid, "", TRUE);
+    scr_DrawRoster();
+    return;
+  }
+
+  // Get the status code
+  // 201: a room has been created
+  // 301: the user has been banned from the room
+  // 303: new room nickname
+  // 307: the user has been kicked from the room
+  // 321,322,332: the user has been removed from the room
+  y = lm_message_node_find_child(xmldata, "status");
+  if (y) {
+    p = lm_message_node_get_attribute(y, "code");
+    if (p)
+      statuscode = atoi(p);
+  }
+
+  // Get the room's "print_status" settings
+  printstatus = buddy_getprintstatus(room_elt->data);
+  if (printstatus == status_default) {
+    printstatus = (guint) settings_opt_get_int("muc_print_status");
+    if (printstatus > 3)
+      printstatus = status_default;
+  }
+
+  // A new room has been created; accept MUC default config
+  if (statuscode == 201)
+    xmpp_room_unlock(roomjid);
+
+  // Check for nickname change
+  if (statuscode == 303 && mbnick) {
+    mbuf = g_strdup_printf("%s is now known as %s", rname, mbnick);
+    scr_WriteIncomingMessage(roomjid, mbuf, usttime,
+                             HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+    if (log_muc_conf)
+      hlog_write_message(roomjid, 0, -1, mbuf);
+    g_free(mbuf);
+    buddy_resource_setname(room_elt->data, rname, mbnick);
+    // Maybe it's _our_ nickname...
+    if (ournick && !strcmp(rname, ournick))
+      buddy_setnickname(room_elt->data, mbnick);
+    nickchange = TRUE;
+  }
+
+  // Check for departure/arrival
+  if (!mbnick && ust == offline) {
+    // Somebody is leaving
+    enum { leave=0, kick, ban } how = leave;
+    bool we_left = FALSE;
+
+    if (statuscode == 307)
+      how = kick;
+    else if (statuscode == 301)
+      how = ban;
+
+    // If this is a leave, check if it is ourself
+    if (ournick && !strcmp(rname, ournick)) {
+      we_left = TRUE; // _We_ have left! (kicked, banned, etc.)
+      buddy_setinsideroom(room_elt->data, FALSE);
+      buddy_setnickname(room_elt->data, NULL);
+      buddy_del_all_resources(room_elt->data);
+      buddy_settopic(room_elt->data, NULL);
+      scr_UpdateChatStatus(FALSE);
+      update_roster = TRUE;
+    }
+
+    // The message depends on _who_ left, and _how_
+    if (how) {
+      gchar *mbuf_end;
+      // Forced leave
+      if (actorjid) {
+        mbuf_end = g_strdup_printf("%s from %s by <%s>.\nReason: %s",
+                                   (how == ban ? "banned" : "kicked"),
+                                   roomjid, actorjid, reason);
+      } else {
+        mbuf_end = g_strdup_printf("%s from %s.",
+                                   (how == ban ? "banned" : "kicked"),
+                                   roomjid);
+      }
+      if (we_left)
+        mbuf = g_strdup_printf("You have been %s", mbuf_end);
+      else
+        mbuf = g_strdup_printf("%s has been %s", rname, mbuf_end);
+
+      g_free(mbuf_end);
+    } else {
+      // Natural leave
+      if (we_left) {
+        LmMessageNode *destroynode = lm_message_node_find_child(xmldata,
+                                                                "destroy");
+        if (destroynode) {
+          if ((reason = lm_message_node_get_child_value(destroynode,
+                                                       "reason"))) {
+            mbuf = g_strdup_printf("You have left %s, "
+                                   "the room has been destroyed: %s",
+                                   roomjid, reason);
+          } else {
+            mbuf = g_strdup_printf("You have left %s, "
+                                   "the room has been destroyed", roomjid);
+          }
+        } else {
+          mbuf = g_strdup_printf("You have left %s", roomjid);
+        }
+      } else {
+        if (ust != offline) {
+          // This can happen when a network failure occurs,
+          // this isn't an official leave but the user isn't there anymore.
+          mbuf = g_strdup_printf("%s has disappeared!", rname);
+          ust = offline;
+        } else {
+          if (ustmsg)
+            mbuf = g_strdup_printf("%s has left: %s", rname, ustmsg);
+          else
+            mbuf = g_strdup_printf("%s has left", rname);
+        }
+      }
+    }
+
+    // Display the mbuf message if we're concerned
+    // or if the print_status isn't set to none.
+    if (we_left || printstatus != status_none) {
+      msgflags = HBB_PREFIX_INFO;
+      if (!we_left && settings_opt_get_int("muc_flag_joins") != 2)
+        msgflags |= HBB_PREFIX_NOFLAG;
+      scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0);
+    }
+
+    if (log_muc_conf)
+      hlog_write_message(roomjid, 0, -1, mbuf);
+
+    if (we_left) {
+      scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
+      g_free(mbuf);
+      return;
+    }
+    g_free(mbuf);
+  } else if (buddy_getstatus(room_elt->data, rname) == offline &&
+             ust != offline) {
+    // Somebody is joining
+    new_member = muc_handle_join(room_elt, rname, roomjid, ournick,
+                                 printstatus, usttime, log_muc_conf);
+  } else {
+    // This is a simple member status change
+
+    if (printstatus == status_all && !nickchange) {
+      mbuf = g_strdup_printf("Member status has changed: %s [%c] %s", rname,
+                             imstatus2char[ust], ((ustmsg) ? ustmsg : ""));
+      scr_WriteIncomingMessage(roomjid, mbuf, usttime,
+                               HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+      g_free(mbuf);
+    }
+  }
+
+  // Sanity check, shouldn't happen...
+  if (!rname)
+    return;
+
+  // Update room member status
+  roster_setstatus(roomjid, rname, bpprio, ust, ustmsg, usttime,
+                   mbrole, mbaffil, mbjid);
+
+  autowhois = buddy_getautowhois(room_elt->data);
+  if (autowhois == autowhois_default)
+    autowhois = (settings_opt_get_int("muc_auto_whois") ?
+                 autowhois_on : autowhois_off);
+
+  if (new_member && autowhois == autowhois_on) {
+    // FIXME: This will fail for some UTF-8 nicknames.
+    gchar *joiner_nick = from_utf8(rname);
+    cmd_room_whois(room_elt->data, joiner_nick, FALSE);
+    g_free(joiner_nick);
+  }
+
+  scr_DrawRoster();
+}
+
+static void roompresence(gpointer room, void *presencedata)
+{
+  const char *bjid;
+  const char *nickname;
+  char *to;
+  struct T_presence *pres = presencedata;
+
+  if (!buddy_getinsideroom(room))
+    return;
+
+  bjid = buddy_getjid(room);
+  if (!bjid) return;
+  nickname = buddy_getnickname(room);
+  if (!nickname) return;
+
+  to = g_strdup_printf("%s/%s", bjid, nickname);
+  xmpp_setstatus(pres->st, to, pres->msg, TRUE);
+  g_free(to);
+}
+
+//  got_invite(from, to, reason, passwd)
+// This function should be called when receiving an invitation from user
+// "from", to enter the room "to".  Optional reason and room password can
+// be provided.
+static void got_invite(const char* from, const char *to, const char* reason,
+                       const char* passwd)
+{
+  eviqs *evn;
+  event_muc_invitation *invitation;
+  GString *sbuf;
+  char *barejid;
+  GSList *room_elt;
+
+  sbuf = g_string_new("");
+  if (reason) {
+    g_string_printf(sbuf,
+                    "Received an invitation to <%s>, from <%s>, reason: %s",
+                    to, from, reason);
+  } else {
+    g_string_printf(sbuf, "Received an invitation to <%s>, from <%s>",
+                    to, from);
+  }
+
+  barejid = jidtodisp(from);
+  scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
+  scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
+
+  evn = evs_new(EVS_TYPE_INVITATION, EVS_MAX_TIMEOUT);
+  if (evn) {
+    evn->callback = &evscallback_invitation;
+    invitation = g_new(event_muc_invitation, 1);
+    invitation->to = g_strdup(to);
+    invitation->from = g_strdup(from);
+    invitation->passwd = g_strdup(passwd);
+    invitation->reason = g_strdup(reason);
+    evn->data = invitation;
+    evn->desc = g_strdup_printf("<%s> invites you to %s ", from, to);
+    g_string_printf(sbuf, "Please use /event %s accept|reject", evn->id);
+  } else {
+    g_string_printf(sbuf, "Unable to create a new event!");
+  }
+  scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
+  scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
+  g_string_free(sbuf, TRUE);
+  g_free(barejid);
+
+  // Make sure the MUC room barejid is a room in the roster
+  barejid = jidtodisp(to);
+  room_elt = roster_find(barejid, jidsearch, 0);
+  if (room_elt)
+    buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+
+  g_free(barejid);
+}
+
+
+// Specific MUC message handling (for example invitation processing)
+static void got_muc_message(const char *from, LmMessageNode *x)
+{
+  LmMessageNode *invite = lm_message_node_get_child(x, "invite");
+  if (invite)
+  {
+    const char *invite_from;
+    const char *reason = NULL;
+    const char *password = NULL;
+
+    invite_from = lm_message_node_get_attribute(invite, "from");
+    reason = lm_message_node_get_child_value(invite, "reason");
+    password = lm_message_node_get_child_value(invite, "password");
+    if (invite_from)
+      got_invite(invite_from, from, reason, password);
+  }
+  // TODO
+  // handle status code = 100 ( not anonymous )
+  // handle status code = 170 ( changement de config )
+  // 10.2.1 Notification of Configuration Changes
+  // declined invitation
+}
+