Mercurial > ~mikael > mcabber > hg
comparison mcabber/mcabber/xmpp_muc.c @ 1668:41c26b7d2890
Install mcabber headers
* Change mcabber headers naming scheme
* Move 'src/' -> 'mcabber/'
* Add missing include <mcabber/config.h>'s
* Create and install clean config.h version in 'include/'
* Move "dirty" config.h version to 'mcabber/'
* Add $(top_srcdir) to compiler include path
* Update modules HOWTO
author | Myhailo Danylenko <isbear@ukrpost.net> |
---|---|
date | Mon, 18 Jan 2010 15:36:19 +0200 |
parents | mcabber/src/xmpp_muc.c@1a4890514eb9 |
children | d1e8fb14ce2d |
comparison
equal
deleted
inserted
replaced
1667:8af0e0ad20ad | 1668:41c26b7d2890 |
---|---|
1 /* | |
2 * xmpp_muc.c -- Jabber MUC protocol handling | |
3 * | |
4 * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de> | |
5 * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net> | |
6 * | |
7 * This program is free software; you can redistribute it and/or modify | |
8 * it under the terms of the GNU General Public License as published by | |
9 * the Free Software Foundation; either version 2 of the License, or (at | |
10 * your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU General Public License | |
18 * along with this program; if not, write to the Free Software | |
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
20 * USA | |
21 */ | |
22 | |
23 #include <string.h> | |
24 #include <stdlib.h> | |
25 | |
26 #include "xmpp_helper.h" | |
27 #include "events.h" | |
28 #include "hooks.h" | |
29 #include "screen.h" | |
30 #include "hbuf.h" | |
31 #include "roster.h" | |
32 #include "commands.h" | |
33 #include "settings.h" | |
34 #include "utils.h" | |
35 #include "histolog.h" | |
36 | |
37 extern enum imstatus mystatus; | |
38 extern gchar *mystatusmsg; | |
39 | |
40 static void decline_invitation(event_muc_invitation *invitation, char *reason) | |
41 { | |
42 // cut and paste from xmpp_room_invite | |
43 LmMessage *m; | |
44 LmMessageNode *x, *y; | |
45 | |
46 if (!invitation) return; | |
47 if (!invitation->to || !invitation->from) return; | |
48 | |
49 m = lm_message_new(invitation->to, LM_MESSAGE_TYPE_MESSAGE); | |
50 | |
51 x = lm_message_node_add_child(m->node, "x", NULL); | |
52 lm_message_node_set_attribute(x, "xmlns", | |
53 "http://jabber.org/protocol/muc#user"); | |
54 | |
55 y = lm_message_node_add_child(x, "decline", NULL); | |
56 lm_message_node_set_attribute(y, "to", invitation->from); | |
57 | |
58 if (reason) | |
59 lm_message_node_add_child(y, "reason", reason); | |
60 | |
61 lm_connection_send(lconnection, m, NULL); | |
62 lm_message_unref(m); | |
63 } | |
64 | |
65 static int evscallback_invitation(eviqs *evp, guint evcontext) | |
66 { | |
67 event_muc_invitation *invitation = evp->data; | |
68 | |
69 // Sanity check | |
70 if (!invitation) { | |
71 // Shouldn't happen. | |
72 scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback."); | |
73 return 0; | |
74 } | |
75 | |
76 if (evcontext == EVS_CONTEXT_TIMEOUT) { | |
77 scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.", evp->id); | |
78 goto evscallback_invitation_free; | |
79 } | |
80 if (evcontext == EVS_CONTEXT_CANCEL) { | |
81 scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id); | |
82 goto evscallback_invitation_free; | |
83 } | |
84 if (!(evcontext & EVS_CONTEXT_USER)) | |
85 goto evscallback_invitation_free; | |
86 // Ok, let's work now. | |
87 // evcontext: 0, 1 == reject, accept | |
88 | |
89 if (evcontext & ~EVS_CONTEXT_USER) { | |
90 char *nickname = default_muc_nickname(invitation->to); | |
91 xmpp_room_join(invitation->to, nickname, invitation->passwd); | |
92 g_free(nickname); | |
93 } else { | |
94 scr_LogPrint(LPRINT_LOGNORM, "Invitation to %s refused.", invitation->to); | |
95 decline_invitation(invitation, NULL); | |
96 } | |
97 | |
98 evscallback_invitation_free: | |
99 g_free(invitation->to); | |
100 g_free(invitation->from); | |
101 g_free(invitation->passwd); | |
102 g_free(invitation->reason); | |
103 g_free(invitation); | |
104 evp->data = NULL; | |
105 return 0; | |
106 } | |
107 | |
108 // Join a MUC room | |
109 void xmpp_room_join(const char *room, const char *nickname, const char *passwd) | |
110 { | |
111 LmMessage *x; | |
112 LmMessageNode *y; | |
113 gchar *roomid; | |
114 GSList *room_elt; | |
115 | |
116 if (!lm_connection_is_authenticated(lconnection) || !room) return; | |
117 if (!nickname) return; | |
118 | |
119 roomid = g_strdup_printf("%s/%s", room, nickname); | |
120 if (check_jid_syntax(roomid)) { | |
121 scr_LogPrint(LPRINT_NORMAL, "<%s/%s> is not a valid Jabber room", room, | |
122 nickname); | |
123 g_free(roomid); | |
124 return; | |
125 } | |
126 | |
127 room_elt = roster_find(room, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_ROOM); | |
128 // Add room if it doesn't already exist | |
129 if (!room_elt) { | |
130 room_elt = roster_add_user(room, NULL, NULL, ROSTER_TYPE_ROOM, | |
131 sub_none, -1); | |
132 } else { | |
133 // Make sure this is a room (it can be a conversion user->room) | |
134 buddy_settype(room_elt->data, ROSTER_TYPE_ROOM); | |
135 } | |
136 // If insideroom is TRUE, this is a nickname change and we don't care here | |
137 if (!buddy_getinsideroom(room_elt->data)) { | |
138 // We're trying to enter a room | |
139 buddy_setnickname(room_elt->data, nickname); | |
140 } | |
141 | |
142 // Send the XML request | |
143 lm_message_new(roomid, LM_MESSAGE_TYPE_PRESENCE); | |
144 | |
145 x = lm_message_new_presence(mystatus, roomid, mystatusmsg); | |
146 y = lm_message_node_add_child(x->node, "x", NULL); | |
147 lm_message_node_set_attribute(y, "xmlns", "http://jabber.org/protocol/muc"); | |
148 if (passwd) | |
149 lm_message_node_add_child(y, "password", passwd); | |
150 | |
151 lm_connection_send(lconnection, x, NULL); | |
152 lm_message_unref(x); | |
153 g_free(roomid); | |
154 } | |
155 | |
156 // Invite a user to a MUC room | |
157 // room syntax: "room@server" | |
158 // reason can be null. | |
159 void xmpp_room_invite(const char *room, const char *fjid, const char *reason) | |
160 { | |
161 LmMessage *msg; | |
162 LmMessageNode *x, *y; | |
163 | |
164 if (!lm_connection_is_authenticated(lconnection) || !room || !fjid) return; | |
165 | |
166 msg = lm_message_new(room, LM_MESSAGE_TYPE_MESSAGE); | |
167 | |
168 x = lm_message_node_add_child(msg->node, "x", NULL); | |
169 lm_message_node_set_attribute(x, "xmlns", | |
170 "http://jabber.org/protocol/muc#user"); | |
171 | |
172 y = lm_message_node_add_child(x, "invite", NULL); | |
173 lm_message_node_set_attribute(y, "to", fjid); | |
174 | |
175 if (reason) | |
176 lm_message_node_add_child(y, "reason", reason); | |
177 | |
178 lm_connection_send(lconnection, msg, NULL); | |
179 lm_message_unref(msg); | |
180 } | |
181 | |
182 int xmpp_room_setattrib(const char *roomid, const char *fjid, | |
183 const char *nick, struct role_affil ra, | |
184 const char *reason) | |
185 { | |
186 LmMessage *iq; | |
187 LmMessageNode *query, *x; | |
188 | |
189 if (!lm_connection_is_authenticated(lconnection) || !roomid) return 1; | |
190 if (!fjid && !nick) return 1; | |
191 | |
192 if (check_jid_syntax((char*)roomid)) { | |
193 scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", roomid); | |
194 return 1; | |
195 } | |
196 if (fjid && check_jid_syntax((char*)fjid)) { | |
197 scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", fjid); | |
198 return 1; | |
199 } | |
200 | |
201 if (ra.type == type_affil && ra.val.affil == affil_outcast && !fjid) | |
202 return 1; // Shouldn't happen (jid mandatory when banning) | |
203 | |
204 iq = lm_message_new_with_sub_type(roomid, LM_MESSAGE_TYPE_IQ, | |
205 LM_MESSAGE_SUB_TYPE_SET); | |
206 query = lm_message_node_add_child(iq->node, "query", NULL); | |
207 lm_message_node_set_attribute(query, "xmlns", | |
208 "http://jabber.org/protocol/muc#admin"); | |
209 x = lm_message_node_add_child(query, "item", NULL); | |
210 | |
211 if (fjid) { | |
212 lm_message_node_set_attribute(x, "jid", fjid); | |
213 } else { // nickname | |
214 lm_message_node_set_attribute(x, "nick", nick); | |
215 } | |
216 | |
217 if (ra.type == type_affil) | |
218 lm_message_node_set_attribute(x, "affiliation", straffil[ra.val.affil]); | |
219 else if (ra.type == type_role) | |
220 lm_message_node_set_attribute(x, "role", strrole[ra.val.role]); | |
221 | |
222 if (reason) | |
223 lm_message_node_add_child(x, "reason", reason); | |
224 | |
225 lm_connection_send(lconnection, iq, NULL); | |
226 lm_message_unref(iq); | |
227 | |
228 return 0; | |
229 } | |
230 | |
231 // Unlock a MUC room | |
232 // room syntax: "room@server" | |
233 void xmpp_room_unlock(const char *room) | |
234 { | |
235 LmMessageNode *node; | |
236 LmMessage *iq; | |
237 | |
238 if (!lm_connection_is_authenticated(lconnection) || !room) return; | |
239 | |
240 iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ, | |
241 LM_MESSAGE_SUB_TYPE_SET); | |
242 | |
243 node = lm_message_node_add_child(iq->node, "query", NULL); | |
244 lm_message_node_set_attribute(node, "xmlns", | |
245 "http://jabber.org/protocol/muc#owner"); | |
246 node = lm_message_node_add_child(node, "x", NULL); | |
247 lm_message_node_set_attributes(node, "xmlns", "jabber:x:data", | |
248 "type", "submit", NULL); | |
249 | |
250 lm_connection_send(lconnection, iq, NULL); | |
251 lm_message_unref(iq); | |
252 } | |
253 | |
254 // Destroy a MUC room | |
255 // room syntax: "room@server" | |
256 void xmpp_room_destroy(const char *room, const char *venue, const char *reason) | |
257 { | |
258 LmMessage *iq; | |
259 LmMessageNode *query, *x; | |
260 | |
261 if (!lm_connection_is_authenticated(lconnection) || !room) return; | |
262 | |
263 iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ, | |
264 LM_MESSAGE_SUB_TYPE_SET); | |
265 query = lm_message_node_add_child(iq->node, "query", NULL); | |
266 lm_message_node_set_attribute(query, "xmlns", | |
267 "http://jabber.org/protocol/muc#owner"); | |
268 x = lm_message_node_add_child(query, "destroy", NULL); | |
269 | |
270 if (venue && *venue) | |
271 lm_message_node_set_attribute(x, "jid", venue); | |
272 | |
273 if (reason) | |
274 lm_message_node_add_child(x, "reason", reason); | |
275 | |
276 lm_connection_send(lconnection, iq, NULL); | |
277 lm_message_unref(iq); | |
278 } | |
279 | |
280 // muc_get_item_info(...) | |
281 // Get room member's information from xmlndata. | |
282 // The variables must be initialized before calling this function, | |
283 // because they are not touched if the relevant information is missing. | |
284 static void muc_get_item_info(const char *from, LmMessageNode *xmldata, | |
285 enum imrole *mbrole, enum imaffiliation *mbaffil, | |
286 const char **mbjid, const char **mbnick, | |
287 const char **actorjid, const char **reason) | |
288 { | |
289 LmMessageNode *y, *z; | |
290 const char *p; | |
291 | |
292 y = lm_message_node_find_child(xmldata, "item"); | |
293 if (!y) | |
294 return; | |
295 | |
296 p = lm_message_node_get_attribute(y, "affiliation"); | |
297 if (p) { | |
298 if (!strcmp(p, "owner")) *mbaffil = affil_owner; | |
299 else if (!strcmp(p, "admin")) *mbaffil = affil_admin; | |
300 else if (!strcmp(p, "member")) *mbaffil = affil_member; | |
301 else if (!strcmp(p, "outcast")) *mbaffil = affil_outcast; | |
302 else if (!strcmp(p, "none")) *mbaffil = affil_none; | |
303 else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown affiliation \"%s\"", | |
304 from, p); | |
305 } | |
306 p = lm_message_node_get_attribute(y, "role"); | |
307 if (p) { | |
308 if (!strcmp(p, "moderator")) *mbrole = role_moderator; | |
309 else if (!strcmp(p, "participant")) *mbrole = role_participant; | |
310 else if (!strcmp(p, "visitor")) *mbrole = role_visitor; | |
311 else if (!strcmp(p, "none")) *mbrole = role_none; | |
312 else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown role \"%s\"", | |
313 from, p); | |
314 } | |
315 *mbjid = lm_message_node_get_attribute(y, "jid"); | |
316 *mbnick = lm_message_node_get_attribute(y, "nick"); | |
317 // For kick/ban, there can be actor and reason tags | |
318 *reason = lm_message_node_get_child_value(y, "reason"); | |
319 z = lm_message_node_find_child(y, "actor"); | |
320 if (z) | |
321 *actorjid = lm_message_node_get_attribute(z, "jid"); | |
322 } | |
323 | |
324 // muc_handle_join(...) | |
325 // Handle a join event in a MUC room. | |
326 // This function will return the new_member value TRUE if somebody else joins | |
327 // the room (and FALSE if _we_ are joining the room). | |
328 static bool muc_handle_join(const GSList *room_elt, const char *rname, | |
329 const char *roomjid, const char *ournick, | |
330 enum room_printstatus printstatus, | |
331 time_t usttime, int log_muc_conf) | |
332 { | |
333 bool new_member = FALSE; // True if somebody else joins the room (not us) | |
334 gchar *mbuf; | |
335 | |
336 if (!buddy_getinsideroom(room_elt->data)) { | |
337 // We weren't inside the room yet. Now we are. | |
338 // However, this could be a presence packet from another room member | |
339 | |
340 buddy_setinsideroom(room_elt->data, TRUE); | |
341 // Set the message flag unless we're already in the room buffer window | |
342 scr_setmsgflag_if_needed(roomjid, FALSE); | |
343 // Add a message to the tracelog file | |
344 mbuf = g_strdup_printf("You have joined %s as \"%s\"", roomjid, ournick); | |
345 scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf); | |
346 g_free(mbuf); | |
347 mbuf = g_strdup_printf("You have joined as \"%s\"", ournick); | |
348 | |
349 // The 1st presence message could be for another room member | |
350 if (strcmp(ournick, rname)) { | |
351 // Display current mbuf and create a new message for the member | |
352 // Note: the usttime timestamp is related to the other member, | |
353 // so we use 0 here. | |
354 scr_WriteIncomingMessage(roomjid, mbuf, 0, | |
355 HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0); | |
356 if (log_muc_conf) | |
357 hlog_write_message(roomjid, 0, -1, mbuf); | |
358 g_free(mbuf); | |
359 if (printstatus != status_none) | |
360 mbuf = g_strdup_printf("%s has joined", rname); | |
361 else | |
362 mbuf = NULL; | |
363 new_member = TRUE; | |
364 } | |
365 } else { | |
366 mbuf = NULL; | |
367 if (strcmp(ournick, rname)) { | |
368 if (printstatus != status_none) | |
369 mbuf = g_strdup_printf("%s has joined", rname); | |
370 new_member = TRUE; | |
371 } | |
372 } | |
373 | |
374 if (mbuf) { | |
375 guint msgflags = HBB_PREFIX_INFO; | |
376 if (!settings_opt_get_int("muc_flag_joins")) | |
377 msgflags |= HBB_PREFIX_NOFLAG; | |
378 scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0); | |
379 if (log_muc_conf) | |
380 hlog_write_message(roomjid, 0, -1, mbuf); | |
381 g_free(mbuf); | |
382 } | |
383 | |
384 return new_member; | |
385 } | |
386 | |
387 void handle_muc_presence(const char *from, LmMessageNode *xmldata, | |
388 const char *roomjid, const char *rname, | |
389 enum imstatus ust, const char *ustmsg, | |
390 time_t usttime, char bpprio) | |
391 { | |
392 LmMessageNode *y; | |
393 const char *p; | |
394 char *mbuf; | |
395 const char *ournick; | |
396 enum imrole mbrole = role_none; | |
397 enum imaffiliation mbaffil = affil_none; | |
398 enum room_printstatus printstatus; | |
399 enum room_autowhois autowhois; | |
400 const char *mbjid = NULL, *mbnick = NULL; | |
401 const char *actorjid = NULL, *reason = NULL; | |
402 bool new_member = FALSE; // True if somebody else joins the room (not us) | |
403 guint statuscode = 0; | |
404 guint nickchange = 0; | |
405 GSList *room_elt; | |
406 int log_muc_conf; | |
407 guint msgflags; | |
408 | |
409 log_muc_conf = settings_opt_get_int("log_muc_conf"); | |
410 | |
411 room_elt = roster_find(roomjid, jidsearch, 0); | |
412 if (!room_elt) { | |
413 // Add room if it doesn't already exist | |
414 // It shouldn't happen, there is probably something wrong (server or | |
415 // network issue?) | |
416 room_elt = roster_add_user(roomjid, NULL, NULL, ROSTER_TYPE_ROOM, | |
417 sub_none, -1); | |
418 scr_LogPrint(LPRINT_LOGNORM, "Strange MUC presence message"); | |
419 } else { | |
420 // Make sure this is a room (it can be a conversion user->room) | |
421 buddy_settype(room_elt->data, ROSTER_TYPE_ROOM); | |
422 } | |
423 | |
424 // Get room member's information | |
425 muc_get_item_info(from, xmldata, &mbrole, &mbaffil, &mbjid, &mbnick, | |
426 &actorjid, &reason); | |
427 | |
428 // Get our room nickname | |
429 ournick = buddy_getnickname(room_elt->data); | |
430 | |
431 if (!ournick) { | |
432 // It shouldn't happen, probably a server issue | |
433 mbuf = g_strdup_printf("Unexpected groupchat packet!"); | |
434 | |
435 scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf); | |
436 scr_WriteIncomingMessage(roomjid, mbuf, 0, HBB_PREFIX_INFO, 0); | |
437 g_free(mbuf); | |
438 // Send back an unavailable packet | |
439 xmpp_setstatus(offline, roomjid, "", TRUE); | |
440 scr_DrawRoster(); | |
441 return; | |
442 } | |
443 | |
444 // Get the status code | |
445 // 201: a room has been created | |
446 // 301: the user has been banned from the room | |
447 // 303: new room nickname | |
448 // 307: the user has been kicked from the room | |
449 // 321,322,332: the user has been removed from the room | |
450 y = lm_message_node_find_child(xmldata, "status"); | |
451 if (y) { | |
452 p = lm_message_node_get_attribute(y, "code"); | |
453 if (p) | |
454 statuscode = atoi(p); | |
455 } | |
456 | |
457 // Get the room's "print_status" settings | |
458 printstatus = buddy_getprintstatus(room_elt->data); | |
459 if (printstatus == status_default) { | |
460 printstatus = (guint) settings_opt_get_int("muc_print_status"); | |
461 if (printstatus > 3) | |
462 printstatus = status_default; | |
463 } | |
464 | |
465 // A new room has been created; accept MUC default config | |
466 if (statuscode == 201) | |
467 xmpp_room_unlock(roomjid); | |
468 | |
469 // Check for nickname change | |
470 if (statuscode == 303 && mbnick) { | |
471 mbuf = g_strdup_printf("%s is now known as %s", rname, mbnick); | |
472 scr_WriteIncomingMessage(roomjid, mbuf, usttime, | |
473 HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0); | |
474 if (log_muc_conf) | |
475 hlog_write_message(roomjid, 0, -1, mbuf); | |
476 g_free(mbuf); | |
477 buddy_resource_setname(room_elt->data, rname, mbnick); | |
478 // Maybe it's _our_ nickname... | |
479 if (ournick && !strcmp(rname, ournick)) | |
480 buddy_setnickname(room_elt->data, mbnick); | |
481 nickchange = TRUE; | |
482 } | |
483 | |
484 // Check for departure/arrival | |
485 if (!mbnick && ust == offline) { | |
486 // Somebody is leaving | |
487 enum { leave=0, kick, ban } how = leave; | |
488 bool we_left = FALSE; | |
489 | |
490 if (statuscode == 307) | |
491 how = kick; | |
492 else if (statuscode == 301) | |
493 how = ban; | |
494 | |
495 // If this is a leave, check if it is ourself | |
496 if (ournick && !strcmp(rname, ournick)) { | |
497 we_left = TRUE; // _We_ have left! (kicked, banned, etc.) | |
498 buddy_setinsideroom(room_elt->data, FALSE); | |
499 buddy_setnickname(room_elt->data, NULL); | |
500 buddy_del_all_resources(room_elt->data); | |
501 buddy_settopic(room_elt->data, NULL); | |
502 scr_UpdateChatStatus(FALSE); | |
503 update_roster = TRUE; | |
504 } | |
505 | |
506 // The message depends on _who_ left, and _how_ | |
507 if (how) { | |
508 gchar *mbuf_end; | |
509 // Forced leave | |
510 if (actorjid) { | |
511 mbuf_end = g_strdup_printf("%s from %s by <%s>.\nReason: %s", | |
512 (how == ban ? "banned" : "kicked"), | |
513 roomjid, actorjid, reason); | |
514 } else { | |
515 mbuf_end = g_strdup_printf("%s from %s.", | |
516 (how == ban ? "banned" : "kicked"), | |
517 roomjid); | |
518 } | |
519 if (we_left) | |
520 mbuf = g_strdup_printf("You have been %s", mbuf_end); | |
521 else | |
522 mbuf = g_strdup_printf("%s has been %s", rname, mbuf_end); | |
523 | |
524 g_free(mbuf_end); | |
525 } else { | |
526 // Natural leave | |
527 if (we_left) { | |
528 LmMessageNode *destroynode = lm_message_node_find_child(xmldata, | |
529 "destroy"); | |
530 if (destroynode) { | |
531 if ((reason = lm_message_node_get_child_value(destroynode, | |
532 "reason"))) { | |
533 mbuf = g_strdup_printf("You have left %s, " | |
534 "the room has been destroyed: %s", | |
535 roomjid, reason); | |
536 } else { | |
537 mbuf = g_strdup_printf("You have left %s, " | |
538 "the room has been destroyed", roomjid); | |
539 } | |
540 } else { | |
541 mbuf = g_strdup_printf("You have left %s", roomjid); | |
542 } | |
543 } else { | |
544 if (ust != offline) { | |
545 // This can happen when a network failure occurs, | |
546 // this isn't an official leave but the user isn't there anymore. | |
547 mbuf = g_strdup_printf("%s has disappeared!", rname); | |
548 ust = offline; | |
549 } else { | |
550 if (ustmsg) | |
551 mbuf = g_strdup_printf("%s has left: %s", rname, ustmsg); | |
552 else | |
553 mbuf = g_strdup_printf("%s has left", rname); | |
554 } | |
555 } | |
556 } | |
557 | |
558 // Display the mbuf message if we're concerned | |
559 // or if the print_status isn't set to none. | |
560 if (we_left || printstatus != status_none) { | |
561 msgflags = HBB_PREFIX_INFO; | |
562 if (!we_left && settings_opt_get_int("muc_flag_joins") != 2) | |
563 msgflags |= HBB_PREFIX_NOFLAG; | |
564 scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0); | |
565 } | |
566 | |
567 if (log_muc_conf) | |
568 hlog_write_message(roomjid, 0, -1, mbuf); | |
569 | |
570 if (we_left) { | |
571 scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf); | |
572 g_free(mbuf); | |
573 return; | |
574 } | |
575 g_free(mbuf); | |
576 } else if (buddy_getstatus(room_elt->data, rname) == offline && | |
577 ust != offline) { | |
578 // Somebody is joining | |
579 new_member = muc_handle_join(room_elt, rname, roomjid, ournick, | |
580 printstatus, usttime, log_muc_conf); | |
581 } else { | |
582 // This is a simple member status change | |
583 | |
584 if (printstatus == status_all && !nickchange) { | |
585 mbuf = g_strdup_printf("Member status has changed: %s [%c] %s", rname, | |
586 imstatus2char[ust], ((ustmsg) ? ustmsg : "")); | |
587 scr_WriteIncomingMessage(roomjid, mbuf, usttime, | |
588 HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0); | |
589 g_free(mbuf); | |
590 } | |
591 } | |
592 | |
593 // Sanity check, shouldn't happen... | |
594 if (!rname) | |
595 return; | |
596 | |
597 // Update room member status | |
598 roster_setstatus(roomjid, rname, bpprio, ust, ustmsg, usttime, | |
599 mbrole, mbaffil, mbjid); | |
600 | |
601 autowhois = buddy_getautowhois(room_elt->data); | |
602 if (autowhois == autowhois_default) | |
603 autowhois = (settings_opt_get_int("muc_auto_whois") ? | |
604 autowhois_on : autowhois_off); | |
605 | |
606 if (new_member && autowhois == autowhois_on) { | |
607 // FIXME: This will fail for some UTF-8 nicknames. | |
608 gchar *joiner_nick = from_utf8(rname); | |
609 cmd_room_whois(room_elt->data, joiner_nick, FALSE); | |
610 g_free(joiner_nick); | |
611 } | |
612 | |
613 scr_DrawRoster(); | |
614 } | |
615 | |
616 void roompresence(gpointer room, void *presencedata) | |
617 { | |
618 const char *bjid; | |
619 const char *nickname; | |
620 char *to; | |
621 struct T_presence *pres = presencedata; | |
622 | |
623 if (!buddy_getinsideroom(room)) | |
624 return; | |
625 | |
626 bjid = buddy_getjid(room); | |
627 if (!bjid) return; | |
628 nickname = buddy_getnickname(room); | |
629 if (!nickname) return; | |
630 | |
631 to = g_strdup_printf("%s/%s", bjid, nickname); | |
632 xmpp_setstatus(pres->st, to, pres->msg, TRUE); | |
633 g_free(to); | |
634 } | |
635 | |
636 // got_invite(from, to, reason, passwd) | |
637 // This function should be called when receiving an invitation from user | |
638 // "from", to enter the room "to". Optional reason and room password can | |
639 // be provided. | |
640 static void got_invite(const char* from, const char *to, const char* reason, | |
641 const char* passwd) | |
642 { | |
643 eviqs *evn; | |
644 event_muc_invitation *invitation; | |
645 GString *sbuf; | |
646 char *barejid; | |
647 GSList *room_elt; | |
648 | |
649 sbuf = g_string_new(""); | |
650 if (reason) { | |
651 g_string_printf(sbuf, | |
652 "Received an invitation to <%s>, from <%s>, reason: %s", | |
653 to, from, reason); | |
654 } else { | |
655 g_string_printf(sbuf, "Received an invitation to <%s>, from <%s>", | |
656 to, from); | |
657 } | |
658 | |
659 barejid = jidtodisp(from); | |
660 scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0); | |
661 scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str); | |
662 | |
663 evn = evs_new(EVS_TYPE_INVITATION, EVS_MAX_TIMEOUT); | |
664 if (evn) { | |
665 evn->callback = &evscallback_invitation; | |
666 invitation = g_new(event_muc_invitation, 1); | |
667 invitation->to = g_strdup(to); | |
668 invitation->from = g_strdup(from); | |
669 invitation->passwd = g_strdup(passwd); | |
670 invitation->reason = g_strdup(reason); | |
671 evn->data = invitation; | |
672 evn->desc = g_strdup_printf("<%s> invites you to %s ", from, to); | |
673 g_string_printf(sbuf, "Please use /event %s accept|reject", evn->id); | |
674 } else { | |
675 g_string_printf(sbuf, "Unable to create a new event!"); | |
676 } | |
677 scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0); | |
678 scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str); | |
679 g_string_free(sbuf, TRUE); | |
680 g_free(barejid); | |
681 | |
682 // Make sure the MUC room barejid is a room in the roster | |
683 barejid = jidtodisp(to); | |
684 room_elt = roster_find(barejid, jidsearch, 0); | |
685 if (room_elt) | |
686 buddy_settype(room_elt->data, ROSTER_TYPE_ROOM); | |
687 | |
688 g_free(barejid); | |
689 } | |
690 | |
691 | |
692 // Specific MUC message handling (for example invitation processing) | |
693 void got_muc_message(const char *from, LmMessageNode *x) | |
694 { | |
695 LmMessageNode *invite = lm_message_node_get_child(x, "invite"); | |
696 if (invite) | |
697 { | |
698 const char *invite_from; | |
699 const char *reason = NULL; | |
700 const char *password = NULL; | |
701 | |
702 invite_from = lm_message_node_get_attribute(invite, "from"); | |
703 reason = lm_message_node_get_child_value(invite, "reason"); | |
704 password = lm_message_node_get_child_value(invite, "password"); | |
705 if (invite_from) | |
706 got_invite(invite_from, from, reason, password); | |
707 } | |
708 // TODO | |
709 // handle status code = 100 ( not anonymous ) | |
710 // handle status code = 170 ( changement de config ) | |
711 // 10.2.1 Notification of Configuration Changes | |
712 // declined invitation | |
713 } | |
714 | |
715 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ |