2 * purple - Jabber Protocol Plugin
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "protocol.h" /* for PurpleProtocolChatEntry */
38 GList
*jabber_chat_info(PurpleConnection
*gc
)
41 PurpleProtocolChatEntry
*pce
;
43 pce
= g_new0(PurpleProtocolChatEntry
, 1);
44 pce
->label
= _("_Room:");
45 pce
->identifier
= "room";
47 m
= g_list_append(m
, pce
);
49 pce
= g_new0(PurpleProtocolChatEntry
, 1);
50 pce
->label
= _("_Server:");
51 pce
->identifier
= "server";
53 m
= g_list_append(m
, pce
);
55 pce
= g_new0(PurpleProtocolChatEntry
, 1);
56 pce
->label
= _("_Handle:");
57 pce
->identifier
= "handle";
59 m
= g_list_append(m
, pce
);
61 pce
= g_new0(PurpleProtocolChatEntry
, 1);
62 pce
->label
= _("_Password:");
63 pce
->identifier
= "password";
65 m
= g_list_append(m
, pce
);
70 GHashTable
*jabber_chat_info_defaults(PurpleConnection
*gc
, const char *chat_name
)
73 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
75 defaults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, g_free
);
77 g_hash_table_insert(defaults
, "handle", g_strdup(js
->user
->node
));
80 g_hash_table_insert(defaults
, "server", g_strdup(js
->chat_servers
->data
));
82 if (chat_name
!= NULL
) {
83 JabberID
*jid
= jabber_id_new(chat_name
);
85 g_hash_table_insert(defaults
, "room", g_strdup(jid
->node
));
87 g_hash_table_replace(defaults
, "server", g_strdup(jid
->domain
));
89 g_hash_table_replace(defaults
, "handle", g_strdup(jid
->resource
));
97 JabberChat
*jabber_chat_find(JabberStream
*js
, const char *room
,
100 JabberChat
*chat
= NULL
;
102 g_return_val_if_fail(room
!= NULL
, NULL
);
103 g_return_val_if_fail(server
!= NULL
, NULL
);
105 if(NULL
!= js
->chats
)
107 char *room_jid
= g_strdup_printf("%s@%s", room
, server
);
109 chat
= g_hash_table_lookup(js
->chats
, room_jid
);
116 struct _find_by_id_data
{
121 static void find_by_id_foreach_cb(gpointer key
, gpointer value
, gpointer user_data
)
123 JabberChat
*chat
= value
;
124 struct _find_by_id_data
*fbid
= user_data
;
126 if(chat
->id
== fbid
->id
)
130 JabberChat
*jabber_chat_find_by_id(JabberStream
*js
, int id
)
133 struct _find_by_id_data
*fbid
= g_new0(struct _find_by_id_data
, 1);
135 g_hash_table_foreach(js
->chats
, find_by_id_foreach_cb
, fbid
);
141 JabberChat
*jabber_chat_find_by_conv(PurpleChatConversation
*conv
)
143 PurpleAccount
*account
= purple_conversation_get_account(PURPLE_CONVERSATION(conv
));
144 PurpleConnection
*gc
= purple_account_get_connection(account
);
149 js
= purple_connection_get_protocol_data(gc
);
150 id
= purple_chat_conversation_get_id(conv
);
151 return jabber_chat_find_by_id(js
, id
);
154 void jabber_chat_invite(PurpleConnection
*gc
, int id
, const char *msg
,
157 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
159 PurpleXmlNode
*message
, *body
, *x
, *invite
;
162 chat
= jabber_chat_find_by_id(js
, id
);
166 message
= purple_xmlnode_new("message");
168 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
171 purple_xmlnode_set_attrib(message
, "to", room_jid
);
172 x
= purple_xmlnode_new_child(message
, "x");
173 purple_xmlnode_set_namespace(x
, "http://jabber.org/protocol/muc#user");
174 invite
= purple_xmlnode_new_child(x
, "invite");
175 purple_xmlnode_set_attrib(invite
, "to", name
);
177 body
= purple_xmlnode_new_child(invite
, "reason");
178 purple_xmlnode_insert_data(body
, msg
, -1);
181 purple_xmlnode_set_attrib(message
, "to", name
);
183 * Putting the reason into the body was an 'undocumented protocol,
184 * ...not part of "groupchat 1.0"'.
185 * http://xmpp.org/extensions/attic/jep-0045-1.16.html#invite
187 * Left here for compatibility.
190 body
= purple_xmlnode_new_child(message
, "body");
191 purple_xmlnode_insert_data(body
, msg
, -1);
194 x
= purple_xmlnode_new_child(message
, "x");
195 purple_xmlnode_set_attrib(x
, "jid", room_jid
);
197 /* The better place for it! XEP-0249 style. */
199 purple_xmlnode_set_attrib(x
, "reason", msg
);
200 purple_xmlnode_set_namespace(x
, "jabber:x:conference");
203 jabber_send(js
, message
);
204 purple_xmlnode_free(message
);
208 void jabber_chat_member_free(JabberChatMember
*jcm
);
210 char *jabber_get_chat_name(GHashTable
*data
) {
211 char *room
, *server
, *chat_name
= NULL
;
213 room
= g_hash_table_lookup(data
, "room");
214 server
= g_hash_table_lookup(data
, "server");
216 if (room
&& server
) {
217 chat_name
= g_strdup_printf("%s@%s", room
, server
);
222 static void insert_in_hash_table(gpointer key
, gpointer value
, gpointer user_data
)
224 GHashTable
*hash_table
= (GHashTable
*)user_data
;
225 g_hash_table_insert(hash_table
, g_strdup(key
), g_strdup(value
));
228 static JabberChat
*jabber_chat_new(JabberStream
*js
, const char *room
,
229 const char *server
, const char *handle
,
230 const char *password
, GHashTable
*data
)
235 if (jabber_chat_find(js
, room
, server
) != NULL
)
238 chat
= g_new0(JabberChat
, 1);
242 chat
->room
= g_strdup(room
);
243 chat
->server
= g_strdup(server
);
244 chat
->handle
= g_strdup(handle
);
246 /* Copy the data hash table to chat->components */
247 chat
->components
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
250 g_hash_table_insert(chat
->components
, g_strdup("handle"), g_strdup(handle
));
251 g_hash_table_insert(chat
->components
, g_strdup("room"), g_strdup(room
));
252 g_hash_table_insert(chat
->components
, g_strdup("server"), g_strdup(server
));
253 /* g_hash_table_insert(chat->components, g_strdup("password"), g_strdup(server)); */
255 g_hash_table_foreach(data
, insert_in_hash_table
, chat
->components
);
258 chat
->members
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
259 (GDestroyNotify
)jabber_chat_member_free
);
261 jid
= g_strdup_printf("%s@%s", room
, server
);
262 g_hash_table_insert(js
->chats
, jid
, chat
);
267 JabberChat
*jabber_join_chat(JabberStream
*js
, const char *room
,
268 const char *server
, const char *handle
,
269 const char *password
, GHashTable
*data
)
273 PurpleConnection
*gc
;
274 PurpleAccount
*account
;
275 PurpleStatus
*status
;
277 PurpleXmlNode
*presence
, *x
;
278 JabberBuddyState state
;
284 char *history_maxchars
;
285 char *history_maxstanzas
;
286 char *history_seconds
;
289 struct tm history_since_datetime
;
290 const char *history_since_string
= NULL
;
292 chat
= jabber_chat_new(js
, room
, server
, handle
, password
, data
);
297 account
= purple_connection_get_account(gc
);
298 status
= purple_account_get_active_status(account
);
299 purple_status_to_jabber(status
, &state
, &msg
, &priority
);
301 presence
= jabber_presence_create_js(js
, state
, msg
, priority
);
304 jid
= g_strdup_printf("%s@%s/%s", room
, server
, handle
);
305 purple_xmlnode_set_attrib(presence
, "to", jid
);
308 history_maxchars
= g_hash_table_lookup(data
, "history_maxchars");
309 history_maxstanzas
= g_hash_table_lookup(data
, "history_maxstanzas");
310 history_seconds
= g_hash_table_lookup(data
, "history_seconds");
311 history_since
= g_hash_table_lookup(data
, "history_since");
314 if (purple_str_to_time(history_since
, TRUE
, &history_since_datetime
, NULL
, NULL
) != 0) {
315 history_since_string
= purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", &history_since_datetime
);
317 history_since_string
= NULL
;
319 purple_debug_error("jabber", "Invalid date format for history_since"
320 " while requesting history: %s", history_since
);
324 x
= purple_xmlnode_new_child(presence
, "x");
325 purple_xmlnode_set_namespace(x
, "http://jabber.org/protocol/muc");
327 if (password
&& *password
) {
328 PurpleXmlNode
*p
= purple_xmlnode_new_child(x
, "password");
329 purple_xmlnode_insert_data(p
, password
, -1);
332 if ((history_maxchars
&& *history_maxchars
)
333 || (history_maxstanzas
&& *history_maxstanzas
)
334 || (history_seconds
&& *history_seconds
)
335 || (history_since_string
&& *history_since_string
)) {
337 PurpleXmlNode
*history
= purple_xmlnode_new_child(x
, "history");
339 if (history_maxchars
&& *history_maxchars
) {
340 purple_xmlnode_set_attrib(history
, "maxchars", history_maxchars
);
342 if (history_maxstanzas
&& *history_maxstanzas
) {
343 purple_xmlnode_set_attrib(history
, "maxstanzas", history_maxstanzas
);
345 if (history_seconds
&& *history_seconds
) {
346 purple_xmlnode_set_attrib(history
, "seconds", history_seconds
);
348 if (history_since_string
&& *history_since_string
) {
349 purple_xmlnode_set_attrib(history
, "since", history_since_string
);
353 jabber_send(js
, presence
);
354 purple_xmlnode_free(presence
);
359 void jabber_chat_join(PurpleConnection
*gc
, GHashTable
*data
)
361 char *room
, *server
, *handle
, *passwd
;
363 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
366 room
= g_hash_table_lookup(data
, "room");
367 server
= g_hash_table_lookup(data
, "server");
368 handle
= g_hash_table_lookup(data
, "handle");
369 passwd
= g_hash_table_lookup(data
, "password");
375 handle
= js
->user
->node
;
377 if(!jabber_nodeprep_validate(room
)) {
378 char *buf
= g_strdup_printf(_("%s is not a valid room name"), room
);
379 purple_notify_error(gc
, _("Invalid Room Name"), _("Invalid Room Name"),
380 buf
, purple_request_cpar_from_connection(gc
));
381 purple_serv_got_join_chat_failed(gc
, data
);
384 } else if(!jabber_domain_validate(server
)) {
385 char *buf
= g_strdup_printf(_("%s is not a valid server name"), server
);
386 purple_notify_error(gc
, _("Invalid Server Name"),
387 _("Invalid Server Name"), buf
,
388 purple_request_cpar_from_connection(gc
));
389 purple_serv_got_join_chat_failed(gc
, data
);
392 } else if(!jabber_resourceprep_validate(handle
)) {
393 char *buf
= g_strdup_printf(_("%s is not a valid room handle"), handle
);
394 purple_notify_error(gc
, _("Invalid Room Handle"),
395 _("Invalid Room Handle"), buf
,
396 purple_request_cpar_from_connection(gc
));
397 purple_serv_got_join_chat_failed(gc
, data
);
402 /* Normalize the room and server parameters */
403 tmp
= g_strdup_printf("%s@%s", room
, server
);
404 jid
= jabber_id_new(tmp
);
408 /* TODO: Error message */
410 g_return_if_reached();
414 * Now that we've done all that nice core-interface stuff, let's join
417 jabber_join_chat(js
, jid
->node
, jid
->domain
, handle
, passwd
, data
);
421 void jabber_chat_leave(PurpleConnection
*gc
, int id
)
423 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
424 JabberChat
*chat
= jabber_chat_find_by_id(js
, id
);
429 jabber_chat_part(chat
, NULL
);
434 void jabber_chat_destroy(JabberChat
*chat
)
436 JabberStream
*js
= chat
->js
;
437 char *room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
439 g_hash_table_remove(js
->chats
, room_jid
);
443 void jabber_chat_free(JabberChat
*chat
)
445 if(chat
->config_dialog_handle
)
446 purple_request_close(chat
->config_dialog_type
, chat
->config_dialog_handle
);
449 g_free(chat
->server
);
450 g_free(chat
->handle
);
451 g_hash_table_destroy(chat
->members
);
452 g_hash_table_destroy(chat
->components
);
456 gboolean
jabber_chat_find_buddy(PurpleChatConversation
*conv
, const char *name
)
458 return purple_chat_conversation_has_user(conv
, name
);
461 char *jabber_chat_user_real_name(PurpleConnection
*gc
, int id
, const char *who
)
463 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
465 JabberChatMember
*jcm
;
467 chat
= jabber_chat_find_by_id(js
, id
);
472 jcm
= g_hash_table_lookup(chat
->members
, who
);
473 if (jcm
!= NULL
&& jcm
->jid
)
474 return g_strdup(jcm
->jid
);
477 return g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
, who
);
480 static void jabber_chat_room_configure_x_data_cb(JabberStream
*js
, PurpleXmlNode
*result
, gpointer data
)
482 JabberChat
*chat
= data
;
483 PurpleXmlNode
*query
;
485 char *to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
487 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "http://jabber.org/protocol/muc#owner");
488 purple_xmlnode_set_attrib(iq
->node
, "to", to
);
491 query
= purple_xmlnode_get_child(iq
->node
, "query");
493 purple_xmlnode_insert_child(query
, result
);
498 static void jabber_chat_room_configure_cb(JabberStream
*js
, const char *from
,
499 JabberIqType type
, const char *id
,
500 PurpleXmlNode
*packet
, gpointer data
)
502 PurpleXmlNode
*query
, *x
;
510 if (type
== JABBER_IQ_RESULT
) {
511 jid
= jabber_id_new(from
);
516 chat
= jabber_chat_find(js
, jid
->node
, jid
->domain
);
522 if(!(query
= purple_xmlnode_get_child(packet
, "query")))
525 for(x
= purple_xmlnode_get_child(query
, "x"); x
; x
= purple_xmlnode_get_next_twin(x
)) {
527 if(!(xmlns
= purple_xmlnode_get_namespace(x
)))
530 if(!strcmp(xmlns
, "jabber:x:data")) {
531 chat
->config_dialog_type
= PURPLE_REQUEST_FIELDS
;
532 chat
->config_dialog_handle
= jabber_x_data_request(js
, x
, jabber_chat_room_configure_x_data_cb
, chat
);
536 } else if (type
== JABBER_IQ_ERROR
) {
537 char *msg
= jabber_parse_error(js
, packet
, NULL
);
539 purple_notify_error(js
->gc
, _("Configuration error"),
540 _("Configuration error"), msg
,
541 purple_request_cpar_from_connection(js
->gc
));
547 msg
= g_strdup_printf("Unable to configure room %s", from
);
549 purple_notify_info(js
->gc
, _("Unable to configure"),
550 _("Unable to configure"), msg
,
551 purple_request_cpar_from_connection(js
->gc
));
556 void jabber_chat_request_room_configure(JabberChat
*chat
) {
563 chat
->config_dialog_handle
= NULL
;
566 purple_notify_error(chat
->js
->gc
, _("Room Configuration Error"),
567 _("Room Configuration Error"),
568 _("This room is not capable of being configured"),
569 purple_request_cpar_from_connection(chat
->js
->gc
));
573 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
,
574 "http://jabber.org/protocol/muc#owner");
575 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
577 purple_xmlnode_set_attrib(iq
->node
, "to", room_jid
);
579 jabber_iq_set_callback(iq
, jabber_chat_room_configure_cb
, NULL
);
586 void jabber_chat_create_instant_room(JabberChat
*chat
) {
588 PurpleXmlNode
*query
, *x
;
594 chat
->config_dialog_handle
= NULL
;
596 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
597 "http://jabber.org/protocol/muc#owner");
598 query
= purple_xmlnode_get_child(iq
->node
, "query");
599 x
= purple_xmlnode_new_child(query
, "x");
600 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
602 purple_xmlnode_set_attrib(iq
->node
, "to", room_jid
);
603 purple_xmlnode_set_namespace(x
, "jabber:x:data");
604 purple_xmlnode_set_attrib(x
, "type", "submit");
612 jabber_chat_register_x_data_result_cb(JabberStream
*js
, const char *from
,
613 JabberIqType type
, const char *id
,
614 PurpleXmlNode
*packet
, gpointer data
)
616 if (type
== JABBER_IQ_ERROR
) {
617 char *msg
= jabber_parse_error(js
, packet
, NULL
);
619 purple_notify_error(js
->gc
, _("Registration error"),
620 _("Registration error"), msg
,
621 purple_request_cpar_from_connection(js
->gc
));
628 static void jabber_chat_register_x_data_cb(JabberStream
*js
, PurpleXmlNode
*result
, gpointer data
)
630 JabberChat
*chat
= data
;
631 PurpleXmlNode
*query
;
633 char *to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
635 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
636 purple_xmlnode_set_attrib(iq
->node
, "to", to
);
639 query
= purple_xmlnode_get_child(iq
->node
, "query");
641 purple_xmlnode_insert_child(query
, result
);
643 jabber_iq_set_callback(iq
, jabber_chat_register_x_data_result_cb
, NULL
);
648 static void jabber_chat_register_cb(JabberStream
*js
, const char *from
,
649 JabberIqType type
, const char *id
,
650 PurpleXmlNode
*packet
, gpointer data
)
652 PurpleXmlNode
*query
, *x
;
660 if (type
== JABBER_IQ_RESULT
) {
661 jid
= jabber_id_new(from
);
666 chat
= jabber_chat_find(js
, jid
->node
, jid
->domain
);
672 if(!(query
= purple_xmlnode_get_child(packet
, "query")))
675 for(x
= purple_xmlnode_get_child(query
, "x"); x
; x
= purple_xmlnode_get_next_twin(x
)) {
678 if(!(xmlns
= purple_xmlnode_get_namespace(x
)))
681 if(!strcmp(xmlns
, "jabber:x:data")) {
682 jabber_x_data_request(js
, x
, jabber_chat_register_x_data_cb
, chat
);
686 } else if (type
== JABBER_IQ_ERROR
) {
687 char *msg
= jabber_parse_error(js
, packet
, NULL
);
689 purple_notify_error(js
->gc
, _("Registration error"),
690 _("Registration error"), msg
,
691 purple_request_cpar_from_connection(js
->gc
));
697 msg
= g_strdup_printf("Unable to configure room %s", from
);
699 purple_notify_info(js
->gc
, _("Unable to configure"), _("Unable to "
700 "configure"), msg
, purple_request_cpar_from_connection(js
->gc
));
705 void jabber_chat_register(JabberChat
*chat
)
713 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
715 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
, "jabber:iq:register");
716 purple_xmlnode_set_attrib(iq
->node
, "to", room_jid
);
719 jabber_iq_set_callback(iq
, jabber_chat_register_cb
, NULL
);
724 /* merge this with the function below when we get everyone on the same page wrt /commands */
725 void jabber_chat_change_topic(JabberChat
*chat
, const char *topic
)
729 jm
= g_new0(JabberMessage
, 1);
731 jm
->type
= JABBER_MESSAGE_GROUPCHAT
;
732 jm
->to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
735 jm
->subject
= g_strdup(topic
);
737 jm
->subject
= g_strdup("");
739 jabber_message_send(jm
);
740 jabber_message_free(jm
);
743 void jabber_chat_set_topic(PurpleConnection
*gc
, int id
, const char *topic
)
745 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
746 JabberChat
*chat
= jabber_chat_find_by_id(js
, id
);
751 jabber_chat_change_topic(chat
, topic
);
755 gboolean
jabber_chat_change_nick(JabberChat
*chat
, const char *nick
)
757 PurpleXmlNode
*presence
;
759 PurpleAccount
*account
;
760 PurpleStatus
*status
;
761 JabberBuddyState state
;
766 purple_conversation_write_system_message(
767 PURPLE_CONVERSATION(chat
->conv
),
768 _("Nick changing not supported in non-MUC chatrooms"), 0);
772 account
= purple_connection_get_account(chat
->js
->gc
);
773 status
= purple_account_get_active_status(account
);
775 purple_status_to_jabber(status
, &state
, &msg
, &priority
);
777 presence
= jabber_presence_create_js(chat
->js
, state
, msg
, priority
);
778 full_jid
= g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
, nick
);
779 purple_xmlnode_set_attrib(presence
, "to", full_jid
);
783 jabber_send(chat
->js
, presence
);
784 purple_xmlnode_free(presence
);
789 void jabber_chat_part(JabberChat
*chat
, const char *msg
)
792 PurpleXmlNode
*presence
;
794 room_jid
= g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
,
796 presence
= purple_xmlnode_new("presence");
797 purple_xmlnode_set_attrib(presence
, "to", room_jid
);
798 purple_xmlnode_set_attrib(presence
, "type", "unavailable");
800 PurpleXmlNode
*status
= purple_xmlnode_new_child(presence
, "status");
801 purple_xmlnode_insert_data(status
, msg
, -1);
803 jabber_send(chat
->js
, presence
);
805 purple_xmlnode_free(presence
);
809 static void roomlist_disco_result_cb(JabberStream
*js
, const char *from
,
810 JabberIqType type
, const char *id
,
811 PurpleXmlNode
*packet
, gpointer data
)
813 PurpleXmlNode
*query
;
819 if (type
== JABBER_IQ_ERROR
) {
820 char *err
= jabber_parse_error(js
, packet
, NULL
);
821 purple_notify_error(js
->gc
, _("Error"),
822 _("Error retrieving room list"), err
,
823 purple_request_cpar_from_connection(js
->gc
));
824 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
825 g_object_unref(js
->roomlist
);
831 if(!(query
= purple_xmlnode_get_child(packet
, "query"))) {
832 char *err
= jabber_parse_error(js
, packet
, NULL
);
833 purple_notify_error(js
->gc
, _("Error"),
834 _("Error retrieving room list"), err
,
835 purple_request_cpar_from_connection(js
->gc
));
836 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
837 g_object_unref(js
->roomlist
);
843 for(item
= purple_xmlnode_get_child(query
, "item"); item
;
844 item
= purple_xmlnode_get_next_twin(item
)) {
846 PurpleRoomlistRoom
*room
;
849 if(!(jid
= jabber_id_new(purple_xmlnode_get_attrib(item
, "jid"))))
851 name
= purple_xmlnode_get_attrib(item
, "name");
854 room
= purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM
, jid
->node
, NULL
);
855 purple_roomlist_room_add_field(js
->roomlist
, room
, jid
->node
);
856 purple_roomlist_room_add_field(js
->roomlist
, room
, jid
->domain
);
857 purple_roomlist_room_add_field(js
->roomlist
, room
, name
? name
: "");
858 purple_roomlist_room_add(js
->roomlist
, room
);
862 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
863 g_object_unref(js
->roomlist
);
867 static void roomlist_cancel_cb(JabberStream
*js
, const char *server
) {
869 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
870 g_object_unref(js
->roomlist
);
875 static void roomlist_ok_cb(JabberStream
*js
, const char *server
)
882 if(!server
|| !*server
) {
883 purple_notify_error(js
->gc
, _("Invalid Server"),
884 _("Invalid Server"), NULL
,
885 purple_request_cpar_from_connection(js
->gc
));
886 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
890 purple_roomlist_set_in_progress(js
->roomlist
, TRUE
);
892 iq
= jabber_iq_new_query(js
, JABBER_IQ_GET
, NS_DISCO_ITEMS
);
894 purple_xmlnode_set_attrib(iq
->node
, "to", server
);
896 jabber_iq_set_callback(iq
, roomlist_disco_result_cb
, NULL
);
901 char *jabber_roomlist_room_serialize(PurpleRoomlistRoom
*room
)
903 GList
*fields
= purple_roomlist_room_get_fields(room
);
904 return g_strdup_printf("%s@%s", (char*)fields
->data
, (char*)fields
->next
->data
);
907 PurpleRoomlist
*jabber_roomlist_get_list(PurpleConnection
*gc
)
909 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
910 GList
*fields
= NULL
;
911 PurpleRoomlistField
*f
;
914 g_object_unref(js
->roomlist
);
916 js
->roomlist
= purple_roomlist_new(purple_connection_get_account(js
->gc
));
918 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, "", "room", TRUE
);
919 fields
= g_list_append(fields
, f
);
921 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, "", "server", TRUE
);
922 fields
= g_list_append(fields
, f
);
924 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, _("Description"), "description", FALSE
);
925 fields
= g_list_append(fields
, f
);
927 purple_roomlist_set_fields(js
->roomlist
, fields
);
930 purple_request_input(gc
, _("Enter a Conference Server"), _("Enter a Conference Server"),
931 _("Select a conference server to query"),
932 js
->chat_servers
? js
->chat_servers
->data
: NULL
,
934 _("Find Rooms"), PURPLE_CALLBACK(roomlist_ok_cb
),
935 _("Cancel"), PURPLE_CALLBACK(roomlist_cancel_cb
),
936 purple_request_cpar_from_connection(gc
),
942 void jabber_roomlist_cancel(PurpleRoomlist
*list
)
944 PurpleAccount
*account
;
945 PurpleConnection
*gc
;
948 account
= purple_roomlist_get_account(list
);
949 gc
= purple_account_get_connection(account
);
950 js
= purple_connection_get_protocol_data(gc
);
952 purple_roomlist_set_in_progress(list
, FALSE
);
954 if (js
->roomlist
== list
) {
956 g_object_unref(list
);
960 void jabber_chat_member_free(JabberChatMember
*jcm
)
967 void jabber_chat_track_handle(JabberChat
*chat
, const char *handle
,
968 const char *jid
, const char *affiliation
, const char *role
)
970 JabberChatMember
*jcm
= g_new0(JabberChatMember
, 1);
972 jcm
->handle
= g_strdup(handle
);
973 jcm
->jid
= g_strdup(jid
);
975 g_hash_table_replace(chat
->members
, jcm
->handle
, jcm
);
977 /* XXX: keep track of role and affiliation */
980 void jabber_chat_remove_handle(JabberChat
*chat
, const char *handle
)
982 g_hash_table_remove(chat
->members
, handle
);
985 gboolean
jabber_chat_ban_user(JabberChat
*chat
, const char *who
, const char *why
)
987 JabberChatMember
*jcm
;
991 PurpleXmlNode
*query
, *item
, *reason
;
993 jcm
= g_hash_table_lookup(chat
->members
, who
);
996 else if (strchr(who
, '@') != NULL
)
1001 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
1002 "http://jabber.org/protocol/muc#admin");
1004 to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1005 purple_xmlnode_set_attrib(iq
->node
, "to", to
);
1008 query
= purple_xmlnode_get_child(iq
->node
, "query");
1009 item
= purple_xmlnode_new_child(query
, "item");
1010 purple_xmlnode_set_attrib(item
, "jid", jid
);
1011 purple_xmlnode_set_attrib(item
, "affiliation", "outcast");
1013 reason
= purple_xmlnode_new_child(item
, "reason");
1014 purple_xmlnode_insert_data(reason
, why
, -1);
1022 gboolean
jabber_chat_affiliate_user(JabberChat
*chat
, const char *who
, const char *affiliation
)
1024 JabberChatMember
*jcm
;
1028 PurpleXmlNode
*query
, *item
;
1030 jcm
= g_hash_table_lookup(chat
->members
, who
);
1031 if (jcm
&& jcm
->jid
)
1033 else if (strchr(who
, '@') != NULL
)
1038 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
1039 "http://jabber.org/protocol/muc#admin");
1041 to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1042 purple_xmlnode_set_attrib(iq
->node
, "to", to
);
1045 query
= purple_xmlnode_get_child(iq
->node
, "query");
1046 item
= purple_xmlnode_new_child(query
, "item");
1047 purple_xmlnode_set_attrib(item
, "jid", jid
);
1048 purple_xmlnode_set_attrib(item
, "affiliation", affiliation
);
1056 jabber_chat_affiliation_list_cb(JabberStream
*js
, const char *from
,
1057 JabberIqType type
, const char *id
,
1058 PurpleXmlNode
*packet
, gpointer data
)
1061 PurpleXmlNode
*query
, *item
;
1062 int chat_id
= GPOINTER_TO_INT(data
);
1065 if(!(chat
= jabber_chat_find_by_id(js
, chat_id
)))
1068 if (type
== JABBER_IQ_ERROR
)
1071 if(!(query
= purple_xmlnode_get_child(packet
, "query")))
1074 buf
= g_string_new(_("Affiliations:"));
1076 item
= purple_xmlnode_get_child(query
, "item");
1078 for( ; item
; item
= purple_xmlnode_get_next_twin(item
)) {
1079 const char *jid
= purple_xmlnode_get_attrib(item
, "jid");
1080 const char *affiliation
= purple_xmlnode_get_attrib(item
, "affiliation");
1081 if (jid
&& affiliation
)
1082 g_string_append_printf(buf
, "\n%s %s", jid
, affiliation
);
1085 buf
= g_string_append_c(buf
, '\n');
1086 buf
= g_string_append_len(buf
, _("No users found"), -1);
1089 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat
->conv
),
1090 buf
->str
, PURPLE_MESSAGE_NO_LOG
);
1092 g_string_free(buf
, TRUE
);
1095 gboolean
jabber_chat_affiliation_list(JabberChat
*chat
, const char *affiliation
)
1099 PurpleXmlNode
*query
, *item
;
1101 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
,
1102 "http://jabber.org/protocol/muc#admin");
1104 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1105 purple_xmlnode_set_attrib(iq
->node
, "to", room_jid
);
1107 query
= purple_xmlnode_get_child(iq
->node
, "query");
1108 item
= purple_xmlnode_new_child(query
, "item");
1109 purple_xmlnode_set_attrib(item
, "affiliation", affiliation
);
1111 jabber_iq_set_callback(iq
, jabber_chat_affiliation_list_cb
, GINT_TO_POINTER(chat
->id
));
1117 gboolean
jabber_chat_role_user(JabberChat
*chat
, const char *who
,
1118 const char *role
, const char *why
)
1122 PurpleXmlNode
*query
, *item
;
1123 JabberChatMember
*jcm
;
1125 jcm
= g_hash_table_lookup(chat
->members
, who
);
1127 if (!jcm
|| !jcm
->handle
)
1130 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
1131 "http://jabber.org/protocol/muc#admin");
1133 to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1134 purple_xmlnode_set_attrib(iq
->node
, "to", to
);
1137 query
= purple_xmlnode_get_child(iq
->node
, "query");
1138 item
= purple_xmlnode_new_child(query
, "item");
1139 purple_xmlnode_set_attrib(item
, "nick", jcm
->handle
);
1140 purple_xmlnode_set_attrib(item
, "role", role
);
1142 PurpleXmlNode
*reason
= purple_xmlnode_new_child(item
, "reason");
1143 purple_xmlnode_insert_data(reason
, why
, -1);
1151 static void jabber_chat_role_list_cb(JabberStream
*js
, const char *from
,
1152 JabberIqType type
, const char *id
,
1153 PurpleXmlNode
*packet
, gpointer data
)
1156 PurpleXmlNode
*query
, *item
;
1157 int chat_id
= GPOINTER_TO_INT(data
);
1160 if(!(chat
= jabber_chat_find_by_id(js
, chat_id
)))
1163 if (type
== JABBER_IQ_ERROR
)
1166 if(!(query
= purple_xmlnode_get_child(packet
, "query")))
1169 buf
= g_string_new(_("Roles:"));
1171 item
= purple_xmlnode_get_child(query
, "item");
1173 for( ; item
; item
= purple_xmlnode_get_next_twin(item
)) {
1174 const char *jid
= purple_xmlnode_get_attrib(item
, "jid");
1175 const char *role
= purple_xmlnode_get_attrib(item
, "role");
1177 g_string_append_printf(buf
, "\n%s %s", jid
, role
);
1180 buf
= g_string_append_c(buf
, '\n');
1181 buf
= g_string_append_len(buf
, _("No users found"), -1);
1184 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat
->conv
),
1185 buf
->str
, PURPLE_MESSAGE_NO_LOG
);
1187 g_string_free(buf
, TRUE
);
1190 gboolean
jabber_chat_role_list(JabberChat
*chat
, const char *role
)
1194 PurpleXmlNode
*query
, *item
;
1196 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
,
1197 "http://jabber.org/protocol/muc#admin");
1199 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1200 purple_xmlnode_set_attrib(iq
->node
, "to", room_jid
);
1202 query
= purple_xmlnode_get_child(iq
->node
, "query");
1203 item
= purple_xmlnode_new_child(query
, "item");
1204 purple_xmlnode_set_attrib(item
, "role", role
);
1206 jabber_iq_set_callback(iq
, jabber_chat_role_list_cb
, GINT_TO_POINTER(chat
->id
));
1212 static void jabber_chat_disco_traffic_cb(JabberStream
*js
, const char *from
,
1213 JabberIqType type
, const char *id
,
1214 PurpleXmlNode
*packet
, gpointer data
)
1218 PurpleXmlNode
*query
, *x
;
1220 int chat_id
= GPOINTER_TO_INT(data
);
1222 if(!(chat
= jabber_chat_find_by_id(js
, chat_id
)))
1225 /* defaults, in case the conference server doesn't
1226 * support this request */
1229 /* disabling this until more MUC servers support
1230 * announcing this */
1232 if (type
== JABBER_IQ_ERROR
) {
1236 if(!(query
= purple_xmlnode_get_child(packet
, "query")))
1239 chat
->xhtml
= FALSE
;
1241 for(x
= purple_xmlnode_get_child(query
, "feature"); x
; x
= purple_xmlnode_get_next_twin(x
)) {
1242 const char *var
= purple_xmlnode_get_attrib(x
, "var");
1244 if(var
&& !strcmp(var
, NS_XHTML_IM
)) {
1251 void jabber_chat_disco_traffic(JabberChat
*chat
)
1254 PurpleXmlNode
*query
;
1257 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1259 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
, NS_DISCO_INFO
);
1261 purple_xmlnode_set_attrib(iq
->node
, "to", room_jid
);
1263 query
= purple_xmlnode_get_child(iq
->node
, "query");
1265 purple_xmlnode_set_attrib(query
, "node", "http://jabber.org/protocol/muc#traffic");
1267 jabber_iq_set_callback(iq
, jabber_chat_disco_traffic_cb
, GINT_TO_POINTER(chat
->id
));
1276 gboolean
*all_support
;
1278 } JabberChatCapsData
;
1281 jabber_chat_all_participants_have_capability_foreach(gpointer key
,
1285 const gchar
*cap
= ((JabberChatCapsData
*) user_data
)->cap
;
1286 gboolean
*all_support
= ((JabberChatCapsData
*) user_data
)->all_support
;
1287 JabberBuddy
*jb
= ((JabberChatCapsData
*) user_data
)->jb
;
1288 JabberChatMember
*member
= (JabberChatMember
*) value
;
1289 const gchar
*resource
= member
->handle
;
1290 JabberBuddyResource
*jbr
= jabber_buddy_find_resource(jb
, resource
);
1293 if (*all_support
&& jabber_resource_has_capability(jbr
, cap
))
1294 *all_support
= TRUE
;
1296 *all_support
= FALSE
;
1301 jabber_chat_all_participants_have_capability(const JabberChat
*chat
,
1304 gchar
*chat_jid
= NULL
;
1305 JabberBuddy
*jb
= NULL
;
1306 gboolean all_support
= TRUE
;
1307 JabberChatCapsData data
;
1309 chat_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1310 jb
= jabber_buddy_find(chat
->js
, chat_jid
, FALSE
);
1314 data
.all_support
= &all_support
;
1317 g_hash_table_foreach(chat
->members
,
1318 jabber_chat_all_participants_have_capability_foreach
, &data
);
1320 all_support
= FALSE
;
1327 jabber_chat_get_num_participants(const JabberChat
*chat
)
1329 return g_hash_table_size(chat
->members
);