/* * xmpp_muc.c -- Jabber MUC protocol handling * * Copyright (C) 2008-2010 Frank Zschockelt <mcabber@freakysoft.de> * Copyright (C) 2005-2014 Mikael Berthe <mikael@lilotux.net> * Copyright (C) 2010 Myhailo Danylenko <isbear@ukrpost.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, see <http://www.gnu.org/licenses/>. */#include<string.h>#include<stdlib.h>#include"xmpp_helper.h"#include"xmpp_iq.h"#include"xmpp_muc.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"externenumimstatusmystatus;externgchar*mystatusmsg;staticGSList*invitations=NULL;staticvoiddecline_invitation(event_muc_invitation*invitation,constchar*reason){// cut and paste from xmpp_room_inviteLmMessage*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",NS_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);}voiddestroy_event_muc_invitation(event_muc_invitation*invitation){invitations=g_slist_remove(invitations,invitation);g_free(invitation->to);g_free(invitation->from);g_free(invitation->passwd);g_free(invitation->reason);g_free(invitation->evid);g_free(invitation);}// invitation event handler// TODO: if event is accepted, check if other events to the same room exist and// destroy them? (need invitation registry list for that)staticgbooleanevscallback_invitation(guintevcontext,constchar*arg,gpointeruserdata){event_muc_invitation*invitation=userdata;// Sanity checkif(G_UNLIKELY(!invitation)){// Shouldn't happen.scr_LogPrint(LPRINT_LOGNORM,"Error in evs callback.");returnFALSE;}if(evcontext==EVS_CONTEXT_TIMEOUT){scr_LogPrint(LPRINT_LOGNORM,"Invitation event %s timed out, cancelled.",invitation->to);returnFALSE;}if(evcontext==EVS_CONTEXT_CANCEL){scr_LogPrint(LPRINT_LOGNORM,"Invitation event %s cancelled.",invitation->to);returnFALSE;}if(!(evcontext==EVS_CONTEXT_ACCEPT||evcontext==EVS_CONTEXT_REJECT))returnFALSE;// Ok, let's work nowif(evcontext==EVS_CONTEXT_ACCEPT){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);if(invitation->reply)decline_invitation(invitation,arg);}returnFALSE;}// Join a MUC roomvoidxmpp_room_join(constchar*room,constchar*nickname,constchar*passwd){LmMessage*x;LmMessageNode*y;gchar*roomid;GSList*room_elt;if(!xmpp_is_online()||!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 existif(!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 hereif(!buddy_getinsideroom(room_elt->data)){// We're trying to enter a roombuddy_setnickname(room_elt->data,nickname);}// Send the XML requestx=lm_message_new_presence(mystatus,roomid,mystatusmsg);xmpp_insert_entity_capabilities(x->node,mystatus);// Entity Caps (XEP-0115)y=lm_message_node_add_child(x->node,"x",NULL);lm_message_node_set_attribute(y,"xmlns",NS_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.voidxmpp_room_invite(constchar*room,constchar*fjid,constchar*reason){LmMessage*msg;LmMessageNode*x,*y;if(!xmpp_is_online()||!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",NS_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);}intxmpp_room_setattrib(constchar*roomid,constchar*fjid,constchar*nick,structrole_affilra,constchar*reason){LmMessage*iq;LmMessageHandler*handler;LmMessageNode*query,*x;if(!xmpp_is_online()||!roomid)return1;if(!fjid&&!nick)return1;if(check_jid_syntax((char*)roomid)){scr_LogPrint(LPRINT_NORMAL,"<%s> is not a valid Jabber id",roomid);return1;}if(fjid&&check_jid_syntax((char*)fjid)){scr_LogPrint(LPRINT_NORMAL,"<%s> is not a valid Jabber id",fjid);return1;}if(ra.type==type_affil&&ra.val.affil==affil_outcast&&!fjid)return1;// 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",NS_MUC_ADMIN);x=lm_message_node_add_child(query,"item",NULL);if(fjid){lm_message_node_set_attribute(x,"jid",fjid);}else{// nicknamelm_message_node_set_attribute(x,"nick",nick);}if(ra.type==type_affil)lm_message_node_set_attribute(x,"affiliation",straffil[ra.val.affil]);elseif(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);handler=lm_message_handler_new(handle_iq_dummy,NULL,FALSE);lm_connection_send_with_reply(lconnection,iq,handler,NULL);lm_message_handler_unref(handler);lm_message_unref(iq);return0;}// Unlock a MUC room// room syntax: "room@server"voidxmpp_room_unlock(constchar*room){LmMessageNode*node;LmMessageHandler*handler;LmMessage*iq;if(!xmpp_is_online()||!room)return;iq=lm_message_new_with_sub_type(room,LM_MESSAGE_TYPE_IQ,LM_MESSAGE_SUB_TYPE_SET);node=lm_message_node_add_child(iq->node,"query",NULL);lm_message_node_set_attribute(node,"xmlns",NS_MUC_OWNER);node=lm_message_node_add_child(node,"x",NULL);lm_message_node_set_attributes(node,"xmlns","jabber:x:data","type","submit",NULL);handler=lm_message_handler_new(handle_iq_dummy,NULL,FALSE);lm_connection_send_with_reply(lconnection,iq,handler,NULL);lm_message_handler_unref(handler);lm_message_unref(iq);}// Destroy a MUC room// room syntax: "room@server"voidxmpp_room_destroy(constchar*room,constchar*venue,constchar*reason){LmMessage*iq;LmMessageHandler*handler;LmMessageNode*query,*x;if(!xmpp_is_online()||!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",NS_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);handler=lm_message_handler_new(handle_iq_dummy,NULL,FALSE);lm_connection_send_with_reply(lconnection,iq,handler,NULL);lm_message_handler_unref(handler);lm_message_unref(iq);}// muc_get_item_info(...)// Get room member's information from xmldata.// The variables must be initialized before calling this function,// because they are not touched if the relevant information is missing.// Note that *actor should be freed by the caller.staticvoidmuc_get_item_info(constchar*from,LmMessageNode*xmldata,enumimrole*mbrole,enumimaffiliation*mbaffil,constchar**mbjid,constchar**mbnick,char**actor,constchar**reason){LmMessageNode*y,*z;constchar*p,*actorjid,*actornick;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;elseif(!strcmp(p,"admin"))*mbaffil=affil_admin;elseif(!strcmp(p,"member"))*mbaffil=affil_member;elseif(!strcmp(p,"outcast"))*mbaffil=affil_outcast;elseif(!strcmp(p,"none"))*mbaffil=affil_none;elsescr_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;elseif(!strcmp(p,"participant"))*mbrole=role_participant;elseif(!strcmp(p,"visitor"))*mbrole=role_visitor;elseif(!strcmp(p,"none"))*mbrole=role_none;elsescr_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 tagsz=lm_message_node_find_child(y,"actor");if(z){actornick=lm_message_node_get_attribute(z,"nick");actorjid=lm_message_node_get_attribute(z,"jid");if(actorjid){if(actornick){// We have both the actor's jid and nick*actor=g_strdup_printf("%s <%s>",actornick,actorjid);}else{*actor=g_strdup(actorjid);// jid only}}elseif(!actorjid&&actornick){// We only have the nickname*actor=g_strdup(actornick);}}*reason=lm_message_node_get_child_value(y,"reason");if(*reason&&!**reason)*reason=NULL;}// 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).staticboolmuc_handle_join(constGSList*room_elt,constchar*rname,constchar*roomjid,constchar*ournick,enumroom_printstatusprintstatus,time_tusttime,intlog_muc_conf,enumroom_autowhoisautowhois,constchar*mbjid){boolnew_member=FALSE;// True if somebody else joins the room (not us)gchar*nickjid;gchar*mbuf;enumroom_flagjoinsflagjoins;char*tmp=NULL;intprintjid;printjid=settings_opt_get_int("muc_print_jid");if(mbjid&&autowhois==autowhois_off&&printjid){if(printjid==1)tmp=strchr(mbjid,JID_RESOURCE_SEPARATOR);if(tmp)*tmp='\0';nickjid=g_strdup_printf("%s <%s>",rname,mbjid);if(tmp)*tmp=JID_RESOURCE_SEPARATOR;}else{nickjid=g_strdup(rname);}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 memberbuddy_setinsideroom(room_elt->data,TRUE);// Set the message flag unless we're already in the room buffer windowscr_setmsgflag_if_needed(roomjid,FALSE);// Add a message to the tracelog filembuf=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 memberif(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",nickjid);elsembuf=NULL;new_member=TRUE;}}else{mbuf=NULL;if(strcmp(ournick,rname)){if(printstatus!=status_none)mbuf=g_strdup_printf("%s has joined",nickjid);new_member=TRUE;}}g_free(nickjid);if(mbuf){guintmsgflags=HBB_PREFIX_INFO;flagjoins=buddy_getflagjoins(room_elt->data);if(flagjoins==flagjoins_default&&!settings_opt_get_int("muc_flag_joins"))flagjoins=flagjoins_none;if(flagjoins==flagjoins_none)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);}returnnew_member;}voidhandle_muc_presence(constchar*from,LmMessageNode*xmldata,constchar*roomjid,constchar*rname,enumimstatusust,constchar*ustmsg,time_tusttime,charbpprio){char*mbuf;constchar*ournick;enumimrolembrole=role_none;enumimaffiliationmbaffil=affil_none;enumroom_printstatusprintstatus;enumroom_autowhoisautowhois;enumroom_flagjoinsflagjoins;constchar*mbjid=NULL,*mbnick=NULL;constchar*reason=NULL;char*actor=NULL;boolnew_member=FALSE;// True if somebody else joins the room (not us)boolour_presence=FALSE;// True if this presence is from us (i.e. bears// code 110)guintstatuscode=0;guintnickchange=0;GSList*room_elt;intlog_muc_conf;guintmsgflags;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 informationmuc_get_item_info(from,xmldata,&mbrole,&mbaffil,&mbjid,&mbnick,&actor,&reason);// Get our room nicknameournick=buddy_getnickname(room_elt->data);if(!ournick){// It shouldn't happen, probably a server issueconstgcharmsg[]="Unexpected groupchat packet!";scr_LogPrint(LPRINT_LOGNORM,msg);scr_WriteIncomingMessage(roomjid,msg,0,HBB_PREFIX_INFO,0);// Send back an unavailable packetxmpp_setstatus(offline,roomjid,"",TRUE);scr_update_roster();return;}#define SETSTATUSCODE(VALUE) \{ \ if (G_UNLIKELY(statuscode)) \ scr_LogPrint(LPRINT_DEBUG, "handle_muc_presence: WARNING: " \ "replacing status code %u with %u.", statuscode, VALUE); \ statuscode = VALUE; \}{// Get the status codeLmMessageNode*node;for(node=xmldata->children;node;node=node->next){if(!g_strcmp0(node->name,"status")){constchar*codestr=lm_message_node_get_attribute(node,"code");if(codestr){constchar*mesg=NULL;switch(atoi(codestr)){// initialcase100:mesg="The room is not anonymous.";break;case110:// It is our presenceour_presence=TRUE;break;// initialcase170:mesg="The room is logged.";break;// initialcase201:// Room createdSETSTATUSCODE(201);break;// initialcase210:// Your nick change (on join)// FIXME: print nickmesg="The room has changed your nick!";buddy_setnickname(room_elt->data,rname);ournick=rname;break;case301:// User bannedSETSTATUSCODE(301);break;case303:// Nick changeSETSTATUSCODE(303);break;case307:// User kickedSETSTATUSCODE(307);break;// XXX (next three)case321:mesg="User leaves room due to affilation change.";break;case322:mesg="User leaves room, as room is only for members now.";break;case332:mesg="User leaves room due to system shutdown.";break;default:scr_LogPrint(LPRINT_DEBUG,"handle_muc_presence: Unknown MUC status code: %s.",codestr);break;}if(mesg){scr_WriteIncomingMessage(roomjid,mesg,usttime,HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG,0);if(log_muc_conf)hlog_write_message(roomjid,0,-1,mesg);}}}}}#undef SETSTATUSCODEif(!our_presence)if(ournick&&!strcmp(ournick,rname))our_presence=TRUE;// Get the room's "print_status" settingsprintstatus=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 configif(statuscode==201)xmpp_room_unlock(roomjid);// Check for nickname changeif(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(our_presence)buddy_setnickname(room_elt->data,mbnick);nickchange=TRUE;}autowhois=buddy_getautowhois(room_elt->data);if(autowhois==autowhois_default)autowhois=(settings_opt_get_int("muc_auto_whois")?autowhois_on:autowhois_off);// Check for departure/arrivalif(statuscode!=303&&ust==offline){// Somebody is leavingenum{leave=0,kick,ban}how=leave;if(statuscode==307)how=kick;elseif(statuscode==301)how=ban;// If this is a leave, check if it is ourselfif(our_presence){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_update_chat_status(FALSE);scr_update_roster();}// The message depends on _who_ left, and _how_if(how){gchar*mbuf_end;gchar*reason_msg=NULL;// Forced leaveif(actor){mbuf_end=g_strdup_printf("%s from %s by %s",(how==ban?"banned":"kicked"),roomjid,actor);}else{mbuf_end=g_strdup_printf("%s from %s",(how==ban?"banned":"kicked"),roomjid);}if(reason)reason_msg=g_strdup_printf("\nReason: %s",reason);if(our_presence)mbuf=g_strdup_printf("You have been %s%s",mbuf_end,reason_msg?reason_msg:"");elsembuf=g_strdup_printf("%s has been %s%s",rname,mbuf_end,reason_msg?reason_msg:"");g_free(reason_msg);g_free(mbuf_end);}else{// Natural leaveif(our_presence){LmMessageNode*destroynode=lm_message_node_find_child(xmldata,"destroy");if(destroynode){reason=lm_message_node_get_child_value(destroynode,"reason");if(reason&&*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);elsembuf=g_strdup_printf("%s has left",rname);}}}g_free(actor);// Display the mbuf message if we're concerned// or if the print_status isn't set to none.if(our_presence||printstatus!=status_none){msgflags=HBB_PREFIX_INFO;flagjoins=buddy_getflagjoins(room_elt->data);if(flagjoins==flagjoins_default&&settings_opt_get_int("muc_flag_joins")==2)flagjoins=flagjoins_all;if(!our_presence&&flagjoins!=flagjoins_all)msgflags|=HBB_PREFIX_NOFLAG;//silent message if someone else joins, and we care about noonescr_WriteIncomingMessage(roomjid,mbuf,usttime,msgflags,0);}if(log_muc_conf)hlog_write_message(roomjid,0,-1,mbuf);if(our_presence){scr_LogPrint(LPRINT_LOGNORM,"%s",mbuf);g_free(mbuf);return;}g_free(mbuf);}else{enumimstatusold_ust=buddy_getstatus(room_elt->data,rname);if(old_ust==offline&&ust!=offline){// Somebody is joiningnew_member=muc_handle_join(room_elt,rname,roomjid,ournick,printstatus,usttime,log_muc_conf,autowhois,mbjid);}else{// This is a simple member status changeif(printstatus==status_all&&!nickchange){constchar*old_ustmsg=buddy_getstatusmsg(room_elt->data,rname);if(old_ust!=ust||g_strcmp0(old_ustmsg,ustmsg)){mbuf=g_strdup_printf("%s [%c>%c] %s",rname,imstatus2char[old_ust],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 statusroster_setstatus(roomjid,rname,bpprio,ust,ustmsg,usttime,mbrole,mbaffil,mbjid);if(new_member&&autowhois==autowhois_on){cmd_room_whois(room_elt->data,rname,FALSE);}scr_update_roster();}voidroompresence(gpointerroom,void*presencedata){constchar*bjid;constchar*nickname;char*to;structT_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, reply)// 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.voidgot_invite(constchar*from,constchar*to,constchar*reason,constchar*passwd,gbooleanreply){GString*sbuf;char*barejid;GSList*room_elt;sbuf=g_string_new("");if(reason&&reason[0]){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);{// remove any equal older invitesGSList*iel=invitations;while(iel){event_muc_invitation*invitation=iel->data;iel=iel->next;if(!g_strcmp0(to,invitation->to)&&!g_strcmp0(passwd,invitation->passwd)){// found a previous invitation// We keep the old one, unless the current one is better and allows us// to send a reply.if(!reply||invitation->reply){g_free(barejid);return;}scr_LogPrint(LPRINT_DEBUG,"Destroying previous invitation event %s.",invitation->evid);evs_del(invitation->evid);}}}{// create eventconstchar*id;char*desc=g_strdup_printf("<%s> invites you to %s",from,to);event_muc_invitation*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);invitation->reply=reply;invitation->evid=NULL;invitations=g_slist_append(invitations,invitation);id=evs_new(desc,NULL,0,evscallback_invitation,invitation,(GDestroyNotify)destroy_event_muc_invitation);g_free(desc);if(id){invitation->evid=g_strdup(id);g_string_printf(sbuf,"Please use /event %s accept|reject",id);}elseg_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 rosterbarejid=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)voidgot_muc_message(constchar*from,LmMessageNode*x,time_ttimestamp){LmMessageNode*node;// invitationnode=lm_message_node_get_child(x,"invite");if(node){constchar*invite_from;constchar*reason=NULL;constchar*password=NULL;invite_from=lm_message_node_get_attribute(node,"from");reason=lm_message_node_get_child_value(node,"reason");password=lm_message_node_get_child_value(node,"password");if(invite_from)got_invite(invite_from,from,reason,password,TRUE);}// declined invitationnode=lm_message_node_get_child(x,"decline");if(node){constchar*decline_from=lm_message_node_get_attribute(node,"from");constchar*reason=lm_message_node_get_child_value(node,"reason");if(decline_from){if(reason&&reason[0])scr_LogPrint(LPRINT_LOGNORM,"<%s> declined your invitation: %s.",from,reason);elsescr_LogPrint(LPRINT_LOGNORM,"<%s> declined your invitation.",from);}}// status codesfor(node=x->children;node;node=node->next){if(!g_strcmp0(node->name,"status")){constchar*codestr=lm_message_node_get_attribute(node,"code");if(codestr){constchar*mesg=NULL;switch(atoi(codestr)){// initialcase100:mesg="The room is not anonymous.";break;case101:mesg="Your affilation has changed while absent.";break;case102:mesg="The room shows unavailable members.";break;case103:mesg="The room does not show unavailable members.";break;case104:mesg="The room configuration has changed.";break;case170:mesg="The room is logged.";break;case171:mesg="The room is not logged.";break;case172:mesg="The room is not anonymous.";break;case173:mesg="The room is semi-anonymous.";break;case174:mesg="The room is anonymous.";break;default:scr_LogPrint(LPRINT_DEBUG,"got_muc_message: Unknown MUC status code: %s.",codestr);break;}if(mesg){scr_WriteIncomingMessage(from,mesg,timestamp,HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG,0);if(settings_opt_get_int("log_muc_conf"))hlog_write_message(from,0,-1,mesg);}}}}}/* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */