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
26 #include "accountopt.h"
27 #include "buddylist.h"
30 #include "connection.h"
31 #include "conversation.h"
36 #include "pluginpref.h"
52 #include "google/google.h"
53 #include "google/google_p2p.h"
54 #include "google/google_roster.h"
55 #include "google/google_session.h"
69 #include "adhoccommands.h"
73 #include "jingle/jingle.h"
74 #include "jingle/content.h"
75 #include "jingle/iceudp.h"
76 #include "jingle/rawudp.h"
77 #include "jingle/rtp.h"
78 #include "jingle/session.h"
80 #define PING_TIMEOUT 60
81 /* Send a whitespace keepalive to the server if we haven't sent
82 * anything in the last 120 seconds
84 #define DEFAULT_INACTIVITY_TIME 120
86 GList
*jabber_features
= NULL
;
87 GList
*jabber_identities
= NULL
;
89 static PurpleProtocol
*xmpp_protocol
= NULL
;
90 static PurpleProtocol
*gtalk_protocol
= NULL
;
92 static GHashTable
*jabber_cmds
= NULL
; /* PurpleProtocol * => GSList of ids */
94 static gint plugin_ref
= 0;
96 static void jabber_unregister_account_cb(JabberStream
*js
);
98 static void jabber_stream_init(JabberStream
*js
)
102 g_free(js
->stream_id
);
103 js
->stream_id
= NULL
;
105 open_stream
= g_strdup_printf("<stream:stream to='%s' "
106 "xmlns='" NS_XMPP_CLIENT
"' "
107 "xmlns:stream='" NS_XMPP_STREAMS
"' "
110 /* setup the parser fresh for each stream */
111 jabber_parser_setup(js
);
112 jabber_send_raw(js
, open_stream
, -1);
118 jabber_session_initialized_cb(JabberStream
*js
, const char *from
,
119 JabberIqType type
, const char *id
,
120 PurpleXmlNode
*packet
, gpointer data
)
122 if (type
== JABBER_IQ_RESULT
) {
123 jabber_disco_items_server(js
);
124 if(js
->unregistration
)
125 jabber_unregister_account_cb(js
);
127 purple_connection_error(js
->gc
,
128 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
129 ("Error initializing session"));
133 static void jabber_session_init(JabberStream
*js
)
135 JabberIq
*iq
= jabber_iq_new(js
, JABBER_IQ_SET
);
136 PurpleXmlNode
*session
;
138 jabber_iq_set_callback(iq
, jabber_session_initialized_cb
, NULL
);
140 session
= purple_xmlnode_new_child(iq
->node
, "session");
141 purple_xmlnode_set_namespace(session
, NS_XMPP_SESSION
);
146 static void jabber_bind_result_cb(JabberStream
*js
, const char *from
,
147 JabberIqType type
, const char *id
,
148 PurpleXmlNode
*packet
, gpointer data
)
152 if (type
== JABBER_IQ_RESULT
&&
153 (bind
= purple_xmlnode_get_child_with_namespace(packet
, "bind", NS_XMPP_BIND
))) {
156 if((jid
= purple_xmlnode_get_child(bind
, "jid")) && (full_jid
= purple_xmlnode_get_data(jid
))) {
157 jabber_id_free(js
->user
);
159 js
->user
= jabber_id_new(full_jid
);
160 if (js
->user
== NULL
) {
161 purple_connection_error(js
->gc
,
162 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
163 _("Invalid response from server"));
168 js
->user_jb
= jabber_buddy_find(js
, full_jid
, TRUE
);
169 js
->user_jb
->subscription
|= JABBER_SUB_BOTH
;
171 purple_connection_set_display_name(js
->gc
, full_jid
);
176 PurpleConnectionError reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
177 char *msg
= jabber_parse_error(js
, packet
, &reason
);
178 purple_connection_error(js
->gc
, reason
, msg
);
184 jabber_session_init(js
);
187 static char *jabber_prep_resource(char *input
) {
188 char hostname
[256], /* current hostname */
191 /* Empty resource == don't send any */
192 if (input
== NULL
|| *input
== '\0')
195 if (strstr(input
, "__HOSTNAME__") == NULL
)
196 return g_strdup(input
);
198 /* Replace __HOSTNAME__ with hostname */
199 if (gethostname(hostname
, sizeof(hostname
) - 1)) {
200 purple_debug_warning("jabber", "gethostname: %s\n", g_strerror(errno
));
201 /* according to glibc doc, the only time an error is returned
202 is if the hostname is longer than the buffer, in which case
203 glibc 2.2+ would still fill the buffer with partial
204 hostname, so maybe we want to detect that and use it
207 g_strlcpy(hostname
, "localhost", sizeof(hostname
));
209 hostname
[sizeof(hostname
) - 1] = '\0';
211 /* We want only the short hostname, not the FQDN - this will prevent the
212 * resource string from being unreasonably long on systems which stuff the
213 * whole FQDN in the hostname */
214 if((dot
= strchr(hostname
, '.')))
217 return purple_strreplace(input
, "__HOSTNAME__", hostname
);
221 jabber_process_starttls(JabberStream
*js
, PurpleXmlNode
*packet
)
224 PurpleXmlNode
*starttls
;
226 PurpleAccount
*account
;
228 account
= purple_connection_get_account(js
->gc
);
231 * This code DOES NOT EXIST, will never be enabled by default, and
232 * will never ever be supported (by me).
233 * It's literally *only* for developer testing.
236 const gchar
*connection_security
= purple_account_get_string(account
, "connection_security", JABBER_DEFAULT_REQUIRE_TLS
);
237 if (!g_str_equal(connection_security
, "none")) {
239 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1);
245 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1);
250 starttls
= purple_xmlnode_get_child(packet
, "starttls");
251 if(purple_xmlnode_get_child(starttls
, "required")) {
252 purple_connection_error(js
->gc
,
253 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
254 _("Server requires TLS/SSL, but no TLS/SSL support was found."));
258 if (g_str_equal("require_tls", purple_account_get_string(account
, "connection_security", JABBER_DEFAULT_REQUIRE_TLS
))) {
259 purple_connection_error(js
->gc
,
260 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
261 _("You require encryption, but no TLS/SSL support was found."));
269 void jabber_stream_features_parse(JabberStream
*js
, PurpleXmlNode
*packet
)
271 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
272 const char *connection_security
=
273 purple_account_get_string(account
, "connection_security", JABBER_DEFAULT_REQUIRE_TLS
);
275 if (purple_xmlnode_get_child(packet
, "starttls")) {
276 if (jabber_process_starttls(js
, packet
)) {
277 jabber_stream_set_state(js
, JABBER_STREAM_INITIALIZING_ENCRYPTION
);
280 } else if (g_str_equal(connection_security
, "require_tls") && !jabber_stream_is_ssl(js
)) {
281 purple_connection_error(js
->gc
,
282 PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
,
283 _("You require encryption, but it is not available on this server."));
287 if(js
->registration
) {
288 jabber_register_start(js
);
289 } else if(purple_xmlnode_get_child(packet
, "mechanisms")) {
290 jabber_stream_set_state(js
, JABBER_STREAM_AUTHENTICATING
);
291 jabber_auth_start(js
, packet
);
292 } else if(purple_xmlnode_get_child(packet
, "bind")) {
293 PurpleXmlNode
*bind
, *resource
;
294 char *requested_resource
;
295 JabberIq
*iq
= jabber_iq_new(js
, JABBER_IQ_SET
);
296 bind
= purple_xmlnode_new_child(iq
->node
, "bind");
297 purple_xmlnode_set_namespace(bind
, NS_XMPP_BIND
);
298 requested_resource
= jabber_prep_resource(js
->user
->resource
);
300 if (requested_resource
!= NULL
) {
301 resource
= purple_xmlnode_new_child(bind
, "resource");
302 purple_xmlnode_insert_data(resource
, requested_resource
, -1);
303 g_free(requested_resource
);
306 jabber_iq_set_callback(iq
, jabber_bind_result_cb
, NULL
);
309 } else if (purple_xmlnode_get_child_with_namespace(packet
, "ver", NS_ROSTER_VERSIONING
)) {
310 js
->server_caps
|= JABBER_CAP_ROSTER_VERSIONING
;
311 } else /* if(purple_xmlnode_get_child_with_namespace(packet, "auth")) */ {
312 /* If we get an empty stream:features packet, or we explicitly get
313 * an auth feature with namespace http://jabber.org/features/iq-auth
314 * we should revert back to iq:auth authentication, even though we're
315 * connecting to an XMPP server. */
316 jabber_stream_set_state(js
, JABBER_STREAM_AUTHENTICATING
);
317 jabber_auth_start_old(js
);
321 static void jabber_stream_handle_error(JabberStream
*js
, PurpleXmlNode
*packet
)
323 PurpleConnectionError reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
324 char *msg
= jabber_parse_error(js
, packet
, &reason
);
326 purple_connection_error(js
->gc
, reason
, msg
);
331 static void tls_init(JabberStream
*js
);
333 void jabber_process_packet(JabberStream
*js
, PurpleXmlNode
**packet
)
338 purple_signal_emit(purple_connection_get_protocol(js
->gc
), "jabber-receiving-xmlnode", js
->gc
, packet
);
340 /* if the signal leaves us with a null packet, we're done */
344 name
= (*packet
)->name
;
345 xmlns
= purple_xmlnode_get_namespace(*packet
);
347 if(!strcmp((*packet
)->name
, "iq")) {
348 jabber_iq_parse(js
, *packet
);
349 } else if(!strcmp((*packet
)->name
, "presence")) {
350 jabber_presence_parse(js
, *packet
);
351 } else if(!strcmp((*packet
)->name
, "message")) {
352 jabber_message_parse(js
, *packet
);
353 } else if (purple_strequal(xmlns
, NS_XMPP_STREAMS
)) {
354 if (g_str_equal(name
, "features"))
355 jabber_stream_features_parse(js
, *packet
);
356 else if (g_str_equal(name
, "error"))
357 jabber_stream_handle_error(js
, *packet
);
358 } else if (purple_strequal(xmlns
, NS_XMPP_SASL
)) {
359 if (js
->state
!= JABBER_STREAM_AUTHENTICATING
)
360 purple_debug_warning("jabber", "Ignoring spurious SASL stanza %s\n", name
);
362 if (g_str_equal(name
, "challenge"))
363 jabber_auth_handle_challenge(js
, *packet
);
364 else if (g_str_equal(name
, "success"))
365 jabber_auth_handle_success(js
, *packet
);
366 else if (g_str_equal(name
, "failure"))
367 jabber_auth_handle_failure(js
, *packet
);
369 } else if (purple_strequal(xmlns
, NS_XMPP_TLS
)) {
370 if (js
->state
!= JABBER_STREAM_INITIALIZING_ENCRYPTION
|| js
->gsc
)
371 purple_debug_warning("jabber", "Ignoring spurious %s\n", name
);
373 if (g_str_equal(name
, "proceed"))
375 /* TODO: Handle <failure/>, I guess? */
378 purple_debug_warning("jabber", "Unknown packet: %s\n", (*packet
)->name
);
382 static int jabber_do_send(JabberStream
*js
, const char *data
, int len
)
387 ret
= purple_ssl_write(js
->gsc
, data
, len
);
389 ret
= write(js
->fd
, data
, len
);
394 static void jabber_send_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
396 JabberStream
*js
= data
;
397 const gchar
*output
= NULL
;
400 writelen
= purple_circular_buffer_get_max_read(js
->write_buffer
);
401 output
= purple_circular_buffer_get_output(js
->write_buffer
);
404 purple_input_remove(js
->writeh
);
409 ret
= jabber_do_send(js
, output
, writelen
);
411 if (ret
< 0 && errno
== EAGAIN
)
414 gchar
*tmp
= g_strdup_printf(_("Lost connection with server: %s"),
416 purple_connection_error(js
->gc
,
417 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
422 purple_circular_buffer_mark_read(js
->write_buffer
, ret
);
425 static gboolean
do_jabber_send_raw(JabberStream
*js
, const char *data
, int len
)
428 gboolean success
= TRUE
;
430 g_return_val_if_fail(len
> 0, FALSE
);
432 if (js
->state
== JABBER_STREAM_CONNECTED
)
433 jabber_stream_restart_inactivity_timer(js
);
436 ret
= jabber_do_send(js
, data
, len
);
442 if (ret
< 0 && errno
!= EAGAIN
) {
443 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
445 * The server may have closed the socket (on a stream error), so if
446 * we're disconnecting, don't generate (possibly another) error that
447 * (for some UIs) would mask the first.
449 if (!purple_account_is_disconnecting(account
)) {
450 gchar
*tmp
= g_strdup_printf(_("Lost connection with server: %s"),
452 purple_connection_error(js
->gc
,
453 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
458 } else if (ret
< len
) {
462 js
->writeh
= purple_input_add(
463 js
->gsc
? js
->gsc
->fd
: js
->fd
,
464 PURPLE_INPUT_WRITE
, jabber_send_cb
, js
);
465 purple_circular_buffer_append(js
->write_buffer
,
466 data
+ ret
, len
- ret
);
472 void jabber_send_raw(JabberStream
*js
, const char *data
, int len
)
474 PurpleConnection
*gc
;
475 PurpleAccount
*account
;
478 account
= purple_connection_get_account(gc
);
480 g_return_if_fail(data
!= NULL
);
482 /* because printing a tab to debug every minute gets old */
483 if (data
&& strcmp(data
, "\t") != 0) {
484 const char *username
;
485 char *text
= NULL
, *last_part
= NULL
, *tag_start
= NULL
;
487 /* Because debug logs with plaintext passwords make me sad */
488 if (!purple_debug_is_unsafe() && js
->state
!= JABBER_STREAM_CONNECTED
&&
489 /* Either <auth> or <query><password>... */
490 (((tag_start
= strstr(data
, "<auth ")) &&
491 strstr(data
, "xmlns='" NS_XMPP_SASL
"'")) ||
492 ((tag_start
= strstr(data
, "<query ")) &&
493 strstr(data
, "xmlns='jabber:iq:auth'>") &&
494 (tag_start
= strstr(tag_start
, "<password>"))))) {
495 char *data_start
, *tag_end
= strchr(tag_start
, '>');
496 text
= g_strdup(data
);
498 /* Better to print out some wacky debugging than crash
499 * due to a plugin sending bad xml */
503 data_start
= text
+ (tag_end
- data
) + 1;
505 last_part
= strchr(data_start
, '<');
509 username
= purple_connection_get_display_name(gc
);
511 username
= purple_account_get_username(account
);
513 purple_debug_misc("jabber", "Sending%s (%s): %s%s%s\n",
514 jabber_stream_is_ssl(js
) ? " (ssl)" : "", username
,
516 last_part
? "password removed" : "",
517 last_part
? last_part
: "");
522 purple_signal_emit(purple_connection_get_protocol(gc
), "jabber-sending-text", gc
, &data
);
529 /* If we've got a security layer, we need to encode the data,
530 * splitting it on the maximum buffer length negotiated */
531 #ifdef HAVE_CYRUS_SASL
532 if (js
->sasl_maxbuf
>0) {
535 if (!js
->gsc
&& js
->fd
<0)
536 g_return_if_reached();
544 towrite
= MIN((len
- pos
), js
->sasl_maxbuf
);
546 rc
= sasl_encode(js
->sasl
, &data
[pos
], towrite
,
550 g_strdup_printf(_("SASL error: %s"),
551 sasl_errdetail(js
->sasl
));
552 purple_debug_error("jabber",
553 "sasl_encode error %d: %s\n", rc
,
554 sasl_errdetail(js
->sasl
));
555 purple_connection_error(gc
,
556 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
563 /* do_jabber_send_raw returns FALSE when it throws a
566 if (!do_jabber_send_raw(js
, out
, olen
))
574 jabber_bosh_connection_send(js
->bosh
, data
);
576 do_jabber_send_raw(js
, data
, len
);
579 int jabber_protocol_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
581 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
583 g_return_val_if_fail(js
!= NULL
, -1);
584 /* TODO: It's probably worthwhile to restrict this to when the account
585 * state is CONNECTED, but I can /almost/ envision reasons for wanting
586 * to do things during the connection process.
589 jabber_send_raw(js
, buf
, len
);
590 return (len
< 0 ? (int)strlen(buf
) : len
);
593 void jabber_send_signal_cb(PurpleConnection
*pc
, PurpleXmlNode
**packet
,
603 PURPLE_ASSERT_CONNECTION_IS_VALID(pc
);
605 js
= purple_connection_get_protocol_data(pc
);
611 if (g_str_equal((*packet
)->name
, "message") ||
612 g_str_equal((*packet
)->name
, "iq") ||
613 g_str_equal((*packet
)->name
, "presence"))
614 purple_xmlnode_set_namespace(*packet
, NS_XMPP_CLIENT
);
615 txt
= purple_xmlnode_to_str(*packet
, &len
);
616 jabber_send_raw(js
, txt
, len
);
620 void jabber_send(JabberStream
*js
, PurpleXmlNode
*packet
)
622 purple_signal_emit(purple_connection_get_protocol(js
->gc
), "jabber-sending-xmlnode", js
->gc
, &packet
);
625 static gboolean
jabber_keepalive_timeout(PurpleConnection
*gc
)
627 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
628 purple_connection_error(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
629 _("Ping timed out"));
630 js
->keepalive_timeout
= 0;
634 void jabber_keepalive(PurpleConnection
*gc
)
636 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
637 time_t now
= time(NULL
);
639 if (js
->keepalive_timeout
== 0 && (now
- js
->last_ping
) >= PING_TIMEOUT
) {
642 jabber_keepalive_ping(js
);
643 js
->keepalive_timeout
= purple_timeout_add_seconds(120,
644 (GSourceFunc
)(jabber_keepalive_timeout
), gc
);
649 jabber_recv_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
650 PurpleInputCondition cond
)
652 PurpleConnection
*gc
= data
;
653 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
655 static char buf
[4096];
657 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
659 while((len
= purple_ssl_read(gsc
, buf
, sizeof(buf
) - 1)) > 0) {
660 purple_connection_update_last_received(gc
);
662 purple_debug_misc("jabber", "Recv (ssl)(%d): %s", len
, buf
);
663 jabber_parser_process(js
, buf
, len
);
665 jabber_stream_init(js
);
668 if(len
< 0 && errno
== EAGAIN
)
673 tmp
= g_strdup(_("Server closed the connection"));
675 tmp
= g_strdup_printf(_("Lost connection with server: %s"),
677 purple_connection_error(js
->gc
,
678 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
684 jabber_recv_cb(gpointer data
, gint source
, PurpleInputCondition condition
)
686 PurpleConnection
*gc
= data
;
687 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
689 static char buf
[4096];
691 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
693 if((len
= read(js
->fd
, buf
, sizeof(buf
) - 1)) > 0) {
694 purple_connection_update_last_received(gc
);
695 #ifdef HAVE_CYRUS_SASL
696 if (js
->sasl_maxbuf
> 0) {
701 rc
= sasl_decode(js
->sasl
, buf
, len
, &out
, &olen
);
704 g_strdup_printf(_("SASL error: %s"),
705 sasl_errdetail(js
->sasl
));
706 purple_debug_error("jabber",
707 "sasl_decode_error %d: %s\n", rc
,
708 sasl_errdetail(js
->sasl
));
709 purple_connection_error(gc
,
710 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
712 } else if (olen
> 0) {
713 purple_debug_info("jabber", "RecvSASL (%u): %s\n", olen
, out
);
714 jabber_parser_process(js
, out
, olen
);
716 jabber_stream_init(js
);
722 purple_debug_misc("jabber", "Recv (%d): %s", len
, buf
);
723 jabber_parser_process(js
, buf
, len
);
725 jabber_stream_init(js
);
726 } else if(len
< 0 && errno
== EAGAIN
) {
731 tmp
= g_strdup(_("Server closed the connection"));
733 tmp
= g_strdup_printf(_("Lost connection with server: %s"),
735 purple_connection_error(js
->gc
,
736 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
742 jabber_login_callback_ssl(gpointer data
, PurpleSslConnection
*gsc
,
743 PurpleInputCondition cond
)
745 PurpleConnection
*gc
= data
;
748 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
750 js
= purple_connection_get_protocol_data(gc
);
752 if(js
->state
== JABBER_STREAM_CONNECTING
)
753 jabber_send_raw(js
, "<?xml version='1.0' ?>", -1);
754 jabber_stream_set_state(js
, JABBER_STREAM_INITIALIZING
);
755 purple_ssl_input_add(gsc
, jabber_recv_cb_ssl
, gc
);
757 /* Tell the app that we're doing encryption */
758 jabber_stream_set_state(js
, JABBER_STREAM_INITIALIZING_ENCRYPTION
);
762 txt_resolved_cb(GObject
*sender
, GAsyncResult
*result
, gpointer data
)
764 GError
*error
= NULL
;
765 GList
*records
= NULL
, *l
= NULL
;
766 JabberStream
*js
= data
;
767 gboolean found
= FALSE
;
769 records
= g_resolver_lookup_records_finish(G_RESOLVER(sender
),
772 purple_debug_warning("jabber", "Unable to find alternative XMPP connection "
773 "methods after failing to connect directly. : %s\n",
776 purple_connection_error(js
->gc
,
777 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
778 _("Unable to connect"));
785 for(l
= records
; l
; l
= l
->next
) {
786 GVariantIter
*iter
= NULL
;
789 g_variant_get((GVariant
*)l
->data
, "(as)", &iter
);
790 while(g_variant_iter_loop(iter
, "s", &str
)) {
791 gchar
**token
= g_strsplit(str
, "=", 2);
793 if(!g_ascii_strcasecmp(token
[0], "_xmpp-client-xbosh")) {
794 purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token
[0], token
[1]);
796 js
->bosh
= jabber_bosh_connection_new(js
, token
[1]);
806 g_variant_iter_free(iter
);
809 g_list_free_full(records
, (GDestroyNotify
)g_variant_unref
);
815 purple_debug_warning("jabber", "Unable to find alternative XMPP connection "
816 "methods after failing to connect directly.\n");
817 purple_connection_error(js
->gc
,
818 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
819 _("Unable to connect"));
825 jabber_login_callback(gpointer data
, gint source
, const gchar
*error
)
827 PurpleConnection
*gc
= data
;
828 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
831 GResolver
*resolver
= g_resolver_get_default();
832 gchar
*name
= g_strdup_printf("_xmppconnect.%s", js
->user
->domain
);
834 purple_debug_info("jabber", "Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js
->user
->domain
);
836 g_resolver_lookup_records_async(resolver
,
838 G_RESOLVER_RECORD_TXT
,
843 g_object_unref(resolver
);
850 if(js
->state
== JABBER_STREAM_CONNECTING
)
851 jabber_send_raw(js
, "<?xml version='1.0' ?>", -1);
853 jabber_stream_set_state(js
, JABBER_STREAM_INITIALIZING
);
854 js
->inpa
= purple_input_add(js
->fd
, PURPLE_INPUT_READ
, jabber_recv_cb
, gc
);
858 jabber_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
861 PurpleConnection
*gc
= data
;
864 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
866 js
= purple_connection_get_protocol_data(gc
);
869 purple_connection_ssl_error (gc
, error
);
872 static void tls_init(JabberStream
*js
)
874 purple_input_remove(js
->inpa
);
876 js
->gsc
= purple_ssl_connect_with_host_fd(purple_connection_get_account(js
->gc
), js
->fd
,
877 jabber_login_callback_ssl
, jabber_ssl_connect_failure
, js
->certificate_CN
, js
->gc
);
878 /* The fd is no longer our concern */
882 static gboolean
jabber_login_connect(JabberStream
*js
, const char *domain
, const char *host
, int port
,
883 gboolean fatal_failure
)
885 /* host should be used in preference to domain to
886 * allow SASL authentication to work with FQDN of the server,
887 * but we use domain as fallback for when users enter IP address
888 * in connect server */
889 g_free(js
->serverFQDN
);
890 if (purple_ip_address_is_valid(host
))
891 js
->serverFQDN
= g_strdup(domain
);
893 js
->serverFQDN
= g_strdup(host
);
895 if (purple_proxy_connect(js
->gc
, purple_connection_get_account(js
->gc
),
896 host
, port
, jabber_login_callback
, js
->gc
) == NULL
) {
898 purple_connection_error(js
->gc
,
899 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
900 _("Unable to connect"));
910 srv_resolved_cb(GObject
*sender
, GAsyncResult
*result
, gpointer data
)
912 GError
*error
= NULL
;
913 GList
*targets
= NULL
, *l
= NULL
;
914 JabberStream
*js
= data
;
916 targets
= g_resolver_lookup_service_finish(G_RESOLVER(sender
),
919 purple_debug_warning("jabber",
920 "SRV lookup failed, proceeding with normal connection : %s",
925 jabber_login_connect(js
, js
->user
->domain
, js
->user
->domain
,
926 purple_account_get_int(purple_connection_get_account(js
->gc
), "port", 5222),
930 for(l
= targets
; l
; l
= l
->next
) {
931 GSrvTarget
*target
= (GSrvTarget
*)l
->data
;
932 const gchar
*hostname
= g_srv_target_get_hostname(target
);
933 guint port
= g_srv_target_get_port(target
);
935 if(jabber_login_connect(js
, hostname
, hostname
, port
, FALSE
)) {
936 g_resolver_free_targets(targets
);
942 g_resolver_free_targets(targets
);
944 jabber_login_connect(js
, js
->user
->domain
, js
->user
->domain
,
945 purple_account_get_int(purple_connection_get_account(js
->gc
), "port", 5222),
950 static JabberStream
*
951 jabber_stream_new(PurpleAccount
*account
)
953 PurpleConnection
*gc
= purple_account_get_connection(account
);
955 PurplePresence
*presence
;
959 js
= g_new0(JabberStream
, 1);
960 purple_connection_set_protocol_data(gc
, js
);
963 js
->http_conns
= purple_http_connection_set_new();
965 /* we might want to expose this at some point */
966 js
->cancellable
= g_cancellable_new();
968 user
= g_strdup(purple_account_get_username(account
));
969 /* jabber_id_new doesn't accept "user@domain/" as valid */
970 slash
= strchr(user
, '/');
971 if (slash
&& *(slash
+ 1) == '\0')
973 js
->user
= jabber_id_new(user
);
976 purple_connection_error(gc
,
977 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
978 _("Invalid XMPP ID"));
980 /* Destroying the connection will free the JabberStream */
984 if (!js
->user
->node
|| *(js
->user
->node
) == '\0') {
985 purple_connection_error(gc
,
986 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
987 _("Invalid XMPP ID. Username portion must be set."));
989 /* Destroying the connection will free the JabberStream */
993 if (!js
->user
->domain
|| *(js
->user
->domain
) == '\0') {
994 purple_connection_error(gc
,
995 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
996 _("Invalid XMPP ID. Domain must be set."));
998 /* Destroying the connection will free the JabberStream */
1002 js
->buddies
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1003 g_free
, (GDestroyNotify
)jabber_buddy_free
);
1005 /* This is overridden during binding, but we need it here
1006 * in case the server only does legacy non-sasl auth!.
1008 purple_connection_set_display_name(gc
, user
);
1010 js
->user_jb
= jabber_buddy_find(js
, user
, TRUE
);
1013 /* This basically *can't* fail, but for good measure... */
1014 purple_connection_error(gc
,
1015 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
1016 _("Invalid XMPP ID"));
1017 /* Destroying the connection will free the JabberStream */
1018 g_return_val_if_reached(NULL
);
1021 js
->user_jb
->subscription
|= JABBER_SUB_BOTH
;
1023 js
->iq_callbacks
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1024 g_free
, (GDestroyNotify
)jabber_iq_callbackdata_free
);
1025 js
->chats
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1026 g_free
, (GDestroyNotify
)jabber_chat_free
);
1027 js
->next_id
= g_random_int();
1028 js
->write_buffer
= purple_circular_buffer_new(512);
1030 js
->keepalive_timeout
= 0;
1031 js
->max_inactivity
= DEFAULT_INACTIVITY_TIME
;
1032 /* Set the default protocol version to 1.0. Overridden in parser.c. */
1033 js
->protocol_version
.major
= 1;
1034 js
->protocol_version
.minor
= 0;
1035 js
->sessions
= NULL
;
1038 js
->google_relay_token
= NULL
;
1039 js
->google_relay_host
= NULL
;
1041 /* if we are idle, set idle-ness on the stream (this could happen if we get
1042 disconnected and the reconnects while being idle. I don't think it makes
1043 sense to do this when registering a new account... */
1044 presence
= purple_account_get_presence(account
);
1045 if (purple_presence_is_idle(presence
))
1046 js
->idle
= purple_presence_get_idle_time(presence
);
1052 jabber_stream_connect(JabberStream
*js
)
1054 PurpleConnection
*gc
= js
->gc
;
1055 PurpleAccount
*account
= purple_connection_get_account(gc
);
1056 const char *connect_server
= purple_account_get_string(account
,
1057 "connect_server", "");
1058 const char *bosh_url
= purple_account_get_string(account
,
1061 jabber_stream_set_state(js
, JABBER_STREAM_CONNECTING
);
1063 /* If both BOSH and a Connect Server are specified, we prefer BOSH. I'm not
1064 * attached to that choice, though.
1067 js
->bosh
= jabber_bosh_connection_new(js
, bosh_url
);
1069 purple_connection_error(gc
,
1070 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
1071 _("Malformed BOSH URL"));
1077 js
->certificate_CN
= g_strdup(connect_server
[0] ? connect_server
: js
->user
->domain
);
1079 /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */
1080 if (g_str_equal("old_ssl", purple_account_get_string(account
, "connection_security", JABBER_DEFAULT_REQUIRE_TLS
))) {
1081 js
->gsc
= purple_ssl_connect(account
, js
->certificate_CN
,
1082 purple_account_get_int(account
, "port", 5223),
1083 jabber_login_callback_ssl
, jabber_ssl_connect_failure
, gc
);
1085 purple_connection_error(gc
,
1086 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
1087 _("Unable to establish SSL connection"));
1093 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll
1094 * invoke the magic of SRV lookups, to figure out host and port */
1095 if(connect_server
[0]) {
1096 jabber_login_connect(js
, js
->user
->domain
, connect_server
,
1097 purple_account_get_int(account
, "port", 5222), TRUE
);
1099 GResolver
*resolver
= g_resolver_get_default();
1100 g_resolver_lookup_service_async(resolver
,
1107 g_object_unref(resolver
);
1112 jabber_login(PurpleAccount
*account
)
1114 PurpleConnection
*gc
= purple_account_get_connection(account
);
1118 purple_connection_set_flags(gc
, PURPLE_CONNECTION_FLAG_HTML
|
1119 PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY
|
1120 PURPLE_CONNECTION_FLAG_NO_IMAGES
);
1121 js
= jabber_stream_new(account
);
1125 /* replace old default proxies with the new default: NULL
1126 * TODO: these can eventually be removed */
1127 if (g_str_equal("proxy.jabber.org", purple_account_get_string(account
, "ft_proxies", ""))
1128 || g_str_equal("proxy.eu.jabber.org", purple_account_get_string(account
, "ft_proxies", "")))
1129 purple_account_set_string(account
, "ft_proxies", NULL
);
1132 * Calculate the avatar hash for our current image so we know (when we
1133 * fetch our vCard and PEP avatar) if we should send our avatar to the
1136 image
= purple_buddy_icons_find_account_icon(account
);
1137 if (image
!= NULL
) {
1138 js
->initial_avatar_hash
= jabber_calculate_data_hash(
1139 purple_image_get_data(image
),
1140 purple_image_get_size(image
), "sha1");
1141 g_object_unref(image
);
1144 jabber_stream_connect(js
);
1149 conn_close_cb(gpointer data
)
1151 JabberStream
*js
= data
;
1152 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1154 jabber_parser_free(js
);
1156 purple_account_disconnect(account
);
1158 js
->conn_close_timeout
= 0;
1164 jabber_connection_schedule_close(JabberStream
*js
)
1166 js
->conn_close_timeout
= purple_timeout_add(0, conn_close_cb
, js
);
1170 jabber_registration_result_cb(JabberStream
*js
, const char *from
,
1171 JabberIqType type
, const char *id
,
1172 PurpleXmlNode
*packet
, gpointer data
)
1174 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1178 if (type
== JABBER_IQ_RESULT
) {
1179 if(js
->registration
) {
1180 buf
= g_strdup_printf(_("Registration of %s@%s successful"),
1181 js
->user
->node
, js
->user
->domain
);
1182 purple_account_register_completed(account
, TRUE
);
1184 g_return_if_fail(to
!= NULL
);
1185 buf
= g_strdup_printf(_("Registration to %s successful"),
1188 purple_notify_info(NULL
, _("Registration Successful"),
1189 _("Registration Successful"), buf
,
1190 purple_request_cpar_from_connection(js
->gc
));
1193 char *msg
= jabber_parse_error(js
, packet
, NULL
);
1196 msg
= g_strdup(_("Unknown Error"));
1198 purple_notify_error(NULL
, _("Registration Failed"),
1199 _("Registration Failed"), msg
,
1200 purple_request_cpar_from_connection(js
->gc
));
1202 purple_account_register_completed(account
, FALSE
);
1205 if(js
->registration
)
1206 jabber_connection_schedule_close(js
);
1210 jabber_unregistration_result_cb(JabberStream
*js
, const char *from
,
1211 JabberIqType type
, const char *id
,
1212 PurpleXmlNode
*packet
, gpointer data
)
1217 /* This function is never called for unregistering our XMPP account from
1218 * the server, so there should always be a 'to' address. */
1219 g_return_if_fail(to
!= NULL
);
1221 if (type
== JABBER_IQ_RESULT
) {
1222 buf
= g_strdup_printf(_("Registration from %s successfully removed"),
1224 purple_notify_info(NULL
, _("Unregistration Successful"),
1225 _("Unregistration Successful"), buf
,
1226 purple_request_cpar_from_connection(js
->gc
));
1229 char *msg
= jabber_parse_error(js
, packet
, NULL
);
1232 msg
= g_strdup(_("Unknown Error"));
1234 purple_notify_error(NULL
, _("Unregistration Failed"),
1235 _("Unregistration Failed"), msg
,
1236 purple_request_cpar_from_connection(js
->gc
));
1242 typedef struct _JabberRegisterCBData
{
1245 } JabberRegisterCBData
;
1248 jabber_register_cb(JabberRegisterCBData
*cbdata
, PurpleRequestFields
*fields
)
1250 GList
*groups
, *flds
;
1251 PurpleXmlNode
*query
, *y
;
1255 iq
= jabber_iq_new_query(cbdata
->js
, JABBER_IQ_SET
, "jabber:iq:register");
1256 query
= purple_xmlnode_get_child(iq
->node
, "query");
1258 purple_xmlnode_set_attrib(iq
->node
, "to", cbdata
->who
);
1260 for(groups
= purple_request_fields_get_groups(fields
); groups
;
1261 groups
= groups
->next
) {
1262 for(flds
= purple_request_field_group_get_fields(groups
->data
);
1263 flds
; flds
= flds
->next
) {
1264 PurpleRequestField
*field
= flds
->data
;
1265 const char *id
= purple_request_field_get_id(field
);
1266 if(!strcmp(id
,"unregister")) {
1267 gboolean value
= purple_request_field_bool_get_value(field
);
1269 /* unregister from service. this doesn't include any of the fields, so remove them from the stanza by recreating it
1270 (there's no "remove child" function for PurpleXmlNode) */
1272 iq
= jabber_iq_new_query(cbdata
->js
, JABBER_IQ_SET
, "jabber:iq:register");
1273 query
= purple_xmlnode_get_child(iq
->node
, "query");
1275 purple_xmlnode_set_attrib(iq
->node
,"to",cbdata
->who
);
1276 purple_xmlnode_new_child(query
, "remove");
1278 jabber_iq_set_callback(iq
, jabber_unregistration_result_cb
, cbdata
->who
);
1285 const char *ids
[] = {"username", "password", "name", "email", "nick", "first",
1286 "last", "address", "city", "state", "zip", "phone", "url", "date",
1288 const char *value
= purple_request_field_string_get_value(field
);
1290 for (i
= 0; ids
[i
]; i
++) {
1291 if (!strcmp(id
, ids
[i
]))
1297 y
= purple_xmlnode_new_child(query
, ids
[i
]);
1298 purple_xmlnode_insert_data(y
, value
, -1);
1299 if(cbdata
->js
->registration
&& !strcmp(id
, "username")) {
1300 g_free(cbdata
->js
->user
->node
);
1301 cbdata
->js
->user
->node
= g_strdup(value
);
1303 if(cbdata
->js
->registration
&& !strcmp(id
, "password"))
1304 purple_account_set_password(purple_connection_get_account(cbdata
->js
->gc
), value
, NULL
, NULL
);
1309 if(cbdata
->js
->registration
) {
1310 username
= g_strdup_printf("%s@%s%s%s", cbdata
->js
->user
->node
, cbdata
->js
->user
->domain
,
1311 cbdata
->js
->user
->resource
? "/" : "",
1312 cbdata
->js
->user
->resource
? cbdata
->js
->user
->resource
: "");
1313 purple_account_set_username(purple_connection_get_account(cbdata
->js
->gc
), username
);
1317 jabber_iq_set_callback(iq
, jabber_registration_result_cb
, cbdata
->who
);
1324 jabber_register_cancel_cb(JabberRegisterCBData
*cbdata
, PurpleRequestFields
*fields
)
1326 PurpleAccount
*account
= purple_connection_get_account(cbdata
->js
->gc
);
1327 if(account
&& cbdata
->js
->registration
) {
1328 purple_account_register_completed(account
, FALSE
);
1329 jabber_connection_schedule_close(cbdata
->js
);
1331 g_free(cbdata
->who
);
1335 static void jabber_register_x_data_cb(JabberStream
*js
, PurpleXmlNode
*result
, gpointer data
)
1337 PurpleXmlNode
*query
;
1341 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
1342 query
= purple_xmlnode_get_child(iq
->node
, "query");
1344 purple_xmlnode_set_attrib(iq
->node
,"to",to
);
1346 purple_xmlnode_insert_child(query
, result
);
1348 jabber_iq_set_callback(iq
, jabber_registration_result_cb
, to
);
1352 static const struct {
1355 } registration_fields
[] = {
1356 { "email", N_("Email") },
1357 { "nick", N_("Nickname") },
1358 { "first", N_("First name") },
1359 { "last", N_("Last name") },
1360 { "address", N_("Address") },
1361 { "city", N_("City") },
1362 { "state", N_("State") },
1363 { "zip", N_("Postal code") },
1364 { "phone", N_("Phone") },
1365 { "url", N_("URL") },
1366 { "date", N_("Date") },
1370 void jabber_register_parse(JabberStream
*js
, const char *from
, JabberIqType type
,
1371 const char *id
, PurpleXmlNode
*query
)
1373 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1374 PurpleRequestFields
*fields
;
1375 PurpleRequestFieldGroup
*group
;
1376 PurpleRequestField
*field
;
1377 PurpleXmlNode
*x
, *y
, *node
;
1379 JabberRegisterCBData
*cbdata
;
1380 gboolean registered
= FALSE
;
1383 if (type
!= JABBER_IQ_RESULT
)
1386 if(js
->registration
) {
1387 /* get rid of the login thingy */
1388 purple_connection_set_state(js
->gc
, PURPLE_CONNECTION_CONNECTED
);
1391 if(purple_xmlnode_get_child(query
, "registered")) {
1394 if(js
->registration
) {
1395 purple_notify_error(NULL
, _("Already Registered"),
1396 _("Already Registered"), NULL
,
1397 purple_request_cpar_from_connection(js
->gc
));
1398 purple_account_register_completed(account
, FALSE
);
1399 jabber_connection_schedule_close(js
);
1404 if((x
= purple_xmlnode_get_child_with_namespace(query
, "x", "jabber:x:data"))) {
1405 jabber_x_data_request(js
, x
, jabber_register_x_data_cb
, g_strdup(from
));
1408 } else if((x
= purple_xmlnode_get_child_with_namespace(query
, "x", NS_OOB_X_DATA
))) {
1411 if((url
= purple_xmlnode_get_child(x
, "url"))) {
1413 if((href
= purple_xmlnode_get_data(url
))) {
1414 purple_notify_uri(NULL
, href
);
1417 if(js
->registration
) {
1418 /* succeeded, but we have no login info */
1419 purple_account_register_completed(account
, TRUE
);
1420 purple_connection_error(js
->gc
, PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
1421 _("Registration completed successfully. Please reconnect to continue."));
1422 jabber_connection_schedule_close(js
);
1429 /* as a last resort, use the old jabber:iq:register syntax */
1431 fields
= purple_request_fields_new();
1432 group
= purple_request_field_group_new(NULL
);
1433 purple_request_fields_add_group(fields
, group
);
1435 if((node
= purple_xmlnode_get_child(query
, "username"))) {
1436 char *data
= purple_xmlnode_get_data(node
);
1437 if(js
->registration
)
1438 field
= purple_request_field_string_new("username", _("Username"), data
? data
: js
->user
->node
, FALSE
);
1440 field
= purple_request_field_string_new("username", _("Username"), data
, FALSE
);
1442 purple_request_field_group_add_field(group
, field
);
1445 if((node
= purple_xmlnode_get_child(query
, "password"))) {
1446 if(js
->registration
)
1447 field
= purple_request_field_string_new("password", _("Password"),
1448 purple_connection_get_password(js
->gc
), FALSE
);
1450 char *data
= purple_xmlnode_get_data(node
);
1451 field
= purple_request_field_string_new("password", _("Password"), data
, FALSE
);
1455 purple_request_field_string_set_masked(field
, TRUE
);
1456 purple_request_field_group_add_field(group
, field
);
1459 if((node
= purple_xmlnode_get_child(query
, "name"))) {
1460 if(js
->registration
)
1461 field
= purple_request_field_string_new("name", _("Name"),
1462 purple_account_get_private_alias(purple_connection_get_account(js
->gc
)), FALSE
);
1464 char *data
= purple_xmlnode_get_data(node
);
1465 field
= purple_request_field_string_new("name", _("Name"), data
, FALSE
);
1468 purple_request_field_group_add_field(group
, field
);
1471 for (i
= 0; registration_fields
[i
].name
!= NULL
; ++i
) {
1472 if ((node
= purple_xmlnode_get_child(query
, registration_fields
[i
].name
))) {
1473 char *data
= purple_xmlnode_get_data(node
);
1474 field
= purple_request_field_string_new(registration_fields
[i
].name
,
1475 _(registration_fields
[i
].label
),
1477 purple_request_field_group_add_field(group
, field
);
1483 field
= purple_request_field_bool_new("unregister", _("Unregister"), FALSE
);
1484 purple_request_field_group_add_field(group
, field
);
1487 if((y
= purple_xmlnode_get_child(query
, "instructions")))
1488 instructions
= purple_xmlnode_get_data(y
);
1490 instructions
= g_strdup(_("Please fill out the information below "
1491 "to change your account registration."));
1493 instructions
= g_strdup(_("Please fill out the information below "
1494 "to register your new account."));
1496 cbdata
= g_new0(JabberRegisterCBData
, 1);
1498 cbdata
->who
= g_strdup(from
);
1500 if(js
->registration
)
1501 purple_request_fields(js
->gc
, _("Register New XMPP Account"),
1502 _("Register New XMPP Account"), instructions
, fields
,
1503 _("Register"), G_CALLBACK(jabber_register_cb
),
1504 _("Cancel"), G_CALLBACK(jabber_register_cancel_cb
),
1505 purple_request_cpar_from_connection(js
->gc
),
1509 g_return_if_fail(from
!= NULL
);
1510 title
= registered
? g_strdup_printf(_("Change Account Registration at %s"), from
)
1511 :g_strdup_printf(_("Register New Account at %s"), from
);
1512 purple_request_fields(js
->gc
, title
, title
, instructions
,
1513 fields
, (registered
? _("Change Registration") :
1514 _("Register")), G_CALLBACK(jabber_register_cb
),
1515 _("Cancel"), G_CALLBACK(jabber_register_cancel_cb
),
1516 purple_request_cpar_from_connection(js
->gc
), cbdata
);
1520 g_free(instructions
);
1523 void jabber_register_start(JabberStream
*js
)
1527 iq
= jabber_iq_new_query(js
, JABBER_IQ_GET
, "jabber:iq:register");
1531 void jabber_register_gateway(JabberStream
*js
, const char *gateway
) {
1534 iq
= jabber_iq_new_query(js
, JABBER_IQ_GET
, "jabber:iq:register");
1535 purple_xmlnode_set_attrib(iq
->node
, "to", gateway
);
1539 void jabber_register_account(PurpleAccount
*account
)
1543 js
= jabber_stream_new(account
);
1547 js
->registration
= TRUE
;
1548 jabber_stream_connect(js
);
1552 jabber_unregister_account_iq_cb(JabberStream
*js
, const char *from
,
1553 JabberIqType type
, const char *id
,
1554 PurpleXmlNode
*packet
, gpointer data
)
1556 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1558 if (type
== JABBER_IQ_ERROR
) {
1559 char *msg
= jabber_parse_error(js
, packet
, NULL
);
1561 purple_notify_error(js
->gc
, _("Error unregistering account"),
1562 _("Error unregistering account"), msg
,
1563 purple_request_cpar_from_connection(js
->gc
));
1565 if(js
->unregistration_cb
)
1566 js
->unregistration_cb(account
, FALSE
, js
->unregistration_user_data
);
1568 purple_notify_info(js
->gc
, _("Account successfully "
1569 "unregistered"), _("Account successfully unregistered"),
1570 NULL
, purple_request_cpar_from_connection(js
->gc
));
1571 if(js
->unregistration_cb
)
1572 js
->unregistration_cb(account
, TRUE
, js
->unregistration_user_data
);
1576 static void jabber_unregister_account_cb(JabberStream
*js
) {
1578 PurpleXmlNode
*query
;
1580 g_return_if_fail(js
->unregistration
);
1582 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
1584 query
= purple_xmlnode_get_child_with_namespace(iq
->node
, "query", "jabber:iq:register");
1586 purple_xmlnode_new_child(query
, "remove");
1587 purple_xmlnode_set_attrib(iq
->node
, "to", js
->user
->domain
);
1589 jabber_iq_set_callback(iq
, jabber_unregister_account_iq_cb
, NULL
);
1593 void jabber_unregister_account(PurpleAccount
*account
, PurpleAccountUnregistrationCb cb
, void *user_data
) {
1594 PurpleConnection
*gc
= purple_account_get_connection(account
);
1597 if (purple_connection_get_state(gc
) != PURPLE_CONNECTION_CONNECTED
) {
1598 if (purple_connection_get_state(gc
) != PURPLE_CONNECTION_CONNECTING
)
1599 jabber_login(account
);
1600 js
= purple_connection_get_protocol_data(gc
);
1601 js
->unregistration
= TRUE
;
1602 js
->unregistration_cb
= cb
;
1603 js
->unregistration_user_data
= user_data
;
1607 js
= purple_connection_get_protocol_data(gc
);
1609 if (js
->unregistration
) {
1610 purple_debug_error("jabber", "Unregistration in process; ignoring duplicate request.\n");
1614 js
->unregistration
= TRUE
;
1615 js
->unregistration_cb
= cb
;
1616 js
->unregistration_user_data
= user_data
;
1618 jabber_unregister_account_cb(js
);
1621 /* TODO: As Will pointed out in IRC, after being notified by the core to
1622 * shutdown, we should async. wait for the server to send us the stream
1623 * termination before destorying everything. That seems like it would require
1624 * changing the semantics of protocol's close(), so it's a good idea for 3.0.0.
1626 void jabber_close(PurpleConnection
*gc
)
1628 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
1630 /* Close all of the open Jingle sessions on this stream */
1631 jingle_terminate_sessions(js
);
1634 jabber_bosh_connection_destroy(js
->bosh
);
1636 } else if ((js
->gsc
&& js
->gsc
->fd
> 0) || js
->fd
> 0)
1637 jabber_send_raw(js
, "</stream:stream>", -1);
1640 purple_ssl_close(js
->gsc
);
1641 } else if (js
->fd
> 0) {
1643 purple_input_remove(js
->inpa
);
1649 jabber_buddy_remove_all_pending_buddy_info_requests(js
);
1651 jabber_parser_free(js
);
1653 if(js
->iq_callbacks
)
1654 g_hash_table_destroy(js
->iq_callbacks
);
1656 g_hash_table_destroy(js
->buddies
);
1658 g_hash_table_destroy(js
->chats
);
1660 while(js
->chat_servers
) {
1661 g_free(js
->chat_servers
->data
);
1662 js
->chat_servers
= g_list_delete_link(js
->chat_servers
, js
->chat_servers
);
1665 while(js
->user_directories
) {
1666 g_free(js
->user_directories
->data
);
1667 js
->user_directories
= g_list_delete_link(js
->user_directories
, js
->user_directories
);
1670 while(js
->bs_proxies
) {
1671 JabberBytestreamsStreamhost
*sh
= js
->bs_proxies
->data
;
1674 g_free(sh
->zeroconf
);
1676 js
->bs_proxies
= g_list_delete_link(js
->bs_proxies
, js
->bs_proxies
);
1679 purple_http_connection_set_destroy(js
->http_conns
);
1681 g_free(js
->stream_id
);
1683 jabber_id_free(js
->user
);
1684 g_free(js
->initial_avatar_hash
);
1685 g_free(js
->avatar_hash
);
1686 g_free(js
->caps_hash
);
1688 if (js
->write_buffer
)
1689 g_object_unref(G_OBJECT(js
->write_buffer
));
1691 purple_input_remove(js
->writeh
);
1692 if (js
->auth_mech
&& js
->auth_mech
->dispose
)
1693 js
->auth_mech
->dispose(js
);
1694 #ifdef HAVE_CYRUS_SASL
1696 sasl_dispose(&js
->sasl
);
1698 g_string_free(js
->sasl_mechs
, TRUE
);
1699 g_free(js
->sasl_cb
);
1700 /* Note: _not_ g_free. See auth_cyrus.c:jabber_sasl_cb_secret */
1701 free(js
->sasl_secret
);
1702 g_free(js
->sasl_password
);
1704 g_free(js
->serverFQDN
);
1705 while(js
->commands
) {
1706 JabberAdHocCommands
*cmd
= js
->commands
->data
;
1711 js
->commands
= g_list_delete_link(js
->commands
, js
->commands
);
1713 g_free(js
->server_name
);
1714 g_free(js
->certificate_CN
);
1715 g_free(js
->gmail_last_time
);
1716 g_free(js
->gmail_last_tid
);
1717 g_free(js
->old_msg
);
1718 g_free(js
->old_avatarhash
);
1719 g_free(js
->old_artist
);
1720 g_free(js
->old_title
);
1721 g_free(js
->old_source
);
1722 g_free(js
->old_uri
);
1723 g_free(js
->old_track
);
1725 if (js
->vcard_timer
!= 0)
1726 purple_timeout_remove(js
->vcard_timer
);
1728 if (js
->keepalive_timeout
!= 0)
1729 purple_timeout_remove(js
->keepalive_timeout
);
1730 if (js
->inactivity_timer
!= 0)
1731 purple_timeout_remove(js
->inactivity_timer
);
1732 if (js
->conn_close_timeout
!= 0)
1733 purple_timeout_remove(js
->conn_close_timeout
);
1735 g_cancellable_cancel(js
->cancellable
);
1736 g_object_unref(G_OBJECT(js
->cancellable
));
1738 g_free(js
->stun_ip
);
1740 /* remove Google relay-related stuff */
1741 g_free(js
->google_relay_token
);
1742 g_free(js
->google_relay_host
);
1746 purple_connection_set_protocol_data(gc
, NULL
);
1749 void jabber_stream_set_state(JabberStream
*js
, JabberStreamState state
)
1751 #define JABBER_CONNECT_STEPS ((js->gsc || js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION) ? 9 : 5)
1755 case JABBER_STREAM_OFFLINE
:
1757 case JABBER_STREAM_CONNECTING
:
1758 purple_connection_update_progress(js
->gc
, _("Connecting"), 1,
1759 JABBER_CONNECT_STEPS
);
1761 case JABBER_STREAM_INITIALIZING
:
1762 purple_connection_update_progress(js
->gc
, _("Initializing Stream"),
1763 js
->gsc
? 5 : 2, JABBER_CONNECT_STEPS
);
1764 jabber_stream_init(js
);
1766 case JABBER_STREAM_INITIALIZING_ENCRYPTION
:
1767 purple_connection_update_progress(js
->gc
, _("Initializing SSL/TLS"),
1768 6, JABBER_CONNECT_STEPS
);
1770 case JABBER_STREAM_AUTHENTICATING
:
1771 purple_connection_update_progress(js
->gc
, _("Authenticating"),
1772 js
->gsc
? 7 : 3, JABBER_CONNECT_STEPS
);
1774 case JABBER_STREAM_POST_AUTH
:
1775 purple_connection_update_progress(js
->gc
, _("Re-initializing Stream"),
1776 (js
->gsc
? 8 : 4), JABBER_CONNECT_STEPS
);
1779 case JABBER_STREAM_CONNECTED
:
1780 /* Send initial presence */
1781 jabber_presence_send(js
, TRUE
);
1782 /* Start up the inactivity timer */
1783 jabber_stream_restart_inactivity_timer(js
);
1785 purple_connection_set_state(js
->gc
, PURPLE_CONNECTION_CONNECTED
);
1789 #undef JABBER_CONNECT_STEPS
1792 char *jabber_get_next_id(JabberStream
*js
)
1794 return g_strdup_printf("purple%x", js
->next_id
++);
1798 void jabber_idle_set(PurpleConnection
*gc
, int idle
)
1800 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
1802 js
->idle
= idle
? time(NULL
) - idle
: idle
;
1804 /* send out an updated prescence */
1805 purple_debug_info("jabber", "sending updated presence for idle\n");
1806 jabber_presence_send(js
, FALSE
);
1809 void jabber_blocklist_parse_push(JabberStream
*js
, const char *from
,
1810 JabberIqType type
, const char *id
,
1811 PurpleXmlNode
*child
)
1814 PurpleXmlNode
*item
;
1815 PurpleAccount
*account
;
1819 if (!jabber_is_own_account(js
, from
)) {
1820 PurpleXmlNode
*error
, *x
;
1821 result
= jabber_iq_new(js
, JABBER_IQ_ERROR
);
1822 purple_xmlnode_set_attrib(result
->node
, "id", id
);
1824 purple_xmlnode_set_attrib(result
->node
, "to", from
);
1826 error
= purple_xmlnode_new_child(result
->node
, "error");
1827 purple_xmlnode_set_attrib(error
, "type", "cancel");
1828 x
= purple_xmlnode_new_child(error
, "not-allowed");
1829 purple_xmlnode_set_namespace(x
, NS_XMPP_STANZAS
);
1831 jabber_iq_send(result
);
1835 account
= purple_connection_get_account(js
->gc
);
1836 is_block
= g_str_equal(child
->name
, "block");
1838 item
= purple_xmlnode_get_child(child
, "item");
1839 if (!is_block
&& item
== NULL
) {
1840 /* Unblock everyone */
1841 purple_debug_info("jabber", "Received unblock push. Unblocking everyone.\n");
1843 while ((deny
= purple_account_privacy_get_denied(account
)) != NULL
) {
1844 purple_account_privacy_deny_remove(account
, deny
->data
, TRUE
);
1846 } else if (item
== NULL
) {
1847 /* An empty <block/> is bogus */
1848 PurpleXmlNode
*error
, *x
;
1849 result
= jabber_iq_new(js
, JABBER_IQ_ERROR
);
1850 purple_xmlnode_set_attrib(result
->node
, "id", id
);
1852 error
= purple_xmlnode_new_child(result
->node
, "error");
1853 purple_xmlnode_set_attrib(error
, "type", "modify");
1854 x
= purple_xmlnode_new_child(error
, "bad-request");
1855 purple_xmlnode_set_namespace(x
, NS_XMPP_STANZAS
);
1857 jabber_iq_send(result
);
1860 for ( ; item
; item
= purple_xmlnode_get_next_twin(item
)) {
1861 const char *jid
= purple_xmlnode_get_attrib(item
, "jid");
1862 if (jid
== NULL
|| *jid
== '\0')
1866 purple_account_privacy_deny_add(account
, jid
, TRUE
);
1868 purple_account_privacy_deny_remove(account
, jid
, TRUE
);
1872 result
= jabber_iq_new(js
, JABBER_IQ_RESULT
);
1873 purple_xmlnode_set_attrib(result
->node
, "id", id
);
1874 jabber_iq_send(result
);
1877 static void jabber_blocklist_parse(JabberStream
*js
, const char *from
,
1878 JabberIqType type
, const char *id
,
1879 PurpleXmlNode
*packet
, gpointer data
)
1881 PurpleXmlNode
*blocklist
, *item
;
1882 PurpleAccount
*account
;
1885 blocklist
= purple_xmlnode_get_child_with_namespace(packet
,
1886 "blocklist", NS_SIMPLE_BLOCKING
);
1887 account
= purple_connection_get_account(js
->gc
);
1889 if (type
== JABBER_IQ_ERROR
|| blocklist
== NULL
)
1892 /* This is the only privacy method supported by XEP-0191 */
1893 purple_account_set_privacy_type(account
, PURPLE_ACCOUNT_PRIVACY_DENY_USERS
);
1896 * TODO: When account->deny is something more than a hash table, this can
1897 * be re-written to find the set intersection and difference.
1899 while ((deny
= purple_account_privacy_get_denied(account
)))
1900 purple_account_privacy_deny_remove(account
, deny
->data
, TRUE
);
1902 item
= purple_xmlnode_get_child(blocklist
, "item");
1903 while (item
!= NULL
) {
1904 const char *jid
= purple_xmlnode_get_attrib(item
, "jid");
1905 purple_account_privacy_deny_add(account
, jid
, TRUE
);
1906 item
= purple_xmlnode_get_next_twin(item
);
1910 void jabber_request_block_list(JabberStream
*js
)
1913 PurpleXmlNode
*blocklist
;
1915 iq
= jabber_iq_new(js
, JABBER_IQ_GET
);
1917 blocklist
= purple_xmlnode_new_child(iq
->node
, "blocklist");
1918 purple_xmlnode_set_namespace(blocklist
, NS_SIMPLE_BLOCKING
);
1920 jabber_iq_set_callback(iq
, jabber_blocklist_parse
, NULL
);
1925 void jabber_add_deny(PurpleConnection
*gc
, const char *who
)
1929 PurpleXmlNode
*block
, *item
;
1931 g_return_if_fail(who
!= NULL
&& *who
!= '\0');
1933 js
= purple_connection_get_protocol_data(gc
);
1937 if (js
->server_caps
& JABBER_CAP_GOOGLE_ROSTER
)
1939 jabber_google_roster_add_deny(js
, who
);
1943 if (!(js
->server_caps
& JABBER_CAP_BLOCKING
))
1945 purple_notify_error(NULL
, _("Server doesn't support blocking"),
1946 _("Server doesn't support blocking"), NULL
,
1947 purple_request_cpar_from_connection(gc
));
1951 iq
= jabber_iq_new(js
, JABBER_IQ_SET
);
1953 block
= purple_xmlnode_new_child(iq
->node
, "block");
1954 purple_xmlnode_set_namespace(block
, NS_SIMPLE_BLOCKING
);
1956 item
= purple_xmlnode_new_child(block
, "item");
1957 purple_xmlnode_set_attrib(item
, "jid", who
);
1962 void jabber_rem_deny(PurpleConnection
*gc
, const char *who
)
1966 PurpleXmlNode
*unblock
, *item
;
1968 g_return_if_fail(who
!= NULL
&& *who
!= '\0');
1970 js
= purple_connection_get_protocol_data(gc
);
1974 if (js
->server_caps
& JABBER_CAP_GOOGLE_ROSTER
)
1976 jabber_google_roster_rem_deny(js
, who
);
1980 if (!(js
->server_caps
& JABBER_CAP_BLOCKING
))
1983 iq
= jabber_iq_new(js
, JABBER_IQ_SET
);
1985 unblock
= purple_xmlnode_new_child(iq
->node
, "unblock");
1986 purple_xmlnode_set_namespace(unblock
, NS_SIMPLE_BLOCKING
);
1988 item
= purple_xmlnode_new_child(unblock
, "item");
1989 purple_xmlnode_set_attrib(item
, "jid", who
);
1994 void jabber_add_feature(const char *namespace, JabberFeatureEnabled cb
) {
1995 JabberFeature
*feat
;
1997 g_return_if_fail(namespace != NULL
);
1999 feat
= g_new0(JabberFeature
,1);
2000 feat
->namespace = g_strdup(namespace);
2001 feat
->is_enabled
= cb
;
2003 /* try to remove just in case it already exists in the list */
2004 jabber_remove_feature(namespace);
2006 jabber_features
= g_list_append(jabber_features
, feat
);
2009 void jabber_remove_feature(const char *namespace) {
2011 for(feature
= jabber_features
; feature
; feature
= feature
->next
) {
2012 JabberFeature
*feat
= (JabberFeature
*)feature
->data
;
2013 if(!strcmp(feat
->namespace, namespace)) {
2014 g_free(feat
->namespace);
2015 g_free(feature
->data
);
2016 jabber_features
= g_list_delete_link(jabber_features
, feature
);
2022 static void jabber_features_destroy(void)
2024 while (jabber_features
) {
2025 JabberFeature
*feature
= jabber_features
->data
;
2026 g_free(feature
->namespace);
2028 jabber_features
= g_list_delete_link(jabber_features
, jabber_features
);
2033 jabber_identity_compare(gconstpointer a
, gconstpointer b
)
2035 const JabberIdentity
*ac
;
2036 const JabberIdentity
*bc
;
2043 if ((cat_cmp
= strcmp(ac
->category
, bc
->category
)) == 0) {
2044 if ((typ_cmp
= strcmp(ac
->type
, bc
->type
)) == 0) {
2045 if (!ac
->lang
&& !bc
->lang
) {
2047 } else if (ac
->lang
&& !bc
->lang
) {
2049 } else if (!ac
->lang
&& bc
->lang
) {
2052 return strcmp(ac
->lang
, bc
->lang
);
2062 void jabber_add_identity(const gchar
*category
, const gchar
*type
,
2063 const gchar
*lang
, const gchar
*name
)
2066 JabberIdentity
*ident
;
2068 /* both required according to XEP-0030 */
2069 g_return_if_fail(category
!= NULL
);
2070 g_return_if_fail(type
!= NULL
);
2072 /* Check if this identity is already there... */
2073 for (identity
= jabber_identities
; identity
; identity
= identity
->next
) {
2074 JabberIdentity
*id
= identity
->data
;
2075 if (g_str_equal(id
->category
, category
) &&
2076 g_str_equal(id
->type
, type
) &&
2077 purple_strequal(id
->lang
, lang
))
2081 ident
= g_new0(JabberIdentity
, 1);
2082 ident
->category
= g_strdup(category
);
2083 ident
->type
= g_strdup(type
);
2084 ident
->lang
= g_strdup(lang
);
2085 ident
->name
= g_strdup(name
);
2086 jabber_identities
= g_list_insert_sorted(jabber_identities
, ident
,
2087 jabber_identity_compare
);
2090 static void jabber_identities_destroy(void)
2092 while (jabber_identities
) {
2093 JabberIdentity
*id
= jabber_identities
->data
;
2094 g_free(id
->category
);
2099 jabber_identities
= g_list_delete_link(jabber_identities
, jabber_identities
);
2103 gboolean
jabber_stream_is_ssl(JabberStream
*js
)
2105 return (js
->bosh
&& jabber_bosh_connection_is_ssl(js
->bosh
)) ||
2106 (!js
->bosh
&& js
->gsc
);
2110 inactivity_cb(gpointer data
)
2112 JabberStream
*js
= data
;
2114 /* We want whatever is sent to set this. It's okay because
2115 * the eventloop unsets it via the return FALSE.
2117 js
->inactivity_timer
= 0;
2120 jabber_bosh_connection_send_keepalive(js
->bosh
);
2122 jabber_send_raw(js
, "\t", 1);
2127 void jabber_stream_restart_inactivity_timer(JabberStream
*js
)
2129 if (js
->inactivity_timer
!= 0) {
2130 purple_timeout_remove(js
->inactivity_timer
);
2131 js
->inactivity_timer
= 0;
2134 g_return_if_fail(js
->max_inactivity
> 0);
2136 js
->inactivity_timer
=
2137 purple_timeout_add_seconds(js
->max_inactivity
,
2141 const char *jabber_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
2146 const char* jabber_list_emblem(PurpleBuddy
*b
)
2149 JabberBuddy
*jb
= NULL
;
2150 PurpleConnection
*gc
= purple_account_get_connection(purple_buddy_get_account(b
));
2155 js
= purple_connection_get_protocol_data(gc
);
2157 jb
= jabber_buddy_find(js
, purple_buddy_get_name(b
), FALSE
);
2159 if(!PURPLE_BUDDY_IS_ONLINE(b
)) {
2160 if(jb
&& (jb
->subscription
& JABBER_SUB_PENDING
||
2161 !(jb
->subscription
& JABBER_SUB_TO
)))
2162 return "not-authorized";
2166 JabberBuddyResource
*jbr
= jabber_buddy_find_resource(jb
, NULL
);
2168 const gchar
*client_type
=
2169 jabber_resource_get_identity_category_type(jbr
, "client");
2172 if (strcmp(client_type
, "phone") == 0) {
2174 } else if (strcmp(client_type
, "web") == 0) {
2176 } else if (strcmp(client_type
, "handheld") == 0) {
2178 } else if (strcmp(client_type
, "bot") == 0) {
2181 /* the default value "pc" falls through and has no emblem */
2189 char *jabber_status_text(PurpleBuddy
*b
)
2192 JabberBuddy
*jb
= NULL
;
2193 PurpleAccount
*account
= purple_buddy_get_account(b
);
2194 PurpleConnection
*gc
= purple_account_get_connection(account
);
2196 if (gc
&& purple_connection_get_protocol_data(gc
))
2197 jb
= jabber_buddy_find(purple_connection_get_protocol_data(gc
), purple_buddy_get_name(b
), FALSE
);
2199 if(jb
&& !PURPLE_BUDDY_IS_ONLINE(b
) && (jb
->subscription
& JABBER_SUB_PENDING
|| !(jb
->subscription
& JABBER_SUB_TO
))) {
2200 ret
= g_strdup(_("Not Authorized"));
2201 } else if(jb
&& !PURPLE_BUDDY_IS_ONLINE(b
) && jb
->error_msg
) {
2202 ret
= g_strdup(jb
->error_msg
);
2204 PurplePresence
*presence
= purple_buddy_get_presence(b
);
2205 PurpleStatus
*status
= purple_presence_get_active_status(presence
);
2206 const char *message
;
2208 if((message
= purple_status_get_attr_string(status
, "message"))) {
2209 ret
= g_markup_escape_text(message
, -1);
2210 } else if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_TUNE
)) {
2211 PurpleStatus
*status
= purple_presence_get_status(presence
, "tune");
2212 const char *title
= purple_status_get_attr_string(status
, PURPLE_TUNE_TITLE
);
2213 const char *artist
= purple_status_get_attr_string(status
, PURPLE_TUNE_ARTIST
);
2214 const char *album
= purple_status_get_attr_string(status
, PURPLE_TUNE_ALBUM
);
2215 ret
= purple_util_format_song_info(title
, artist
, album
, NULL
);
2223 jabber_tooltip_add_resource_text(JabberBuddyResource
*jbr
,
2224 PurpleNotifyUserInfo
*user_info
, gboolean multiple_resources
)
2228 char *label
, *value
;
2232 text
= g_markup_escape_text(jbr
->status
, -1);
2236 res
= g_strdup_printf(" (%s)", jbr
->name
);
2238 state
= jabber_buddy_state_get_name(jbr
->state
);
2239 if (text
!= NULL
&& !purple_utf8_strcasecmp(state
, text
)) {
2244 label
= g_strdup_printf("%s%s", _("Status"), (res
? res
: ""));
2245 value
= g_strdup_printf("%s%s%s", state
, (text
? ": " : ""), (text
? text
: ""));
2247 purple_notify_user_info_add_pair_html(user_info
, label
, value
);
2252 /* if the resource is idle, show that */
2253 /* only show it if there is more than one resource available for
2254 the buddy, since the "general" idleness will be shown anyway,
2255 this way we can see see the idleness of lower-priority resources */
2256 if (jbr
->idle
&& multiple_resources
) {
2258 purple_str_seconds_to_string(time(NULL
) - jbr
->idle
);
2259 label
= g_strdup_printf("%s%s", _("Idle"), (res
? res
: ""));
2260 purple_notify_user_info_add_pair_plaintext(user_info
, label
, idle_str
);
2267 void jabber_tooltip_text(PurpleBuddy
*b
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
2270 PurpleAccount
*account
;
2271 PurpleConnection
*gc
;
2274 g_return_if_fail(b
!= NULL
);
2276 account
= purple_buddy_get_account(b
);
2277 g_return_if_fail(account
!= NULL
);
2279 gc
= purple_account_get_connection(account
);
2280 g_return_if_fail(gc
!= NULL
);
2282 js
= purple_connection_get_protocol_data(gc
);
2283 g_return_if_fail(js
!= NULL
);
2285 jb
= jabber_buddy_find(js
, purple_buddy_get_name(b
), FALSE
);
2288 JabberBuddyResource
*jbr
= NULL
;
2289 PurplePresence
*presence
= purple_buddy_get_presence(b
);
2293 gboolean multiple_resources
=
2294 jb
->resources
&& g_list_next(jb
->resources
);
2295 JabberBuddyResource
*top_jbr
= jabber_buddy_find_resource(jb
, NULL
);
2297 /* resource-specific info for the top resource */
2299 jabber_tooltip_add_resource_text(top_jbr
, user_info
,
2300 multiple_resources
);
2303 for(l
=jb
->resources
; l
; l
= l
->next
) {
2305 /* the remaining resources */
2306 if (jbr
!= top_jbr
) {
2307 jabber_tooltip_add_resource_text(jbr
, user_info
,
2308 multiple_resources
);
2313 PurpleStatus
*status
;
2315 status
= purple_presence_get_status(presence
, "mood");
2316 mood
= purple_status_get_attr_string(status
, PURPLE_MOOD_NAME
);
2318 const char *moodtext
;
2320 PurpleMood
*moods
= jabber_get_moods(account
);
2321 const char *description
= NULL
;
2323 for (; moods
->mood
; moods
++) {
2324 if (purple_strequal(moods
->mood
, mood
)) {
2325 description
= moods
->description
;
2330 moodtext
= purple_status_get_attr_string(status
, PURPLE_MOOD_COMMENT
);
2331 if(moodtext
&& *moodtext
) {
2332 char *moodplustext
=
2333 g_strdup_printf("%s (%s)", description
? _(description
) : mood
, moodtext
);
2335 purple_notify_user_info_add_pair_html(user_info
, _("Mood"), moodplustext
);
2336 g_free(moodplustext
);
2338 purple_notify_user_info_add_pair_html(user_info
, _("Mood"),
2339 description
? _(description
) : mood
);
2341 if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_TUNE
)) {
2342 PurpleStatus
*tune
= purple_presence_get_status(presence
, "tune");
2343 const char *title
= purple_status_get_attr_string(tune
, PURPLE_TUNE_TITLE
);
2344 const char *artist
= purple_status_get_attr_string(tune
, PURPLE_TUNE_ARTIST
);
2345 const char *album
= purple_status_get_attr_string(tune
, PURPLE_TUNE_ALBUM
);
2346 char *playing
= purple_util_format_song_info(title
, artist
, album
, NULL
);
2348 purple_notify_user_info_add_pair_html(user_info
, _("Now Listening"), playing
);
2353 if(jb
->subscription
& JABBER_SUB_FROM
) {
2354 if(jb
->subscription
& JABBER_SUB_TO
)
2356 else if(jb
->subscription
& JABBER_SUB_PENDING
)
2357 sub
= _("From (To pending)");
2361 if(jb
->subscription
& JABBER_SUB_TO
)
2363 else if(jb
->subscription
& JABBER_SUB_PENDING
)
2364 sub
= _("None (To pending)");
2369 purple_notify_user_info_add_pair_html(user_info
, _("Subscription"), sub
);
2373 if(!PURPLE_BUDDY_IS_ONLINE(b
) && jb
->error_msg
) {
2374 purple_notify_user_info_add_pair_html(user_info
, _("Error"), jb
->error_msg
);
2379 GList
*jabber_status_types(PurpleAccount
*account
)
2381 PurpleStatusType
*type
;
2382 GList
*types
= NULL
;
2383 GValue
*priority_value
;
2384 GValue
*buzz_enabled
;
2386 priority_value
= purple_value_new(G_TYPE_INT
);
2387 g_value_set_int(priority_value
, 1);
2388 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2389 g_value_set_boolean(buzz_enabled
, TRUE
);
2390 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE
,
2391 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE
),
2392 NULL
, TRUE
, TRUE
, FALSE
,
2393 "priority", _("Priority"), priority_value
,
2394 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2395 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2396 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2397 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2398 "buzz", _("Allow Buzz"), buzz_enabled
,
2400 types
= g_list_prepend(types
, type
);
2403 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD
,
2404 "mood", NULL
, TRUE
, TRUE
, TRUE
,
2405 PURPLE_MOOD_NAME
, _("Mood Name"), purple_value_new(G_TYPE_STRING
),
2406 PURPLE_MOOD_COMMENT
, _("Mood Comment"), purple_value_new(G_TYPE_STRING
),
2408 types
= g_list_prepend(types
, type
);
2410 priority_value
= purple_value_new(G_TYPE_INT
);
2411 g_value_set_int(priority_value
, 1);
2412 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2413 g_value_set_boolean(buzz_enabled
, TRUE
);
2414 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE
,
2415 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_CHAT
),
2416 _("Chatty"), TRUE
, TRUE
, FALSE
,
2417 "priority", _("Priority"), priority_value
,
2418 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2419 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2420 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2421 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2422 "buzz", _("Allow Buzz"), buzz_enabled
,
2424 types
= g_list_prepend(types
, type
);
2426 priority_value
= purple_value_new(G_TYPE_INT
);
2427 g_value_set_int(priority_value
, 0);
2428 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2429 g_value_set_boolean(buzz_enabled
, TRUE
);
2430 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY
,
2431 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY
),
2432 NULL
, TRUE
, TRUE
, FALSE
,
2433 "priority", _("Priority"), priority_value
,
2434 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2435 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2436 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2437 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2438 "buzz", _("Allow Buzz"), buzz_enabled
,
2440 types
= g_list_prepend(types
, type
);
2442 priority_value
= purple_value_new(G_TYPE_INT
);
2443 g_value_set_int(priority_value
, 0);
2444 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2445 g_value_set_boolean(buzz_enabled
, TRUE
);
2446 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY
,
2447 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_XA
),
2448 NULL
, TRUE
, TRUE
, FALSE
,
2449 "priority", _("Priority"), priority_value
,
2450 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2451 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2452 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2453 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2454 "buzz", _("Allow Buzz"), buzz_enabled
,
2456 types
= g_list_prepend(types
, type
);
2458 priority_value
= purple_value_new(G_TYPE_INT
);
2459 g_value_set_int(priority_value
, 0);
2460 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE
,
2461 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_DND
),
2462 _("Do Not Disturb"), TRUE
, TRUE
, FALSE
,
2463 "priority", _("Priority"), priority_value
,
2464 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2465 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2466 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2467 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2469 types
= g_list_prepend(types
, type
);
2472 if(js->protocol_version == JABBER_PROTO_0_9)
2476 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE
,
2477 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE
),
2478 NULL
, TRUE
, TRUE
, FALSE
,
2479 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2481 types
= g_list_prepend(types
, type
);
2483 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE
,
2484 "tune", NULL
, FALSE
, TRUE
, TRUE
,
2485 PURPLE_TUNE_ARTIST
, _("Tune Artist"), purple_value_new(G_TYPE_STRING
),
2486 PURPLE_TUNE_TITLE
, _("Tune Title"), purple_value_new(G_TYPE_STRING
),
2487 PURPLE_TUNE_ALBUM
, _("Tune Album"), purple_value_new(G_TYPE_STRING
),
2488 PURPLE_TUNE_GENRE
, _("Tune Genre"), purple_value_new(G_TYPE_STRING
),
2489 PURPLE_TUNE_COMMENT
, _("Tune Comment"), purple_value_new(G_TYPE_STRING
),
2490 PURPLE_TUNE_TRACK
, _("Tune Track"), purple_value_new(G_TYPE_STRING
),
2491 PURPLE_TUNE_TIME
, _("Tune Time"), purple_value_new(G_TYPE_INT
),
2492 PURPLE_TUNE_YEAR
, _("Tune Year"), purple_value_new(G_TYPE_INT
),
2493 PURPLE_TUNE_URL
, _("Tune URL"), purple_value_new(G_TYPE_STRING
),
2495 types
= g_list_prepend(types
, type
);
2497 return g_list_reverse(types
);
2501 jabber_password_change_result_cb(JabberStream
*js
, const char *from
,
2502 JabberIqType type
, const char *id
,
2503 PurpleXmlNode
*packet
, gpointer data
)
2505 if (type
== JABBER_IQ_RESULT
) {
2506 purple_notify_info(js
->gc
, _("Password Changed"), _("Password "
2507 "Changed"), _("Your password has been changed."),
2508 purple_request_cpar_from_connection(js
->gc
));
2510 purple_account_set_password(purple_connection_get_account(js
->gc
), (const char *)data
, NULL
, NULL
);
2512 char *msg
= jabber_parse_error(js
, packet
, NULL
);
2514 purple_notify_error(js
->gc
, _("Error changing password"),
2515 _("Error changing password"), msg
,
2516 purple_request_cpar_from_connection(js
->gc
));
2523 static void jabber_password_change_cb(JabberStream
*js
,
2524 PurpleRequestFields
*fields
)
2526 const char *p1
, *p2
;
2528 PurpleXmlNode
*query
, *y
;
2530 p1
= purple_request_fields_get_string(fields
, "password1");
2531 p2
= purple_request_fields_get_string(fields
, "password2");
2533 if(strcmp(p1
, p2
)) {
2534 purple_notify_error(js
->gc
, NULL
,
2535 _("New passwords do not match."), NULL
,
2536 purple_request_cpar_from_connection(js
->gc
));
2540 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
2542 purple_xmlnode_set_attrib(iq
->node
, "to", js
->user
->domain
);
2544 query
= purple_xmlnode_get_child(iq
->node
, "query");
2546 y
= purple_xmlnode_new_child(query
, "username");
2547 purple_xmlnode_insert_data(y
, js
->user
->node
, -1);
2548 y
= purple_xmlnode_new_child(query
, "password");
2549 purple_xmlnode_insert_data(y
, p1
, -1);
2551 jabber_iq_set_callback(iq
, jabber_password_change_result_cb
, g_strdup(p1
));
2556 static void jabber_password_change(PurpleProtocolAction
*action
)
2559 PurpleConnection
*gc
= (PurpleConnection
*) action
->connection
;
2560 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
2561 PurpleRequestFields
*fields
;
2562 PurpleRequestFieldGroup
*group
;
2563 PurpleRequestField
*field
;
2565 fields
= purple_request_fields_new();
2566 group
= purple_request_field_group_new(NULL
);
2567 purple_request_fields_add_group(fields
, group
);
2569 field
= purple_request_field_string_new("password1", _("Password"),
2571 purple_request_field_string_set_masked(field
, TRUE
);
2572 purple_request_field_set_required(field
, TRUE
);
2573 purple_request_field_group_add_field(group
, field
);
2575 field
= purple_request_field_string_new("password2", _("Password (again)"),
2577 purple_request_field_string_set_masked(field
, TRUE
);
2578 purple_request_field_set_required(field
, TRUE
);
2579 purple_request_field_group_add_field(group
, field
);
2581 purple_request_fields(js
->gc
, _("Change XMPP Password"),
2582 _("Change XMPP Password"), _("Please enter your new password"),
2583 fields
, _("OK"), G_CALLBACK(jabber_password_change_cb
),
2585 purple_request_cpar_from_connection(gc
), js
);
2588 GList
*jabber_get_actions(PurpleConnection
*gc
)
2590 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
2592 PurpleProtocolAction
*act
;
2594 act
= purple_protocol_action_new(_("Set User Info..."),
2595 jabber_setup_set_info
);
2596 m
= g_list_append(m
, act
);
2598 /* if (js->account_options & CHANGE_PASSWORD) { */
2599 act
= purple_protocol_action_new(_("Change Password..."),
2600 jabber_password_change
);
2601 m
= g_list_append(m
, act
);
2604 act
= purple_protocol_action_new(_("Search for Users..."),
2605 jabber_user_search_begin
);
2606 m
= g_list_append(m
, act
);
2608 purple_debug_info("jabber", "jabber_get_actions: have pep: %s\n", js
->pep
?"YES":"NO");
2611 jabber_pep_init_actions(&m
);
2614 jabber_adhoc_init_server_commands(js
, &m
);
2619 PurpleChat
*jabber_find_blist_chat(PurpleAccount
*account
, const char *name
)
2621 PurpleBlistNode
*gnode
, *cnode
;
2624 if(!(jid
= jabber_id_new(name
)))
2627 for(gnode
= purple_blist_get_root(); gnode
;
2628 gnode
= purple_blist_node_get_sibling_next(gnode
)) {
2629 for(cnode
= purple_blist_node_get_first_child(gnode
);
2631 cnode
= purple_blist_node_get_sibling_next(cnode
)) {
2632 PurpleChat
*chat
= (PurpleChat
*)cnode
;
2633 const char *room
, *server
;
2634 GHashTable
*components
;
2635 if(!PURPLE_IS_CHAT(cnode
))
2638 if (purple_chat_get_account(chat
) != account
)
2641 components
= purple_chat_get_components(chat
);
2642 if(!(room
= g_hash_table_lookup(components
, "room")))
2644 if(!(server
= g_hash_table_lookup(components
, "server")))
2647 /* FIXME: Collate is wrong in a few cases here; this should be prepped */
2648 if(jid
->node
&& jid
->domain
&&
2649 !g_utf8_collate(room
, jid
->node
) && !g_utf8_collate(server
, jid
->domain
)) {
2650 jabber_id_free(jid
);
2655 jabber_id_free(jid
);
2659 void jabber_convo_closed(PurpleConnection
*gc
, const char *who
)
2661 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
2664 JabberBuddyResource
*jbr
;
2666 if(!(jid
= jabber_id_new(who
)))
2669 if((jb
= jabber_buddy_find(js
, who
, TRUE
)) &&
2670 (jbr
= jabber_buddy_find_resource(jb
, jid
->resource
))) {
2671 g_free(jbr
->thread_id
);
2672 jbr
->thread_id
= NULL
;
2675 jabber_id_free(jid
);
2679 char *jabber_parse_error(JabberStream
*js
,
2680 PurpleXmlNode
*packet
,
2681 PurpleConnectionError
*reason
)
2683 PurpleXmlNode
*error
;
2684 const char *code
= NULL
, *text
= NULL
;
2685 const char *xmlns
= purple_xmlnode_get_namespace(packet
);
2688 #define SET_REASON(x) \
2689 if(reason != NULL) { *reason = x; }
2691 if((error
= purple_xmlnode_get_child(packet
, "error"))) {
2692 PurpleXmlNode
*t
= purple_xmlnode_get_child_with_namespace(error
, "text", NS_XMPP_STANZAS
);
2694 cdata
= purple_xmlnode_get_data(t
);
2696 cdata
= purple_xmlnode_get_data(error
);
2698 code
= purple_xmlnode_get_attrib(error
, "code");
2701 if(purple_xmlnode_get_child(error
, "bad-request")) {
2702 text
= _("Bad Request");
2703 } else if(purple_xmlnode_get_child(error
, "conflict")) {
2704 SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE
);
2705 text
= _("Conflict");
2706 } else if(purple_xmlnode_get_child(error
, "feature-not-implemented")) {
2707 text
= _("Feature Not Implemented");
2708 } else if(purple_xmlnode_get_child(error
, "forbidden")) {
2709 text
= _("Forbidden");
2710 } else if(purple_xmlnode_get_child(error
, "gone")) {
2712 } else if(purple_xmlnode_get_child(error
, "internal-server-error")) {
2713 text
= _("Internal Server Error");
2714 } else if(purple_xmlnode_get_child(error
, "item-not-found")) {
2715 text
= _("Item Not Found");
2716 } else if(purple_xmlnode_get_child(error
, "jid-malformed")) {
2717 text
= _("Malformed XMPP ID");
2718 } else if(purple_xmlnode_get_child(error
, "not-acceptable")) {
2719 text
= _("Not Acceptable");
2720 } else if(purple_xmlnode_get_child(error
, "not-allowed")) {
2721 text
= _("Not Allowed");
2722 } else if(purple_xmlnode_get_child(error
, "not-authorized")) {
2723 text
= _("Not Authorized");
2724 } else if(purple_xmlnode_get_child(error
, "payment-required")) {
2725 text
= _("Payment Required");
2726 } else if(purple_xmlnode_get_child(error
, "recipient-unavailable")) {
2727 text
= _("Recipient Unavailable");
2728 } else if(purple_xmlnode_get_child(error
, "redirect")) {
2730 } else if(purple_xmlnode_get_child(error
, "registration-required")) {
2731 text
= _("Registration Required");
2732 } else if(purple_xmlnode_get_child(error
, "remote-server-not-found")) {
2733 text
= _("Remote Server Not Found");
2734 } else if(purple_xmlnode_get_child(error
, "remote-server-timeout")) {
2735 text
= _("Remote Server Timeout");
2736 } else if(purple_xmlnode_get_child(error
, "resource-constraint")) {
2737 text
= _("Server Overloaded");
2738 } else if(purple_xmlnode_get_child(error
, "service-unavailable")) {
2739 text
= _("Service Unavailable");
2740 } else if(purple_xmlnode_get_child(error
, "subscription-required")) {
2741 text
= _("Subscription Required");
2742 } else if(purple_xmlnode_get_child(error
, "unexpected-request")) {
2743 text
= _("Unexpected Request");
2744 } else if(purple_xmlnode_get_child(error
, "undefined-condition")) {
2745 text
= _("Unknown Error");
2747 } else if(xmlns
&& !strcmp(xmlns
, NS_XMPP_SASL
)) {
2748 /* Most common reason can be the default */
2749 SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR
);
2750 if(purple_xmlnode_get_child(packet
, "aborted")) {
2751 text
= _("Authorization Aborted");
2752 } else if(purple_xmlnode_get_child(packet
, "incorrect-encoding")) {
2753 text
= _("Incorrect encoding in authorization");
2754 } else if(purple_xmlnode_get_child(packet
, "invalid-authzid")) {
2755 text
= _("Invalid authzid");
2756 } else if(purple_xmlnode_get_child(packet
, "invalid-mechanism")) {
2757 text
= _("Invalid Authorization Mechanism");
2758 } else if(purple_xmlnode_get_child(packet
, "mechanism-too-weak")) {
2759 SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
);
2760 text
= _("Authorization mechanism too weak");
2761 } else if(purple_xmlnode_get_child(packet
, "not-authorized")) {
2762 SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
);
2763 /* Clear the pasword if it isn't being saved */
2764 if (!purple_account_get_remember_password(purple_connection_get_account(js
->gc
)))
2765 purple_account_set_password(purple_connection_get_account(js
->gc
), NULL
, NULL
, NULL
);
2766 text
= _("Not Authorized");
2767 } else if(purple_xmlnode_get_child(packet
, "temporary-auth-failure")) {
2768 text
= _("Temporary Authentication Failure");
2770 SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
);
2771 text
= _("Authentication Failure");
2773 } else if(!strcmp(packet
->name
, "stream:error") ||
2774 (!strcmp(packet
->name
, "error") && xmlns
&&
2775 !strcmp(xmlns
, NS_XMPP_STREAMS
))) {
2776 /* Most common reason as default: */
2777 SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR
);
2778 if(purple_xmlnode_get_child(packet
, "bad-format")) {
2779 text
= _("Bad Format");
2780 } else if(purple_xmlnode_get_child(packet
, "bad-namespace-prefix")) {
2781 text
= _("Bad Namespace Prefix");
2782 } else if(purple_xmlnode_get_child(packet
, "conflict")) {
2783 SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE
);
2784 text
= _("Resource Conflict");
2785 } else if(purple_xmlnode_get_child(packet
, "connection-timeout")) {
2786 text
= _("Connection Timeout");
2787 } else if(purple_xmlnode_get_child(packet
, "host-gone")) {
2788 text
= _("Host Gone");
2789 } else if(purple_xmlnode_get_child(packet
, "host-unknown")) {
2790 text
= _("Host Unknown");
2791 } else if(purple_xmlnode_get_child(packet
, "improper-addressing")) {
2792 text
= _("Improper Addressing");
2793 } else if(purple_xmlnode_get_child(packet
, "internal-server-error")) {
2794 text
= _("Internal Server Error");
2795 } else if(purple_xmlnode_get_child(packet
, "invalid-id")) {
2796 text
= _("Invalid ID");
2797 } else if(purple_xmlnode_get_child(packet
, "invalid-namespace")) {
2798 text
= _("Invalid Namespace");
2799 } else if(purple_xmlnode_get_child(packet
, "invalid-xml")) {
2800 text
= _("Invalid XML");
2801 } else if(purple_xmlnode_get_child(packet
, "nonmatching-hosts")) {
2802 text
= _("Non-matching Hosts");
2803 } else if(purple_xmlnode_get_child(packet
, "not-authorized")) {
2804 text
= _("Not Authorized");
2805 } else if(purple_xmlnode_get_child(packet
, "policy-violation")) {
2806 text
= _("Policy Violation");
2807 } else if(purple_xmlnode_get_child(packet
, "remote-connection-failed")) {
2808 text
= _("Remote Connection Failed");
2809 } else if(purple_xmlnode_get_child(packet
, "resource-constraint")) {
2810 text
= _("Resource Constraint");
2811 } else if(purple_xmlnode_get_child(packet
, "restricted-xml")) {
2812 text
= _("Restricted XML");
2813 } else if(purple_xmlnode_get_child(packet
, "see-other-host")) {
2814 text
= _("See Other Host");
2815 } else if(purple_xmlnode_get_child(packet
, "system-shutdown")) {
2816 text
= _("System Shutdown");
2817 } else if(purple_xmlnode_get_child(packet
, "undefined-condition")) {
2818 text
= _("Undefined Condition");
2819 } else if(purple_xmlnode_get_child(packet
, "unsupported-encoding")) {
2820 text
= _("Unsupported Encoding");
2821 } else if(purple_xmlnode_get_child(packet
, "unsupported-stanza-type")) {
2822 text
= _("Unsupported Stanza Type");
2823 } else if(purple_xmlnode_get_child(packet
, "unsupported-version")) {
2824 text
= _("Unsupported Version");
2825 } else if(purple_xmlnode_get_child(packet
, "xml-not-well-formed")) {
2826 text
= _("XML Not Well Formed");
2828 text
= _("Stream Error");
2835 char *ret
= g_strdup_printf("%s%s%s", code
? code
: "",
2836 code
? ": " : "", text
? text
: cdata
);
2844 static PurpleCmdRet
jabber_cmd_chat_config(PurpleConversation
*conv
,
2845 const char *cmd
, char **args
, char **error
, void *data
)
2847 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2850 return PURPLE_CMD_RET_FAILED
;
2852 jabber_chat_request_room_configure(chat
);
2853 return PURPLE_CMD_RET_OK
;
2856 static PurpleCmdRet
jabber_cmd_chat_register(PurpleConversation
*conv
,
2857 const char *cmd
, char **args
, char **error
, void *data
)
2859 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2862 return PURPLE_CMD_RET_FAILED
;
2864 jabber_chat_register(chat
);
2865 return PURPLE_CMD_RET_OK
;
2868 static PurpleCmdRet
jabber_cmd_chat_topic(PurpleConversation
*conv
,
2869 const char *cmd
, char **args
, char **error
, void *data
)
2871 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2874 return PURPLE_CMD_RET_FAILED
;
2876 if (args
&& args
[0] && *args
[0])
2877 jabber_chat_change_topic(chat
, args
[0]);
2879 const char *cur
= purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv
));
2880 char *buf
, *tmp
, *tmp2
;
2883 tmp
= g_markup_escape_text(cur
, -1);
2884 tmp2
= purple_markup_linkify(tmp
);
2885 buf
= g_strdup_printf(_("current topic is: %s"), tmp2
);
2889 buf
= g_strdup(_("No topic is set"));
2890 purple_conversation_write_system_message(conv
, buf
, PURPLE_MESSAGE_NO_LOG
);
2894 return PURPLE_CMD_RET_OK
;
2897 static PurpleCmdRet
jabber_cmd_chat_nick(PurpleConversation
*conv
,
2898 const char *cmd
, char **args
, char **error
, void *data
)
2900 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2902 if(!chat
|| !args
|| !args
[0])
2903 return PURPLE_CMD_RET_FAILED
;
2905 if (!jabber_resourceprep_validate(args
[0])) {
2906 *error
= g_strdup(_("Invalid nickname"));
2907 return PURPLE_CMD_RET_FAILED
;
2910 if (jabber_chat_change_nick(chat
, args
[0]))
2911 return PURPLE_CMD_RET_OK
;
2913 return PURPLE_CMD_RET_FAILED
;
2916 static PurpleCmdRet
jabber_cmd_chat_part(PurpleConversation
*conv
,
2917 const char *cmd
, char **args
, char **error
, void *data
)
2919 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2922 return PURPLE_CMD_RET_FAILED
;
2924 jabber_chat_part(chat
, args
? args
[0] : NULL
);
2925 return PURPLE_CMD_RET_OK
;
2928 static PurpleCmdRet
jabber_cmd_chat_ban(PurpleConversation
*conv
,
2929 const char *cmd
, char **args
, char **error
, void *data
)
2931 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2933 if(!chat
|| !args
|| !args
[0])
2934 return PURPLE_CMD_RET_FAILED
;
2936 if(!jabber_chat_ban_user(chat
, args
[0], args
[1])) {
2937 *error
= g_strdup_printf(_("Unable to ban user %s"), args
[0]);
2938 return PURPLE_CMD_RET_FAILED
;
2941 return PURPLE_CMD_RET_OK
;
2944 static PurpleCmdRet
jabber_cmd_chat_affiliate(PurpleConversation
*conv
,
2945 const char *cmd
, char **args
, char **error
, void *data
)
2947 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2949 if (!chat
|| !args
|| !args
[0])
2950 return PURPLE_CMD_RET_FAILED
;
2952 if (strcmp(args
[0], "owner") != 0 &&
2953 strcmp(args
[0], "admin") != 0 &&
2954 strcmp(args
[0], "member") != 0 &&
2955 strcmp(args
[0], "outcast") != 0 &&
2956 strcmp(args
[0], "none") != 0) {
2957 *error
= g_strdup_printf(_("Unknown affiliation: \"%s\""), args
[0]);
2958 return PURPLE_CMD_RET_FAILED
;
2963 char **nicks
= g_strsplit(args
[1], " ", -1);
2965 for (i
= 0; nicks
[i
]; ++i
)
2966 if (!jabber_chat_affiliate_user(chat
, nicks
[i
], args
[0])) {
2967 *error
= g_strdup_printf(_("Unable to affiliate user %s as \"%s\""), nicks
[i
], args
[0]);
2969 return PURPLE_CMD_RET_FAILED
;
2974 jabber_chat_affiliation_list(chat
, args
[0]);
2977 return PURPLE_CMD_RET_OK
;
2980 static PurpleCmdRet
jabber_cmd_chat_role(PurpleConversation
*conv
,
2981 const char *cmd
, char **args
, char **error
, void *data
)
2983 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2985 if (!chat
|| !args
|| !args
[0])
2986 return PURPLE_CMD_RET_FAILED
;
2988 if (strcmp(args
[0], "moderator") != 0 &&
2989 strcmp(args
[0], "participant") != 0 &&
2990 strcmp(args
[0], "visitor") != 0 &&
2991 strcmp(args
[0], "none") != 0) {
2992 *error
= g_strdup_printf(_("Unknown role: \"%s\""), args
[0]);
2993 return PURPLE_CMD_RET_FAILED
;
2998 char **nicks
= g_strsplit(args
[1], " ", -1);
3000 for (i
= 0; nicks
[i
]; i
++)
3001 if (!jabber_chat_role_user(chat
, nicks
[i
], args
[0], NULL
)) {
3002 *error
= g_strdup_printf(_("Unable to set role \"%s\" for user: %s"),
3005 return PURPLE_CMD_RET_FAILED
;
3010 jabber_chat_role_list(chat
, args
[0]);
3012 return PURPLE_CMD_RET_OK
;
3015 static PurpleCmdRet
jabber_cmd_chat_invite(PurpleConversation
*conv
,
3016 const char *cmd
, char **args
, char **error
, void *data
)
3018 if(!args
|| !args
[0])
3019 return PURPLE_CMD_RET_FAILED
;
3021 jabber_chat_invite(purple_conversation_get_connection(conv
),
3022 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv
)), args
[1] ? args
[1] : "",
3025 return PURPLE_CMD_RET_OK
;
3028 static PurpleCmdRet
jabber_cmd_chat_join(PurpleConversation
*conv
,
3029 const char *cmd
, char **args
, char **error
, void *data
)
3031 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
3032 GHashTable
*components
;
3033 JabberID
*jid
= NULL
;
3034 const char *room
= NULL
, *server
= NULL
, *handle
= NULL
;
3036 if (!chat
|| !args
|| !args
[0])
3037 return PURPLE_CMD_RET_FAILED
;
3039 components
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
3041 if (strchr(args
[0], '@'))
3042 jid
= jabber_id_new(args
[0]);
3045 server
= jid
->domain
;
3046 handle
= jid
->resource
? jid
->resource
: chat
->handle
;
3048 /* If jabber_id_new failed, the user may have just passed in
3049 * a room name. For backward compatibility, handle that here.
3051 if (strchr(args
[0], '@')) {
3052 *error
= g_strdup(_("Invalid XMPP ID"));
3053 return PURPLE_CMD_RET_FAILED
;
3057 server
= chat
->server
;
3058 handle
= chat
->handle
;
3061 g_hash_table_insert(components
, "room", (gpointer
)room
);
3062 g_hash_table_insert(components
, "server", (gpointer
)server
);
3063 g_hash_table_insert(components
, "handle", (gpointer
)handle
);
3066 g_hash_table_insert(components
, "password", args
[1]);
3068 jabber_chat_join(purple_conversation_get_connection(conv
), components
);
3070 g_hash_table_destroy(components
);
3071 jabber_id_free(jid
);
3072 return PURPLE_CMD_RET_OK
;
3075 static PurpleCmdRet
jabber_cmd_chat_kick(PurpleConversation
*conv
,
3076 const char *cmd
, char **args
, char **error
, void *data
)
3078 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
3080 if(!chat
|| !args
|| !args
[0])
3081 return PURPLE_CMD_RET_FAILED
;
3083 if(!jabber_chat_role_user(chat
, args
[0], "none", args
[1])) {
3084 *error
= g_strdup_printf(_("Unable to kick user %s"), args
[0]);
3085 return PURPLE_CMD_RET_FAILED
;
3088 return PURPLE_CMD_RET_OK
;
3091 static PurpleCmdRet
jabber_cmd_chat_msg(PurpleConversation
*conv
,
3092 const char *cmd
, char **args
, char **error
, void *data
)
3094 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
3098 return PURPLE_CMD_RET_FAILED
;
3100 who
= g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
, args
[0]);
3102 jabber_message_send_im(purple_conversation_get_connection(conv
),
3103 purple_message_new_outgoing(who
, args
[1], 0));
3106 return PURPLE_CMD_RET_OK
;
3109 static PurpleCmdRet
jabber_cmd_ping(PurpleConversation
*conv
,
3110 const char *cmd
, char **args
, char **error
, void *data
)
3112 PurpleAccount
*account
;
3113 PurpleConnection
*pc
;
3115 if(!args
|| !args
[0])
3116 return PURPLE_CMD_RET_FAILED
;
3118 account
= purple_conversation_get_account(conv
);
3119 pc
= purple_account_get_connection(account
);
3121 if(!jabber_ping_jid(purple_connection_get_protocol_data(pc
), args
[0])) {
3122 *error
= g_strdup_printf(_("Unable to ping user %s"), args
[0]);
3123 return PURPLE_CMD_RET_FAILED
;
3126 return PURPLE_CMD_RET_OK
;
3129 static gboolean
_jabber_send_buzz(JabberStream
*js
, const char *username
, char **error
) {
3132 JabberBuddyResource
*jbr
;
3133 PurpleConnection
*gc
= js
->gc
;
3134 PurpleBuddy
*buddy
=
3135 purple_blist_find_buddy(purple_connection_get_account(gc
), username
);
3136 const gchar
*alias
=
3137 buddy
? purple_buddy_get_contact_alias(buddy
) : username
;
3142 jb
= jabber_buddy_find(js
, username
, FALSE
);
3144 *error
= g_strdup_printf(_("Unable to buzz, because there is nothing "
3145 "known about %s."), alias
);
3149 jbr
= jabber_buddy_find_resource(jb
, NULL
);
3151 *error
= g_strdup_printf(_("Unable to buzz, because %s might be offline."),
3156 if (jabber_resource_has_capability(jbr
, NS_ATTENTION
)) {
3157 PurpleXmlNode
*buzz
, *msg
= purple_xmlnode_new("message");
3160 to
= g_strdup_printf("%s/%s", username
, jbr
->name
);
3161 purple_xmlnode_set_attrib(msg
, "to", to
);
3164 /* avoid offline storage */
3165 purple_xmlnode_set_attrib(msg
, "type", "headline");
3167 buzz
= purple_xmlnode_new_child(msg
, "attention");
3168 purple_xmlnode_set_namespace(buzz
, NS_ATTENTION
);
3170 jabber_send(js
, msg
);
3171 purple_xmlnode_free(msg
);
3175 *error
= g_strdup_printf(_("Unable to buzz, because %s does "
3176 "not support it or does not wish to receive buzzes now."), alias
);
3181 static PurpleCmdRet
jabber_cmd_buzz(PurpleConversation
*conv
,
3182 const char *cmd
, char **args
, char **error
, void *data
)
3184 PurpleAccount
*account
= purple_conversation_get_account(conv
);
3185 JabberStream
*js
= purple_connection_get_protocol_data(purple_account_get_connection(account
));
3190 PurpleAttentionType
*attn
=
3191 purple_get_attention_type_from_code(account
, 0);
3193 if (!args
|| !args
[0]) {
3194 /* use the buddy from conversation, if it's a one-to-one conversation */
3195 if (PURPLE_IS_IM_CONVERSATION(conv
)) {
3196 who
= purple_conversation_get_name(conv
);
3198 return PURPLE_CMD_RET_FAILED
;
3204 buddy
= purple_blist_find_buddy(account
, who
);
3206 alias
= purple_buddy_get_contact_alias(buddy
);
3211 g_strdup_printf(purple_attention_type_get_outgoing_desc(attn
), alias
);
3212 purple_conversation_write_system_message(conv
, description
,
3213 PURPLE_MESSAGE_NOTIFY
);
3214 g_free(description
);
3215 return _jabber_send_buzz(js
, who
, error
) ? PURPLE_CMD_RET_OK
: PURPLE_CMD_RET_FAILED
;
3218 GList
*jabber_attention_types(PurpleAccount
*account
)
3220 static GList
*types
= NULL
;
3223 types
= g_list_append(types
, purple_attention_type_new("Buzz", _("Buzz"),
3224 _("%s has buzzed you!"), _("Buzzing %s...")));
3230 gboolean
jabber_send_attention(PurpleConnection
*gc
, const char *username
, guint code
)
3232 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3233 gchar
*error
= NULL
;
3235 if (!_jabber_send_buzz(js
, username
, &error
)) {
3236 PurpleAccount
*account
= purple_connection_get_account(gc
);
3237 PurpleConversation
*conv
=
3238 purple_conversations_find_with_account(username
, account
);
3239 purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error
? error
: "(NULL)");
3242 purple_conversation_write_system_message(conv
,
3243 error
, PURPLE_MESSAGE_ERROR
);
3254 gboolean
jabber_offline_message(const PurpleBuddy
*buddy
)
3261 jabber_audio_enabled(JabberStream
*js
, const char *namespace)
3263 PurpleMediaManager
*manager
= purple_media_manager_get();
3264 PurpleMediaCaps caps
= purple_media_manager_get_ui_caps(manager
);
3266 return (caps
& (PURPLE_MEDIA_CAPS_AUDIO
| PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION
));
3270 jabber_video_enabled(JabberStream
*js
, const char *namespace)
3272 PurpleMediaManager
*manager
= purple_media_manager_get();
3273 PurpleMediaCaps caps
= purple_media_manager_get_ui_caps(manager
);
3275 return (caps
& (PURPLE_MEDIA_CAPS_VIDEO
| PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION
));
3279 PurpleAccount
*account
;
3281 PurpleMediaSessionType type
;
3283 } JabberMediaRequest
;
3286 jabber_media_cancel_cb(JabberMediaRequest
*request
,
3287 PurpleRequestFields
*fields
)
3289 g_free(request
->who
);
3294 jabber_media_ok_cb(JabberMediaRequest
*request
, PurpleRequestFields
*fields
)
3296 PurpleRequestField
*field
=
3297 purple_request_fields_get_field(fields
, "resource");
3298 const gchar
*selected
= purple_request_field_choice_get_value(field
);
3299 gchar
*who
= g_strdup_printf("%s/%s", request
->who
, selected
);
3300 jabber_initiate_media(request
->account
, who
, request
->type
);
3303 g_free(request
->who
);
3309 jabber_initiate_media(PurpleAccount
*account
, const char *who
,
3310 PurpleMediaSessionType type
)
3313 PurpleConnection
*gc
= purple_account_get_connection(account
);
3314 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3316 JabberBuddyResource
*jbr
= NULL
;
3317 char *resource
= NULL
;
3320 purple_debug_error("jabber",
3321 "jabber_initiate_media: NULL stream\n");
3325 jb
= jabber_buddy_find(js
, who
, FALSE
);
3327 if(!jb
|| !jb
->resources
||
3328 (((resource
= jabber_get_resource(who
)) != NULL
)
3329 && (jbr
= jabber_buddy_find_resource(jb
, resource
)) == NULL
)) {
3330 /* no resources online, we're trying to initiate with someone
3331 * whose presence we're not subscribed to, or
3332 * someone who is offline. Let's inform the user */
3336 msg
= g_strdup_printf(_("Unable to initiate media with %s: invalid JID"), who
);
3337 } else if(jb
->subscription
& JABBER_SUB_TO
&& !jb
->resources
) {
3338 msg
= g_strdup_printf(_("Unable to initiate media with %s: user is not online"), who
);
3339 } else if(resource
) {
3340 msg
= g_strdup_printf(_("Unable to initiate media with %s: resource is not online"), who
);
3342 msg
= g_strdup_printf(_("Unable to initiate media with %s: not subscribed to user presence"), who
);
3345 purple_notify_error(account
, _("Media Initiation Failed"),
3346 _("Media Initiation Failed"), msg
,
3347 purple_request_cpar_from_connection(gc
));
3351 } else if(jbr
!= NULL
) {
3352 /* they've specified a resource, no need to ask or
3353 * default or anything, just do it */
3357 if (type
& PURPLE_MEDIA_AUDIO
&&
3358 !jabber_resource_has_capability(jbr
,
3359 JINGLE_APP_RTP_SUPPORT_AUDIO
) &&
3360 jabber_resource_has_capability(jbr
, NS_GOOGLE_VOICE
))
3361 return jabber_google_session_initiate(js
, who
, type
);
3363 return jingle_rtp_initiate_media(js
, who
, type
);
3364 } else if(!jb
->resources
->next
) {
3365 /* only 1 resource online (probably our most common case)
3366 * so no need to ask who to initiate with */
3369 jbr
= jb
->resources
->data
;
3370 name
= g_strdup_printf("%s/%s", who
, jbr
->name
);
3371 result
= jabber_initiate_media(account
, name
, type
);
3375 /* we've got multiple resources,
3376 * we need to pick one to initiate with */
3379 PurpleRequestFields
*fields
;
3380 PurpleRequestField
*field
= purple_request_field_choice_new(
3381 "resource", _("Resource"), 0);
3382 PurpleRequestFieldGroup
*group
;
3383 JabberMediaRequest
*request
;
3385 purple_request_field_choice_set_data_destructor(field
, g_free
);
3387 for(l
= jb
->resources
; l
; l
= l
->next
)
3389 JabberBuddyResource
*ljbr
= l
->data
;
3390 PurpleMediaCaps caps
;
3392 name
= g_strdup_printf("%s/%s", who
, ljbr
->name
);
3393 caps
= jabber_get_media_caps(account
, name
);
3396 if ((type
& PURPLE_MEDIA_AUDIO
) &&
3397 (type
& PURPLE_MEDIA_VIDEO
)) {
3398 if (caps
& PURPLE_MEDIA_CAPS_AUDIO_VIDEO
) {
3400 purple_request_field_choice_add(field
,
3401 jbr
->name
, g_strdup(jbr
->name
));
3403 } else if (type
& (PURPLE_MEDIA_AUDIO
) &&
3404 (caps
& PURPLE_MEDIA_CAPS_AUDIO
)) {
3406 purple_request_field_choice_add(field
,
3407 jbr
->name
, g_strdup(jbr
->name
));
3408 }else if (type
& (PURPLE_MEDIA_VIDEO
) &&
3409 (caps
& PURPLE_MEDIA_CAPS_VIDEO
)) {
3411 purple_request_field_choice_add(field
,
3412 jbr
->name
, g_strdup(jbr
->name
));
3417 purple_debug_error("jabber",
3418 "No resources available\n");
3422 if (g_list_length(purple_request_field_choice_get_elements(
3426 purple_request_field_destroy(field
);
3427 name
= g_strdup_printf("%s/%s", who
, jbr
->name
);
3428 result
= jabber_initiate_media(account
, name
, type
);
3433 msg
= g_strdup_printf(_("Please select the resource of %s with which you would like to start a media session."), who
);
3434 fields
= purple_request_fields_new();
3435 group
= purple_request_field_group_new(NULL
);
3436 request
= g_new0(JabberMediaRequest
, 1);
3437 request
->account
= account
;
3438 request
->who
= g_strdup(who
);
3439 request
->type
= type
;
3441 purple_request_field_group_add_field(group
, field
);
3442 purple_request_fields_add_group(fields
, group
);
3443 purple_request_fields(account
, _("Select a Resource"), msg
,
3444 NULL
, fields
, _("Initiate Media"),
3445 G_CALLBACK(jabber_media_ok_cb
), _("Cancel"),
3446 G_CALLBACK(jabber_media_cancel_cb
),
3447 purple_request_cpar_from_account(account
),
3457 PurpleMediaCaps
jabber_get_media_caps(PurpleAccount
*account
, const char *who
)
3460 PurpleConnection
*gc
= purple_account_get_connection(account
);
3461 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3463 JabberBuddyResource
*jbr
;
3464 PurpleMediaCaps total
= PURPLE_MEDIA_CAPS_NONE
;
3466 GList
*specific
= NULL
, *l
;
3469 purple_debug_info("jabber",
3470 "jabber_can_do_media: NULL stream\n");
3474 jb
= jabber_buddy_find(js
, who
, FALSE
);
3476 if (!jb
|| !jb
->resources
) {
3477 /* no resources online, we're trying to get caps for someone
3478 * whose presence we're not subscribed to, or
3479 * someone who is offline. */
3482 } else if ((resource
= jabber_get_resource(who
)) != NULL
) {
3483 /* they've specified a resource, no need to ask or
3484 * default or anything, just do it */
3485 jbr
= jabber_buddy_find_resource(jb
, resource
);
3489 purple_debug_error("jabber", "jabber_get_media_caps:"
3490 " Can't find resource %s\n", who
);
3494 l
= specific
= g_list_prepend(specific
, jbr
);
3497 /* we've got multiple resources, combine their caps */
3501 for (; l
; l
= l
->next
) {
3502 PurpleMediaCaps caps
= PURPLE_MEDIA_CAPS_NONE
;
3505 if (jabber_resource_has_capability(jbr
,
3506 JINGLE_APP_RTP_SUPPORT_AUDIO
))
3507 caps
|= PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION
|
3508 PURPLE_MEDIA_CAPS_AUDIO
;
3509 if (jabber_resource_has_capability(jbr
,
3510 JINGLE_APP_RTP_SUPPORT_VIDEO
))
3511 caps
|= PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION
|
3512 PURPLE_MEDIA_CAPS_VIDEO
;
3513 if (caps
& PURPLE_MEDIA_CAPS_AUDIO
&& caps
&
3514 PURPLE_MEDIA_CAPS_VIDEO
)
3515 caps
|= PURPLE_MEDIA_CAPS_AUDIO_VIDEO
;
3516 if (caps
!= PURPLE_MEDIA_CAPS_NONE
) {
3517 if (!jabber_resource_has_capability(jbr
,
3518 JINGLE_TRANSPORT_ICEUDP
) &&
3519 !jabber_resource_has_capability(jbr
,
3520 JINGLE_TRANSPORT_RAWUDP
)) {
3521 purple_debug_info("jingle-rtp", "Buddy doesn't "
3522 "support the same transport types\n");
3523 caps
= PURPLE_MEDIA_CAPS_NONE
;
3525 caps
|= PURPLE_MEDIA_CAPS_MODIFY_SESSION
|
3526 PURPLE_MEDIA_CAPS_CHANGE_DIRECTION
;
3528 if (jabber_resource_has_capability(jbr
, NS_GOOGLE_VOICE
)) {
3529 caps
|= PURPLE_MEDIA_CAPS_AUDIO
;
3530 if (jabber_resource_has_capability(jbr
, NS_GOOGLE_VIDEO
))
3531 caps
|= PURPLE_MEDIA_CAPS_AUDIO_VIDEO
;
3538 g_list_free(specific
);
3543 return PURPLE_MEDIA_CAPS_NONE
;
3547 gboolean
jabber_can_receive_file(PurpleConnection
*gc
, const char *who
)
3549 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3552 JabberBuddy
*jb
= jabber_buddy_find(js
, who
, FALSE
);
3554 gboolean has_resources_without_caps
= FALSE
;
3556 /* if we didn't find a JabberBuddy, we don't have presence for this
3557 buddy, let's assume they can receive files, disco should tell us
3558 when actually trying */
3562 /* find out if there is any resources without caps */
3563 for (iter
= jb
->resources
; iter
; iter
= g_list_next(iter
)) {
3564 JabberBuddyResource
*jbr
= (JabberBuddyResource
*) iter
->data
;
3566 if (!jabber_resource_know_capabilities(jbr
)) {
3567 has_resources_without_caps
= TRUE
;
3571 if (has_resources_without_caps
) {
3572 /* there is at least one resource which we don't have caps for,
3573 let's assume they can receive files... */
3576 /* we have caps for all the resources, see if at least one has
3578 for (iter
= jb
->resources
; iter
; iter
= g_list_next(iter
)) {
3579 JabberBuddyResource
*jbr
= (JabberBuddyResource
*) iter
->data
;
3581 if (jabber_resource_has_capability(jbr
, NS_SI_FILE_TRANSFER
)
3582 && (jabber_resource_has_capability(jbr
,
3584 || jabber_resource_has_capability(jbr
, NS_IBB
))) {
3596 jabber_cmd_mood(PurpleConversation
*conv
,
3597 const char *cmd
, char **args
, char **error
, void *data
)
3599 PurpleAccount
*account
= purple_conversation_get_account(conv
);
3600 JabberStream
*js
= purple_connection_get_protocol_data(purple_account_get_connection(account
));
3605 if (!args
|| !args
[0]) {
3606 /* No arguments; unset mood */
3607 ret
= jabber_mood_set(js
, NULL
, NULL
);
3609 /* At least one argument. Relying on the list of arguments
3610 * being NULL-terminated.
3612 ret
= jabber_mood_set(js
, args
[0], args
[1]);
3614 /* Let's try again */
3615 char *tmp
= g_strjoin(" ", args
[0], args
[1], NULL
);
3616 ret
= jabber_mood_set(js
, "undefined", tmp
);
3622 return PURPLE_CMD_RET_OK
;
3624 purple_conversation_write_system_message(conv
,
3625 _("Failed to specify mood"),
3626 PURPLE_MESSAGE_ERROR
);
3627 return PURPLE_CMD_RET_FAILED
;
3630 /* account does not support PEP, can't set a mood */
3631 purple_conversation_write_system_message(conv
,
3632 _("Account does not support PEP, can't set mood"),
3633 PURPLE_MESSAGE_ERROR
);
3634 return PURPLE_CMD_RET_FAILED
;
3639 jabber_register_commands(PurpleProtocol
*protocol
)
3641 GSList
*commands
= NULL
;
3643 const gchar
*proto_id
= purple_protocol_get_id(protocol
);
3645 id
= purple_cmd_register("config", "", PURPLE_CMD_P_PROTOCOL
,
3646 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3647 jabber_cmd_chat_config
, _("config: Configure a chat room."),
3649 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3651 id
= purple_cmd_register("configure", "", PURPLE_CMD_P_PROTOCOL
,
3652 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3653 jabber_cmd_chat_config
, _("configure: Configure a chat room."),
3655 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3657 id
= purple_cmd_register("nick", "s", PURPLE_CMD_P_PROTOCOL
,
3658 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3659 jabber_cmd_chat_nick
, _("nick <new nickname>: "
3660 "Change your nickname."), NULL
);
3661 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3663 id
= purple_cmd_register("part", "s", PURPLE_CMD_P_PROTOCOL
,
3664 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3665 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_part
,
3666 _("part [message]: Leave the room."), NULL
);
3667 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3669 id
= purple_cmd_register("register", "", PURPLE_CMD_P_PROTOCOL
,
3670 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3671 jabber_cmd_chat_register
,
3672 _("register: Register with a chat room."), NULL
);
3673 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3675 /* XXX: there needs to be a core /topic cmd, methinks */
3676 id
= purple_cmd_register("topic", "s", PURPLE_CMD_P_PROTOCOL
,
3677 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3678 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_topic
,
3679 _("topic [new topic]: View or change the topic."), NULL
);
3680 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3682 id
= purple_cmd_register("ban", "ws", PURPLE_CMD_P_PROTOCOL
,
3683 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3684 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_ban
,
3685 _("ban <user> [reason]: Ban a user from the room."),
3687 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3689 id
= purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PROTOCOL
,
3690 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3691 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
,
3692 jabber_cmd_chat_affiliate
, _("affiliate "
3693 "<owner|admin|member|outcast|none> [nick1] [nick2] ...: "
3694 "Get the users with an affiliation or set users' affiliation "
3695 "with the room."), NULL
);
3696 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3698 id
= purple_cmd_register("role", "ws", PURPLE_CMD_P_PROTOCOL
,
3699 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3700 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_role
,
3701 _("role <moderator|participant|visitor|none> [nick1] "
3702 "[nick2] ...: Get the users with a role or set users' role "
3703 "with the room."), NULL
);
3704 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3706 id
= purple_cmd_register("invite", "ws", PURPLE_CMD_P_PROTOCOL
,
3707 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3708 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_invite
,
3709 _("invite <user> [message]: Invite a user to the room."),
3711 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3713 id
= purple_cmd_register("join", "ws", PURPLE_CMD_P_PROTOCOL
,
3714 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3715 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_join
,
3716 _("join: <room[@server]> [password]: Join a chat."),
3718 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3720 id
= purple_cmd_register("kick", "ws", PURPLE_CMD_P_PROTOCOL
,
3721 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3722 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_kick
,
3723 _("kick <user> [reason]: Kick a user from the room."),
3725 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3727 id
= purple_cmd_register("msg", "ws", PURPLE_CMD_P_PROTOCOL
,
3728 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3729 jabber_cmd_chat_msg
, _("msg <user> <message>: "
3730 "Send a private message to another user."), NULL
);
3731 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3733 id
= purple_cmd_register("ping", "w", PURPLE_CMD_P_PROTOCOL
,
3734 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_IM
|
3735 PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
, jabber_cmd_ping
,
3736 _("ping <jid>: Ping a user/component/server."), NULL
);
3737 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3739 id
= purple_cmd_register("buzz", "w", PURPLE_CMD_P_PROTOCOL
,
3740 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3741 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_buzz
,
3742 _("buzz: Buzz a user to get their attention"), NULL
);
3743 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3745 id
= purple_cmd_register("mood", "ws", PURPLE_CMD_P_PROTOCOL
,
3746 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_IM
|
3747 PURPLE_CMD_FLAG_PROTOCOL_ONLY
| PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
,
3748 proto_id
, jabber_cmd_mood
,
3749 _("mood <mood> [text]: Set current user mood"), NULL
);
3750 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3752 g_hash_table_insert(jabber_cmds
, protocol
, commands
);
3755 static void cmds_free_func(gpointer value
)
3757 GSList
*commands
= value
;
3759 purple_cmd_unregister(GPOINTER_TO_UINT(commands
->data
));
3760 commands
= g_slist_delete_link(commands
, commands
);
3764 static void jabber_unregister_commands(PurpleProtocol
*protocol
)
3766 g_hash_table_remove(jabber_cmds
, protocol
);
3773 * IPC function for determining if a contact supports a certain feature.
3775 * @param account The PurpleAccount
3776 * @param jid The full JID of the contact.
3777 * @param feature The feature's namespace.
3779 * @return TRUE if supports feature; else FALSE.
3782 jabber_ipc_contact_has_feature(PurpleAccount
*account
, const gchar
*jid
,
3783 const gchar
*feature
)
3785 PurpleConnection
*gc
= purple_account_get_connection(account
);
3788 JabberBuddyResource
*jbr
;
3791 if (!purple_account_is_connected(account
))
3793 js
= purple_connection_get_protocol_data(gc
);
3795 if (!(resource
= jabber_get_resource(jid
)) ||
3796 !(jb
= jabber_buddy_find(js
, jid
, FALSE
)) ||
3797 !(jbr
= jabber_buddy_find_resource(jb
, resource
))) {
3804 return jabber_resource_has_capability(jbr
, feature
);
3808 jabber_ipc_add_feature(const gchar
*feature
)
3812 jabber_add_feature(feature
, 0);
3814 /* send presence with new caps info for all connected accounts */
3815 jabber_caps_broadcast_change();
3819 static PurpleAccount
*find_acct(const char *protocol
, const char *acct_id
)
3821 PurpleAccount
*acct
= NULL
;
3823 /* If we have a specific acct, use it */
3825 acct
= purple_accounts_find(acct_id
, protocol
);
3826 if (acct
&& !purple_account_is_connected(acct
))
3828 } else { /* Otherwise find an active account for the protocol */
3829 GList
*l
= purple_accounts_get_all();
3831 if (!strcmp(protocol
, purple_account_get_protocol_id(l
->data
))
3832 && purple_account_is_connected(l
->data
)) {
3843 static gboolean
xmpp_uri_handler(const char *proto
, const char *user
, GHashTable
*params
)
3845 char *acct_id
= params
? g_hash_table_lookup(params
, "account") : NULL
;
3846 PurpleAccount
*acct
;
3848 if (g_ascii_strcasecmp(proto
, "xmpp"))
3851 acct
= find_acct(proto
, acct_id
);
3856 /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */
3857 /* params is NULL if the URI has no '?' (or anything after it) */
3858 if (!params
|| g_hash_table_lookup_extended(params
, "message", NULL
, NULL
)) {
3859 char *body
= g_hash_table_lookup(params
, "body");
3860 if (user
&& *user
) {
3861 PurpleIMConversation
*im
=
3862 purple_im_conversation_new(acct
, user
);
3863 purple_conversation_present(PURPLE_CONVERSATION(im
));
3865 purple_conversation_send_confirm(PURPLE_CONVERSATION(im
), body
);
3867 } else if (g_hash_table_lookup_extended(params
, "roster", NULL
, NULL
)) {
3868 char *name
= g_hash_table_lookup(params
, "name");
3870 purple_blist_request_add_buddy(acct
, user
, NULL
, name
);
3871 } else if (g_hash_table_lookup_extended(params
, "join", NULL
, NULL
)) {
3872 PurpleConnection
*gc
= purple_account_get_connection(acct
);
3873 if (user
&& *user
) {
3874 GHashTable
*params
= jabber_chat_info_defaults(gc
, user
);
3875 jabber_chat_join(gc
, params
);
3884 jabber_do_init(void)
3886 GHashTable
*ui_info
= purple_core_get_ui_info();
3887 const gchar
*ui_type
;
3888 const gchar
*type
= "pc"; /* default client type, if unknown or
3890 const gchar
*ui_name
= NULL
;
3891 #ifdef HAVE_CYRUS_SASL
3892 /* We really really only want to do this once per process */
3893 static gboolean sasl_initialized
= FALSE
;
3895 UINT old_error_mode
;
3901 /* XXX - If any other plugin wants SASL this won't be good ... */
3902 #ifdef HAVE_CYRUS_SASL
3903 if (!sasl_initialized
) {
3904 sasl_initialized
= TRUE
;
3906 sasldir
= g_build_filename(wpurple_bin_dir(), "sasl2", NULL
);
3907 sasl_set_path(SASL_PATH_TYPE_PLUGIN
, sasldir
);
3909 /* Suppress error popups for failing to load sasl plugins */
3910 old_error_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
3912 if ((ret
= sasl_client_init(NULL
)) != SASL_OK
) {
3913 purple_debug_error("xmpp", "Error (%d) initializing SASL.\n", ret
);
3916 /* Restore the original error mode */
3917 SetErrorMode(old_error_mode
);
3922 jabber_cmds
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
, NULL
, cmds_free_func
);
3924 ui_type
= ui_info
? g_hash_table_lookup(ui_info
, "client_type") : NULL
;
3926 if (strcmp(ui_type
, "pc") == 0 ||
3927 strcmp(ui_type
, "console") == 0 ||
3928 strcmp(ui_type
, "phone") == 0 ||
3929 strcmp(ui_type
, "handheld") == 0 ||
3930 strcmp(ui_type
, "web") == 0 ||
3931 strcmp(ui_type
, "bot") == 0) {
3937 ui_name
= g_hash_table_lookup(ui_info
, "name");
3938 if (ui_name
== NULL
)
3941 jabber_add_identity("client", type
, NULL
, ui_name
);
3943 /* initialize jabber_features list */
3944 jabber_add_feature(NS_LAST_ACTIVITY
, NULL
);
3945 jabber_add_feature(NS_OOB_IQ_DATA
, NULL
);
3946 jabber_add_feature(NS_ENTITY_TIME
, NULL
);
3947 jabber_add_feature("jabber:iq:version", NULL
);
3948 jabber_add_feature("jabber:x:conference", NULL
);
3949 jabber_add_feature(NS_BYTESTREAMS
, NULL
);
3950 jabber_add_feature("http://jabber.org/protocol/caps", NULL
);
3951 jabber_add_feature("http://jabber.org/protocol/chatstates", NULL
);
3952 jabber_add_feature(NS_DISCO_INFO
, NULL
);
3953 jabber_add_feature(NS_DISCO_ITEMS
, NULL
);
3954 jabber_add_feature(NS_IBB
, NULL
);
3955 jabber_add_feature("http://jabber.org/protocol/muc", NULL
);
3956 jabber_add_feature("http://jabber.org/protocol/muc#user", NULL
);
3957 jabber_add_feature("http://jabber.org/protocol/si", NULL
);
3958 jabber_add_feature(NS_SI_FILE_TRANSFER
, NULL
);
3959 jabber_add_feature(NS_XHTML_IM
, NULL
);
3960 jabber_add_feature(NS_PING
, NULL
);
3962 /* Buzz/Attention */
3963 jabber_add_feature(NS_ATTENTION
, jabber_buzz_isenabled
);
3965 /* Bits Of Binary */
3966 jabber_add_feature(NS_BOB
, NULL
);
3968 /* Jingle features! */
3969 jabber_add_feature(JINGLE
, NULL
);
3972 jabber_add_feature(NS_GOOGLE_PROTOCOL_SESSION
, jabber_audio_enabled
);
3973 jabber_add_feature(NS_GOOGLE_VOICE
, jabber_audio_enabled
);
3974 jabber_add_feature(NS_GOOGLE_VIDEO
, jabber_video_enabled
);
3975 jabber_add_feature(NS_GOOGLE_CAMERA
, jabber_video_enabled
);
3976 jabber_add_feature(JINGLE_APP_RTP
, NULL
);
3977 jabber_add_feature(JINGLE_APP_RTP_SUPPORT_AUDIO
, jabber_audio_enabled
);
3978 jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO
, jabber_video_enabled
);
3979 jabber_add_feature(JINGLE_TRANSPORT_RAWUDP
, NULL
);
3980 jabber_add_feature(JINGLE_TRANSPORT_ICEUDP
, NULL
);
3982 g_signal_connect(G_OBJECT(purple_media_manager_get()), "ui-caps-changed",
3983 G_CALLBACK(jabber_caps_broadcast_change
), NULL
);
3986 /* reverse order of unload_plugin */
3988 jabber_presence_init();
3990 /* PEP things should be init via jabber_pep_init, not here */
3995 /* TODO: Implement adding and retrieving own features via IPC API */
4004 jabber_do_uninit(void)
4006 /* reverse order of jabber_do_init */
4007 jabber_bosh_uninit();
4008 jabber_data_uninit();
4010 jabber_ibb_uninit();
4011 /* PEP things should be uninit via jabber_pep_uninit, not here */
4012 jabber_pep_uninit();
4013 jabber_caps_uninit();
4014 jabber_presence_uninit();
4018 g_signal_handlers_disconnect_by_func(G_OBJECT(purple_media_manager_get()),
4019 G_CALLBACK(jabber_caps_broadcast_change
), NULL
);
4022 jabber_auth_uninit();
4023 jabber_features_destroy();
4024 jabber_identities_destroy();
4026 g_hash_table_destroy(jabber_cmds
);
4030 static void jabber_init_protocol(PurpleProtocol
*protocol
)
4034 if (plugin_ref
== 1)
4037 jabber_register_commands(protocol
);
4041 purple_plugin_ipc_register(plugin
, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature
),
4042 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER
,
4044 PURPLE_TYPE_ACCOUNT
, G_TYPE_STRING
, G_TYPE_STRING
);
4046 purple_plugin_ipc_register(plugin
, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature
),
4047 purple_marshal_VOID__POINTER
,
4048 G_TYPE_NONE
, 1, G_TYPE_STRING
);
4050 purple_plugin_ipc_register(plugin
, "register_namespace_watcher",
4051 PURPLE_CALLBACK(jabber_iq_signal_register
),
4052 purple_marshal_VOID__POINTER_POINTER
,
4054 G_TYPE_STRING
, /* node */
4055 G_TYPE_STRING
); /* namespace */
4057 purple_plugin_ipc_register(plugin
, "unregister_namespace_watcher",
4058 PURPLE_CALLBACK(jabber_iq_signal_unregister
),
4059 purple_marshal_VOID__POINTER_POINTER
,
4061 G_TYPE_STRING
, /* node */
4062 G_TYPE_STRING
); /* namespace */
4065 purple_signal_register(protocol
, "jabber-register-namespace-watcher",
4066 purple_marshal_VOID__POINTER_POINTER
,
4068 G_TYPE_STRING
, /* node */
4069 G_TYPE_STRING
); /* namespace */
4071 purple_signal_register(protocol
, "jabber-unregister-namespace-watcher",
4072 purple_marshal_VOID__POINTER_POINTER
,
4074 G_TYPE_STRING
, /* node */
4075 G_TYPE_STRING
); /* namespace */
4077 purple_signal_connect(protocol
, "jabber-register-namespace-watcher",
4078 protocol
, PURPLE_CALLBACK(jabber_iq_signal_register
), NULL
);
4079 purple_signal_connect(protocol
, "jabber-unregister-namespace-watcher",
4080 protocol
, PURPLE_CALLBACK(jabber_iq_signal_unregister
), NULL
);
4083 purple_signal_register(protocol
, "jabber-receiving-xmlnode",
4084 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
4085 PURPLE_TYPE_CONNECTION
,
4086 G_TYPE_POINTER
); /* pointer to a PurpleXmlNode* */
4088 purple_signal_register(protocol
, "jabber-sending-xmlnode",
4089 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
4090 PURPLE_TYPE_CONNECTION
,
4091 G_TYPE_POINTER
); /* pointer to a PurpleXmlNode* */
4094 * Do not remove this or the plugin will fail. Completely. You have been
4097 purple_signal_connect_priority(protocol
, "jabber-sending-xmlnode",
4098 protocol
, PURPLE_CALLBACK(jabber_send_signal_cb
),
4099 NULL
, PURPLE_SIGNAL_PRIORITY_HIGHEST
);
4101 purple_signal_register(protocol
, "jabber-sending-text",
4102 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
4103 PURPLE_TYPE_CONNECTION
,
4104 G_TYPE_POINTER
); /* pointer to a string */
4106 purple_signal_register(protocol
, "jabber-receiving-message",
4107 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER
,
4109 PURPLE_TYPE_CONNECTION
,
4110 G_TYPE_STRING
, /* type */
4111 G_TYPE_STRING
, /* id */
4112 G_TYPE_STRING
, /* from */
4113 G_TYPE_STRING
, /* to */
4114 PURPLE_TYPE_XMLNODE
);
4116 purple_signal_register(protocol
, "jabber-receiving-iq",
4117 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER
,
4119 PURPLE_TYPE_CONNECTION
,
4120 G_TYPE_STRING
, /* type */
4121 G_TYPE_STRING
, /* id */
4122 G_TYPE_STRING
, /* from */
4123 PURPLE_TYPE_XMLNODE
);
4125 purple_signal_register(protocol
, "jabber-watched-iq",
4126 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER
,
4128 PURPLE_TYPE_CONNECTION
,
4129 G_TYPE_STRING
, /* type */
4130 G_TYPE_STRING
, /* id */
4131 G_TYPE_STRING
, /* from */
4132 PURPLE_TYPE_XMLNODE
); /* child */
4134 purple_signal_register(protocol
, "jabber-receiving-presence",
4135 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER
,
4137 PURPLE_TYPE_CONNECTION
,
4138 G_TYPE_STRING
, /* type */
4139 G_TYPE_STRING
, /* from */
4140 PURPLE_TYPE_XMLNODE
);
4143 static void jabber_uninit_protocol(PurpleProtocol
*protocol
)
4145 g_return_if_fail(plugin_ref
> 0);
4147 purple_signals_unregister_by_instance(protocol
);
4149 purple_plugin_ipc_unregister_all(plugin
);
4151 jabber_unregister_commands(protocol
);
4154 if (plugin_ref
== 0)
4159 jabber_protocol_init(PurpleProtocol
*protocol
)
4161 protocol
->id
= "prpl-jabber";
4162 protocol
->name
= "XMPP";
4163 protocol
->options
= OPT_PROTO_CHAT_TOPIC
| OPT_PROTO_UNIQUE_CHATNAME
|
4164 OPT_PROTO_MAIL_CHECK
|
4165 #ifdef HAVE_CYRUS_SASL
4166 OPT_PROTO_PASSWORD_OPTIONAL
|
4168 OPT_PROTO_SLASH_COMMANDS_NATIVE
;
4170 protocol
->icon_spec
= purple_buddy_icon_spec_new("png",
4172 PURPLE_ICON_SCALE_SEND
|
4173 PURPLE_ICON_SCALE_DISPLAY
);
4177 jabber_protocol_class_init(PurpleProtocolClass
*klass
)
4179 klass
->login
= jabber_login
;
4180 klass
->close
= jabber_close
;
4181 klass
->status_types
= jabber_status_types
;
4182 klass
->list_icon
= jabber_list_icon
;
4186 jabber_protocol_client_iface_init(PurpleProtocolClientIface
*client_iface
)
4188 client_iface
->get_actions
= jabber_get_actions
;
4189 client_iface
->list_emblem
= jabber_list_emblem
;
4190 client_iface
->status_text
= jabber_status_text
;
4191 client_iface
->tooltip_text
= jabber_tooltip_text
;
4192 client_iface
->blist_node_menu
= jabber_blist_node_menu
;
4193 client_iface
->convo_closed
= jabber_convo_closed
;
4194 client_iface
->normalize
= jabber_normalize
;
4195 client_iface
->find_blist_chat
= jabber_find_blist_chat
;
4196 client_iface
->offline_message
= jabber_offline_message
;
4197 client_iface
->get_moods
= jabber_get_moods
;
4201 jabber_protocol_server_iface_init(PurpleProtocolServerIface
*server_iface
)
4203 server_iface
->register_user
= jabber_register_account
;
4204 server_iface
->unregister_user
= jabber_unregister_account
;
4205 server_iface
->set_info
= jabber_set_info
;
4206 server_iface
->get_info
= jabber_buddy_get_info
;
4207 server_iface
->set_status
= jabber_set_status
;
4208 server_iface
->set_idle
= jabber_idle_set
;
4209 server_iface
->add_buddy
= jabber_roster_add_buddy
;
4210 server_iface
->remove_buddy
= jabber_roster_remove_buddy
;
4211 server_iface
->keepalive
= jabber_keepalive
;
4212 server_iface
->alias_buddy
= jabber_roster_alias_change
;
4213 server_iface
->group_buddy
= jabber_roster_group_change
;
4214 server_iface
->rename_group
= jabber_roster_group_rename
;
4215 server_iface
->set_buddy_icon
= jabber_set_buddy_icon
;
4216 server_iface
->send_raw
= jabber_protocol_send_raw
;
4220 jabber_protocol_im_iface_init(PurpleProtocolIMIface
*im_iface
)
4222 im_iface
->send
= jabber_message_send_im
;
4223 im_iface
->send_typing
= jabber_send_typing
;
4227 jabber_protocol_chat_iface_init(PurpleProtocolChatIface
*chat_iface
)
4229 chat_iface
->info
= jabber_chat_info
;
4230 chat_iface
->info_defaults
= jabber_chat_info_defaults
;
4231 chat_iface
->join
= jabber_chat_join
;
4232 chat_iface
->get_name
= jabber_get_chat_name
;
4233 chat_iface
->invite
= jabber_chat_invite
;
4234 chat_iface
->leave
= jabber_chat_leave
;
4235 chat_iface
->send
= jabber_message_send_chat
;
4236 chat_iface
->get_user_real_name
= jabber_chat_user_real_name
;
4237 chat_iface
->set_topic
= jabber_chat_set_topic
;
4241 jabber_protocol_privacy_iface_init(PurpleProtocolPrivacyIface
*privacy_iface
)
4243 privacy_iface
->add_deny
= jabber_add_deny
;
4244 privacy_iface
->rem_deny
= jabber_rem_deny
;
4248 jabber_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface
*roomlist_iface
)
4250 roomlist_iface
->get_list
= jabber_roomlist_get_list
;
4251 roomlist_iface
->cancel
= jabber_roomlist_cancel
;
4252 roomlist_iface
->room_serialize
= jabber_roomlist_room_serialize
;
4256 jabber_protocol_attention_iface_init(PurpleProtocolAttentionIface
*attention_iface
)
4258 attention_iface
->send
= jabber_send_attention
;
4259 attention_iface
->get_types
= jabber_attention_types
;
4263 jabber_protocol_media_iface_init(PurpleProtocolMediaIface
*media_iface
)
4265 media_iface
->initiate_session
= jabber_initiate_media
;
4266 media_iface
->get_caps
= jabber_get_media_caps
;
4270 jabber_protocol_xfer_iface_init(PurpleProtocolXferIface
*xfer_iface
)
4272 xfer_iface
->can_receive
= jabber_can_receive_file
;
4273 xfer_iface
->send
= jabber_si_xfer_send
;
4274 xfer_iface
->new_xfer
= jabber_si_new_xfer
;
4277 PURPLE_DEFINE_TYPE_EXTENDED(
4278 JabberProtocol
, jabber_protocol
, PURPLE_TYPE_PROTOCOL
, G_TYPE_FLAG_ABSTRACT
,
4280 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE
,
4281 jabber_protocol_client_iface_init
)
4283 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE
,
4284 jabber_protocol_server_iface_init
)
4286 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE
,
4287 jabber_protocol_im_iface_init
)
4289 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE
,
4290 jabber_protocol_chat_iface_init
)
4292 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE
,
4293 jabber_protocol_privacy_iface_init
)
4295 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE
,
4296 jabber_protocol_roomlist_iface_init
)
4298 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE
,
4299 jabber_protocol_attention_iface_init
)
4301 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_MEDIA_IFACE
,
4302 jabber_protocol_media_iface_init
)
4304 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE
,
4305 jabber_protocol_xfer_iface_init
)
4308 static PurplePluginInfo
*
4309 plugin_query(GError
**error
)
4311 return purple_plugin_info_new(
4313 "name", "XMPP Protocols",
4314 "version", DISPLAY_VERSION
,
4315 "category", N_("Protocol"),
4316 "summary", N_("XMPP and GTalk Protocols Plugin"),
4317 "description", N_("XMPP and GTalk Protocols Plugin"),
4318 "website", PURPLE_WEBSITE
,
4319 "abi-version", PURPLE_ABI_VERSION
,
4320 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
4321 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
4327 plugin_load(PurplePlugin
*plugin
, GError
**error
)
4329 jingle_session_register_type(plugin
);
4331 jingle_transport_register_type(plugin
);
4332 jingle_iceudp_register_type(plugin
);
4333 jingle_rawudp_register_type(plugin
);
4334 jingle_google_p2p_register_type(plugin
);
4336 jingle_content_register_type(plugin
);
4338 jingle_rtp_register_type(plugin
);
4341 jabber_protocol_register_type(plugin
);
4343 gtalk_protocol_register_type(plugin
);
4344 xmpp_protocol_register_type(plugin
);
4346 xmpp_protocol
= purple_protocols_add(XMPP_TYPE_PROTOCOL
, error
);
4350 gtalk_protocol
= purple_protocols_add(GTALK_TYPE_PROTOCOL
, error
);
4351 if (!gtalk_protocol
)
4354 purple_signal_connect(purple_get_core(), "uri-handler", xmpp_protocol
,
4355 PURPLE_CALLBACK(xmpp_uri_handler
), NULL
);
4356 purple_signal_connect(purple_get_core(), "uri-handler", gtalk_protocol
,
4357 PURPLE_CALLBACK(xmpp_uri_handler
), NULL
);
4359 jabber_init_protocol(xmpp_protocol
);
4360 jabber_init_protocol(gtalk_protocol
);
4366 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
4368 jabber_uninit_protocol(gtalk_protocol
);
4369 jabber_uninit_protocol(xmpp_protocol
);
4371 if (!purple_protocols_remove(gtalk_protocol
, error
))
4374 if (!purple_protocols_remove(xmpp_protocol
, error
))
4380 PURPLE_PLUGIN_INIT(jabber
, plugin_query
, plugin_load
, plugin_unload
);