view mcabber/src/xmpp_muc.c @ 1610:6db9f403f707

Replace 'username' with 'jid' in the configuration file The previous behaviour doesn't make much sense anymore. MCabber does DNS SRV lookups so providing the server name is usually not needed.
author Mikael Berthe <mikael@lilotux.net>
date Sun, 11 Oct 2009 20:06:47 +0200
parents 351427ef0b4b
children 2f6bdfa0cb01
line wrap: on
line source

/*
 * xmpp_muc.c   -- Jabber MUC protocol handling
 *
 * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
 * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include <string.h>
#include <stdlib.h>

#include "xmpp_helper.h"
#include "events.h"
#include "hooks.h"
#include "screen.h"
#include "hbuf.h"
#include "roster.h"
#include "commands.h"
#include "settings.h"
#include "utils.h"
#include "histolog.h"

extern enum imstatus mystatus;
extern gchar *mystatusmsg;

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;
}

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();
}

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)
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
}

/* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */