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 "prpl.h" /* for proto_chat_entry */
38 GList
*jabber_chat_info(PurpleConnection
*gc
)
41 struct proto_chat_entry
*pce
;
43 pce
= g_new0(struct proto_chat_entry
, 1);
44 pce
->label
= _("_Room:");
45 pce
->identifier
= "room";
47 m
= g_list_append(m
, pce
);
49 pce
= g_new0(struct proto_chat_entry
, 1);
50 pce
->label
= _("_Server:");
51 pce
->identifier
= "server";
53 m
= g_list_append(m
, pce
);
55 pce
= g_new0(struct proto_chat_entry
, 1);
56 pce
->label
= _("_Handle:");
57 pce
->identifier
= "handle";
59 m
= g_list_append(m
, pce
);
61 pce
= g_new0(struct proto_chat_entry
, 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
= gc
->proto_data
;
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(PurpleConversation
*conv
)
143 PurpleAccount
*account
= purple_conversation_get_account(conv
);
144 PurpleConnection
*gc
= purple_account_get_connection(account
);
150 id
= purple_conv_chat_get_id(PURPLE_CONV_CHAT(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
= gc
->proto_data
;
159 xmlnode
*message
, *body
, *x
, *invite
;
162 chat
= jabber_chat_find_by_id(js
, id
);
166 message
= xmlnode_new("message");
168 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
171 xmlnode_set_attrib(message
, "to", room_jid
);
172 x
= xmlnode_new_child(message
, "x");
173 xmlnode_set_namespace(x
, "http://jabber.org/protocol/muc#user");
174 invite
= xmlnode_new_child(x
, "invite");
175 xmlnode_set_attrib(invite
, "to", name
);
177 body
= xmlnode_new_child(invite
, "reason");
178 xmlnode_insert_data(body
, msg
, -1);
181 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
= xmlnode_new_child(message
, "body");
191 xmlnode_insert_data(body
, msg
, -1);
194 x
= xmlnode_new_child(message
, "x");
195 xmlnode_set_attrib(x
, "jid", room_jid
);
197 /* The better place for it! XEP-0249 style. */
199 xmlnode_set_attrib(x
, "reason", msg
);
200 xmlnode_set_namespace(x
, "jabber:x:conference");
203 jabber_send(js
, message
);
204 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 xmlnode
*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 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
= xmlnode_new_child(presence
, "x");
325 xmlnode_set_namespace(x
, "http://jabber.org/protocol/muc");
327 if (password
&& *password
) {
328 xmlnode
*p
= xmlnode_new_child(x
, "password");
329 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 xmlnode
*history
= xmlnode_new_child(x
, "history");
339 if (history_maxchars
&& *history_maxchars
) {
340 xmlnode_set_attrib(history
, "maxchars", history_maxchars
);
342 if (history_maxstanzas
&& *history_maxstanzas
) {
343 xmlnode_set_attrib(history
, "maxstanzas", history_maxstanzas
);
345 if (history_seconds
&& *history_seconds
) {
346 xmlnode_set_attrib(history
, "seconds", history_seconds
);
348 if (history_since_string
&& *history_since_string
) {
349 xmlnode_set_attrib(history
, "since", history_since_string
);
353 jabber_send(js
, presence
);
354 xmlnode_free(presence
);
359 void jabber_chat_join(PurpleConnection
*gc
, GHashTable
*data
)
361 char *room
, *server
, *handle
, *passwd
;
363 JabberStream
*js
= gc
->proto_data
;
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"),
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_serv_got_join_chat_failed(gc
, data
);
391 } else if(!jabber_resourceprep_validate(handle
)) {
392 char *buf
= g_strdup_printf(_("%s is not a valid room handle"), handle
);
393 purple_notify_error(gc
, _("Invalid Room Handle"),
394 _("Invalid Room Handle"), buf
);
395 purple_serv_got_join_chat_failed(gc
, data
);
400 /* Normalize the room and server parameters */
401 tmp
= g_strdup_printf("%s@%s", room
, server
);
402 jid
= jabber_id_new(tmp
);
406 /* TODO: Error message */
408 g_return_if_reached();
412 * Now that we've done all that nice core-interface stuff, let's join
415 jabber_join_chat(js
, jid
->node
, jid
->domain
, handle
, passwd
, data
);
419 void jabber_chat_leave(PurpleConnection
*gc
, int id
)
421 JabberStream
*js
= gc
->proto_data
;
422 JabberChat
*chat
= jabber_chat_find_by_id(js
, id
);
428 jabber_chat_part(chat
, NULL
);
433 void jabber_chat_destroy(JabberChat
*chat
)
435 JabberStream
*js
= chat
->js
;
436 char *room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
438 g_hash_table_remove(js
->chats
, room_jid
);
442 void jabber_chat_free(JabberChat
*chat
)
444 if(chat
->config_dialog_handle
)
445 purple_request_close(chat
->config_dialog_type
, chat
->config_dialog_handle
);
448 g_free(chat
->server
);
449 g_free(chat
->handle
);
450 g_hash_table_destroy(chat
->members
);
451 g_hash_table_destroy(chat
->components
);
455 gboolean
jabber_chat_find_buddy(PurpleConversation
*conv
, const char *name
)
457 return purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv
), name
);
460 char *jabber_chat_buddy_real_name(PurpleConnection
*gc
, int id
, const char *who
)
462 JabberStream
*js
= gc
->proto_data
;
464 JabberChatMember
*jcm
;
466 chat
= jabber_chat_find_by_id(js
, id
);
471 jcm
= g_hash_table_lookup(chat
->members
, who
);
472 if (jcm
!= NULL
&& jcm
->jid
)
473 return g_strdup(jcm
->jid
);
476 return g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
, who
);
479 static void jabber_chat_room_configure_x_data_cb(JabberStream
*js
, xmlnode
*result
, gpointer data
)
481 JabberChat
*chat
= data
;
484 char *to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
486 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "http://jabber.org/protocol/muc#owner");
487 xmlnode_set_attrib(iq
->node
, "to", to
);
490 query
= xmlnode_get_child(iq
->node
, "query");
492 xmlnode_insert_child(query
, result
);
497 static void jabber_chat_room_configure_cb(JabberStream
*js
, const char *from
,
498 JabberIqType type
, const char *id
,
499 xmlnode
*packet
, gpointer data
)
509 if (type
== JABBER_IQ_RESULT
) {
510 jid
= jabber_id_new(from
);
515 chat
= jabber_chat_find(js
, jid
->node
, jid
->domain
);
521 if(!(query
= xmlnode_get_child(packet
, "query")))
524 for(x
= xmlnode_get_child(query
, "x"); x
; x
= xmlnode_get_next_twin(x
)) {
526 if(!(xmlns
= xmlnode_get_namespace(x
)))
529 if(purple_strequal(xmlns
, "jabber:x:data")) {
530 chat
->config_dialog_type
= PURPLE_REQUEST_FIELDS
;
531 chat
->config_dialog_handle
= jabber_x_data_request(js
, x
, jabber_chat_room_configure_x_data_cb
, chat
);
535 } else if (type
== JABBER_IQ_ERROR
) {
536 char *msg
= jabber_parse_error(js
, packet
, NULL
);
538 purple_notify_error(js
->gc
, _("Configuration error"), _("Configuration error"), msg
);
545 msg
= g_strdup_printf("Unable to configure room %s", from
);
547 purple_notify_info(js
->gc
, _("Unable to configure"), _("Unable to configure"), msg
);
552 void jabber_chat_request_room_configure(JabberChat
*chat
) {
559 chat
->config_dialog_handle
= NULL
;
562 purple_notify_error(chat
->js
->gc
, _("Room Configuration Error"), _("Room Configuration Error"),
563 _("This room is not capable of being configured"));
567 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
,
568 "http://jabber.org/protocol/muc#owner");
569 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
571 xmlnode_set_attrib(iq
->node
, "to", room_jid
);
573 jabber_iq_set_callback(iq
, jabber_chat_room_configure_cb
, NULL
);
580 void jabber_chat_create_instant_room(JabberChat
*chat
) {
588 chat
->config_dialog_handle
= NULL
;
590 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
591 "http://jabber.org/protocol/muc#owner");
592 query
= xmlnode_get_child(iq
->node
, "query");
593 x
= xmlnode_new_child(query
, "x");
594 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
596 xmlnode_set_attrib(iq
->node
, "to", room_jid
);
597 xmlnode_set_namespace(x
, "jabber:x:data");
598 xmlnode_set_attrib(x
, "type", "submit");
606 jabber_chat_register_x_data_result_cb(JabberStream
*js
, const char *from
,
607 JabberIqType type
, const char *id
,
608 xmlnode
*packet
, gpointer data
)
610 if (type
== JABBER_IQ_ERROR
) {
611 char *msg
= jabber_parse_error(js
, packet
, NULL
);
613 purple_notify_error(js
->gc
, _("Registration error"), _("Registration error"), msg
);
621 static void jabber_chat_register_x_data_cb(JabberStream
*js
, xmlnode
*result
, gpointer data
)
623 JabberChat
*chat
= data
;
626 char *to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
628 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
629 xmlnode_set_attrib(iq
->node
, "to", to
);
632 query
= xmlnode_get_child(iq
->node
, "query");
634 xmlnode_insert_child(query
, result
);
636 jabber_iq_set_callback(iq
, jabber_chat_register_x_data_result_cb
, NULL
);
641 static void jabber_chat_register_cb(JabberStream
*js
, const char *from
,
642 JabberIqType type
, const char *id
,
643 xmlnode
*packet
, gpointer data
)
653 if (type
== JABBER_IQ_RESULT
) {
654 jid
= jabber_id_new(from
);
659 chat
= jabber_chat_find(js
, jid
->node
, jid
->domain
);
665 if(!(query
= xmlnode_get_child(packet
, "query")))
668 for(x
= xmlnode_get_child(query
, "x"); x
; x
= xmlnode_get_next_twin(x
)) {
671 if(!(xmlns
= xmlnode_get_namespace(x
)))
674 if(purple_strequal(xmlns
, "jabber:x:data")) {
675 jabber_x_data_request(js
, x
, jabber_chat_register_x_data_cb
, chat
);
679 } else if (type
== JABBER_IQ_ERROR
) {
680 char *msg
= jabber_parse_error(js
, packet
, NULL
);
682 purple_notify_error(js
->gc
, _("Registration error"), _("Registration error"), msg
);
689 msg
= g_strdup_printf("Unable to configure room %s", from
);
691 purple_notify_info(js
->gc
, _("Unable to configure"), _("Unable to configure"), msg
);
696 void jabber_chat_register(JabberChat
*chat
)
704 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
706 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
, "jabber:iq:register");
707 xmlnode_set_attrib(iq
->node
, "to", room_jid
);
710 jabber_iq_set_callback(iq
, jabber_chat_register_cb
, NULL
);
715 /* merge this with the function below when we get everyone on the same page wrt /commands */
716 void jabber_chat_change_topic(JabberChat
*chat
, const char *topic
)
720 jm
= g_new0(JabberMessage
, 1);
722 jm
->type
= JABBER_MESSAGE_GROUPCHAT
;
723 jm
->to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
726 jm
->subject
= g_strdup(topic
);
728 jm
->subject
= g_strdup("");
730 jabber_message_send(jm
);
731 jabber_message_free(jm
);
734 void jabber_chat_set_topic(PurpleConnection
*gc
, int id
, const char *topic
)
736 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
737 JabberChat
*chat
= jabber_chat_find_by_id(js
, id
);
742 jabber_chat_change_topic(chat
, topic
);
746 gboolean
jabber_chat_change_nick(JabberChat
*chat
, const char *nick
)
750 PurpleAccount
*account
;
751 PurpleStatus
*status
;
752 JabberBuddyState state
;
757 purple_conv_chat_write(PURPLE_CONV_CHAT(chat
->conv
), "",
758 _("Nick changing not supported in non-MUC chatrooms"),
759 PURPLE_MESSAGE_SYSTEM
, time(NULL
));
763 account
= purple_connection_get_account(chat
->js
->gc
);
764 status
= purple_account_get_active_status(account
);
766 purple_status_to_jabber(status
, &state
, &msg
, &priority
);
768 presence
= jabber_presence_create_js(chat
->js
, state
, msg
, priority
);
769 full_jid
= g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
, nick
);
770 xmlnode_set_attrib(presence
, "to", full_jid
);
774 jabber_send(chat
->js
, presence
);
775 xmlnode_free(presence
);
780 void jabber_chat_part(JabberChat
*chat
, const char *msg
)
785 room_jid
= g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
,
787 presence
= xmlnode_new("presence");
788 xmlnode_set_attrib(presence
, "to", room_jid
);
789 xmlnode_set_attrib(presence
, "type", "unavailable");
791 xmlnode
*status
= xmlnode_new_child(presence
, "status");
792 xmlnode_insert_data(status
, msg
, -1);
794 jabber_send(chat
->js
, presence
);
796 xmlnode_free(presence
);
800 static void roomlist_disco_result_cb(JabberStream
*js
, const char *from
,
801 JabberIqType type
, const char *id
,
802 xmlnode
*packet
, gpointer data
)
810 if (type
== JABBER_IQ_ERROR
) {
811 char *err
= jabber_parse_error(js
, packet
, NULL
);
812 purple_notify_error(js
->gc
, _("Error"),
813 _("Error retrieving room list"), err
);
814 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
815 purple_roomlist_unref(js
->roomlist
);
821 if(!(query
= xmlnode_get_child(packet
, "query"))) {
822 char *err
= jabber_parse_error(js
, packet
, NULL
);
823 purple_notify_error(js
->gc
, _("Error"),
824 _("Error retrieving room list"), err
);
825 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
826 purple_roomlist_unref(js
->roomlist
);
832 for(item
= xmlnode_get_child(query
, "item"); item
;
833 item
= xmlnode_get_next_twin(item
)) {
835 PurpleRoomlistRoom
*room
;
838 if(!(jid
= jabber_id_new(xmlnode_get_attrib(item
, "jid"))))
840 name
= xmlnode_get_attrib(item
, "name");
843 room
= purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM
, jid
->node
, NULL
);
844 purple_roomlist_room_add_field(js
->roomlist
, room
, jid
->node
);
845 purple_roomlist_room_add_field(js
->roomlist
, room
, jid
->domain
);
846 purple_roomlist_room_add_field(js
->roomlist
, room
, name
? name
: "");
847 purple_roomlist_room_add(js
->roomlist
, room
);
851 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
852 purple_roomlist_unref(js
->roomlist
);
856 static void roomlist_cancel_cb(JabberStream
*js
, const char *server
) {
858 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
859 purple_roomlist_unref(js
->roomlist
);
864 static void roomlist_ok_cb(JabberStream
*js
, const char *server
)
871 if(!server
|| !*server
) {
872 purple_notify_error(js
->gc
, _("Invalid Server"), _("Invalid Server"), NULL
);
873 purple_roomlist_set_in_progress(js
->roomlist
, FALSE
);
877 purple_roomlist_set_in_progress(js
->roomlist
, TRUE
);
879 iq
= jabber_iq_new_query(js
, JABBER_IQ_GET
, NS_DISCO_ITEMS
);
881 xmlnode_set_attrib(iq
->node
, "to", server
);
883 jabber_iq_set_callback(iq
, roomlist_disco_result_cb
, NULL
);
888 char *jabber_roomlist_room_serialize(PurpleRoomlistRoom
*room
)
891 return g_strdup_printf("%s@%s", (char*)room
->fields
->data
, (char*)room
->fields
->next
->data
);
894 PurpleRoomlist
*jabber_roomlist_get_list(PurpleConnection
*gc
)
896 JabberStream
*js
= gc
->proto_data
;
897 GList
*fields
= NULL
;
898 PurpleRoomlistField
*f
;
901 purple_roomlist_unref(js
->roomlist
);
903 js
->roomlist
= purple_roomlist_new(purple_connection_get_account(js
->gc
));
905 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, "", "room", TRUE
);
906 fields
= g_list_append(fields
, f
);
908 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, "", "server", TRUE
);
909 fields
= g_list_append(fields
, f
);
911 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, _("Description"), "description", FALSE
);
912 fields
= g_list_append(fields
, f
);
914 purple_roomlist_set_fields(js
->roomlist
, fields
);
917 purple_request_input(gc
, _("Enter a Conference Server"), _("Enter a Conference Server"),
918 _("Select a conference server to query"),
919 js
->chat_servers
? js
->chat_servers
->data
: NULL
,
921 _("Find Rooms"), PURPLE_CALLBACK(roomlist_ok_cb
),
922 _("Cancel"), PURPLE_CALLBACK(roomlist_cancel_cb
),
923 purple_connection_get_account(gc
), NULL
, NULL
,
929 void jabber_roomlist_cancel(PurpleRoomlist
*list
)
931 PurpleConnection
*gc
;
934 gc
= purple_account_get_connection(list
->account
);
937 purple_roomlist_set_in_progress(list
, FALSE
);
939 if (js
->roomlist
== list
) {
941 purple_roomlist_unref(list
);
945 void jabber_chat_member_free(JabberChatMember
*jcm
)
952 void jabber_chat_track_handle(JabberChat
*chat
, const char *handle
,
953 const char *jid
, const char *affiliation
, const char *role
)
955 JabberChatMember
*jcm
= g_new0(JabberChatMember
, 1);
957 jcm
->handle
= g_strdup(handle
);
958 jcm
->jid
= g_strdup(jid
);
960 g_hash_table_replace(chat
->members
, jcm
->handle
, jcm
);
962 /* XXX: keep track of role and affiliation */
965 void jabber_chat_remove_handle(JabberChat
*chat
, const char *handle
)
967 g_hash_table_remove(chat
->members
, handle
);
970 gboolean
jabber_chat_ban_user(JabberChat
*chat
, const char *who
, const char *why
)
972 JabberChatMember
*jcm
;
976 xmlnode
*query
, *item
, *reason
;
978 jcm
= g_hash_table_lookup(chat
->members
, who
);
981 else if (strchr(who
, '@') != NULL
)
986 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
987 "http://jabber.org/protocol/muc#admin");
989 to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
990 xmlnode_set_attrib(iq
->node
, "to", to
);
993 query
= xmlnode_get_child(iq
->node
, "query");
994 item
= xmlnode_new_child(query
, "item");
995 xmlnode_set_attrib(item
, "jid", jid
);
996 xmlnode_set_attrib(item
, "affiliation", "outcast");
998 reason
= xmlnode_new_child(item
, "reason");
999 xmlnode_insert_data(reason
, why
, -1);
1007 gboolean
jabber_chat_affiliate_user(JabberChat
*chat
, const char *who
, const char *affiliation
)
1009 JabberChatMember
*jcm
;
1013 xmlnode
*query
, *item
;
1015 jcm
= g_hash_table_lookup(chat
->members
, who
);
1016 if (jcm
&& jcm
->jid
)
1018 else if (strchr(who
, '@') != NULL
)
1023 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
1024 "http://jabber.org/protocol/muc#admin");
1026 to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1027 xmlnode_set_attrib(iq
->node
, "to", to
);
1030 query
= xmlnode_get_child(iq
->node
, "query");
1031 item
= xmlnode_new_child(query
, "item");
1032 xmlnode_set_attrib(item
, "jid", jid
);
1033 xmlnode_set_attrib(item
, "affiliation", affiliation
);
1041 jabber_chat_affiliation_list_cb(JabberStream
*js
, const char *from
,
1042 JabberIqType type
, const char *id
,
1043 xmlnode
*packet
, gpointer data
)
1046 xmlnode
*query
, *item
;
1047 int chat_id
= GPOINTER_TO_INT(data
);
1050 if(!(chat
= jabber_chat_find_by_id(js
, chat_id
)))
1053 if (type
== JABBER_IQ_ERROR
)
1056 if(!(query
= xmlnode_get_child(packet
, "query")))
1059 buf
= g_string_new(_("Affiliations:"));
1061 item
= xmlnode_get_child(query
, "item");
1063 for( ; item
; item
= xmlnode_get_next_twin(item
)) {
1064 const char *jid
= xmlnode_get_attrib(item
, "jid");
1065 const char *affiliation
= xmlnode_get_attrib(item
, "affiliation");
1066 if (jid
&& affiliation
)
1067 g_string_append_printf(buf
, "\n%s %s", jid
, affiliation
);
1070 buf
= g_string_append_c(buf
, '\n');
1071 buf
= g_string_append_len(buf
, _("No users found"), -1);
1074 purple_conv_chat_write(PURPLE_CONV_CHAT(chat
->conv
), "", buf
->str
,
1075 PURPLE_MESSAGE_SYSTEM
| PURPLE_MESSAGE_NO_LOG
, time(NULL
));
1077 g_string_free(buf
, TRUE
);
1080 gboolean
jabber_chat_affiliation_list(JabberChat
*chat
, const char *affiliation
)
1084 xmlnode
*query
, *item
;
1086 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
,
1087 "http://jabber.org/protocol/muc#admin");
1089 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1090 xmlnode_set_attrib(iq
->node
, "to", room_jid
);
1092 query
= xmlnode_get_child(iq
->node
, "query");
1093 item
= xmlnode_new_child(query
, "item");
1094 xmlnode_set_attrib(item
, "affiliation", affiliation
);
1096 jabber_iq_set_callback(iq
, jabber_chat_affiliation_list_cb
, GINT_TO_POINTER(chat
->id
));
1102 gboolean
jabber_chat_role_user(JabberChat
*chat
, const char *who
,
1103 const char *role
, const char *why
)
1107 xmlnode
*query
, *item
;
1108 JabberChatMember
*jcm
;
1110 jcm
= g_hash_table_lookup(chat
->members
, who
);
1112 if (!jcm
|| !jcm
->handle
)
1115 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_SET
,
1116 "http://jabber.org/protocol/muc#admin");
1118 to
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1119 xmlnode_set_attrib(iq
->node
, "to", to
);
1122 query
= xmlnode_get_child(iq
->node
, "query");
1123 item
= xmlnode_new_child(query
, "item");
1124 xmlnode_set_attrib(item
, "nick", jcm
->handle
);
1125 xmlnode_set_attrib(item
, "role", role
);
1127 xmlnode
*reason
= xmlnode_new_child(item
, "reason");
1128 xmlnode_insert_data(reason
, why
, -1);
1136 static void jabber_chat_role_list_cb(JabberStream
*js
, const char *from
,
1137 JabberIqType type
, const char *id
,
1138 xmlnode
*packet
, gpointer data
)
1141 xmlnode
*query
, *item
;
1142 int chat_id
= GPOINTER_TO_INT(data
);
1145 if(!(chat
= jabber_chat_find_by_id(js
, chat_id
)))
1148 if (type
== JABBER_IQ_ERROR
)
1151 if(!(query
= xmlnode_get_child(packet
, "query")))
1154 buf
= g_string_new(_("Roles:"));
1156 item
= xmlnode_get_child(query
, "item");
1158 for( ; item
; item
= xmlnode_get_next_twin(item
)) {
1159 const char *jid
= xmlnode_get_attrib(item
, "jid");
1160 const char *role
= xmlnode_get_attrib(item
, "role");
1162 g_string_append_printf(buf
, "\n%s %s", jid
, role
);
1165 buf
= g_string_append_c(buf
, '\n');
1166 buf
= g_string_append_len(buf
, _("No users found"), -1);
1169 purple_conv_chat_write(PURPLE_CONV_CHAT(chat
->conv
), "", buf
->str
,
1170 PURPLE_MESSAGE_SYSTEM
| PURPLE_MESSAGE_NO_LOG
, time(NULL
));
1172 g_string_free(buf
, TRUE
);
1175 gboolean
jabber_chat_role_list(JabberChat
*chat
, const char *role
)
1179 xmlnode
*query
, *item
;
1181 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
,
1182 "http://jabber.org/protocol/muc#admin");
1184 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1185 xmlnode_set_attrib(iq
->node
, "to", room_jid
);
1187 query
= xmlnode_get_child(iq
->node
, "query");
1188 item
= xmlnode_new_child(query
, "item");
1189 xmlnode_set_attrib(item
, "role", role
);
1191 jabber_iq_set_callback(iq
, jabber_chat_role_list_cb
, GINT_TO_POINTER(chat
->id
));
1197 static void jabber_chat_disco_traffic_cb(JabberStream
*js
, const char *from
,
1198 JabberIqType type
, const char *id
,
1199 xmlnode
*packet
, gpointer data
)
1205 int chat_id
= GPOINTER_TO_INT(data
);
1207 if(!(chat
= jabber_chat_find_by_id(js
, chat_id
)))
1210 /* defaults, in case the conference server doesn't
1211 * support this request */
1214 /* disabling this until more MUC servers support
1215 * announcing this */
1217 if (type
== JABBER_IQ_ERROR
) {
1221 if(!(query
= xmlnode_get_child(packet
, "query")))
1224 chat
->xhtml
= FALSE
;
1226 for(x
= xmlnode_get_child(query
, "feature"); x
; x
= xmlnode_get_next_twin(x
)) {
1227 const char *var
= xmlnode_get_attrib(x
, "var");
1229 if(purple_strequal(var
, NS_XHTML_IM
)) {
1236 void jabber_chat_disco_traffic(JabberChat
*chat
)
1242 room_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1244 iq
= jabber_iq_new_query(chat
->js
, JABBER_IQ_GET
, NS_DISCO_INFO
);
1246 xmlnode_set_attrib(iq
->node
, "to", room_jid
);
1248 query
= xmlnode_get_child(iq
->node
, "query");
1250 xmlnode_set_attrib(query
, "node", "http://jabber.org/protocol/muc#traffic");
1252 jabber_iq_set_callback(iq
, jabber_chat_disco_traffic_cb
, GINT_TO_POINTER(chat
->id
));
1261 gboolean
*all_support
;
1263 } JabberChatCapsData
;
1266 jabber_chat_all_participants_have_capability_foreach(gpointer key
,
1270 const gchar
*cap
= ((JabberChatCapsData
*) user_data
)->cap
;
1271 gboolean
*all_support
= ((JabberChatCapsData
*) user_data
)->all_support
;
1272 JabberBuddy
*jb
= ((JabberChatCapsData
*) user_data
)->jb
;
1273 JabberChatMember
*member
= (JabberChatMember
*) value
;
1274 const gchar
*resource
= member
->handle
;
1275 JabberBuddyResource
*jbr
= jabber_buddy_find_resource(jb
, resource
);
1278 if (*all_support
&& jabber_resource_has_capability(jbr
, cap
))
1279 *all_support
= TRUE
;
1281 *all_support
= FALSE
;
1286 jabber_chat_all_participants_have_capability(const JabberChat
*chat
,
1289 gchar
*chat_jid
= NULL
;
1290 JabberBuddy
*jb
= NULL
;
1291 gboolean all_support
= TRUE
;
1292 JabberChatCapsData data
;
1294 chat_jid
= g_strdup_printf("%s@%s", chat
->room
, chat
->server
);
1295 jb
= jabber_buddy_find(chat
->js
, chat_jid
, FALSE
);
1299 data
.all_support
= &all_support
;
1302 g_hash_table_foreach(chat
->members
,
1303 jabber_chat_all_participants_have_capability_foreach
, &data
);
1305 all_support
= FALSE
;
1312 jabber_chat_get_num_participants(const JabberChat
*chat
)
1314 return g_hash_table_size(chat
->members
);