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 "buddylist.h"
29 #include "connection.h"
30 #include "conversation.h"
35 #include "pluginpref.h"
38 #include "purpleaccountoption.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
)
223 PurpleAccount
*account
= NULL
;
224 PurpleXmlNode
*starttls
= NULL
;
226 /* It's a secure BOSH connection, just return FALSE and skip, without doing anything extra.
227 * XEP-0206 (XMPP Over BOSH): The client SHOULD ignore any Transport Layer Security (TLS)
228 * feature since BOSH channel encryption SHOULD be negotiated at the HTTP layer.
230 * Note: we are already receiving STARTTLS at this point from a SSL/TLS BOSH connection,
231 * so it is not necessary to check if purple_ssl_is_supported().
233 if (js
->bosh
&& jabber_bosh_connection_is_ssl(js
->bosh
)) {
237 /* Otherwise, it's a standard XMPP connection, or a HTTP (insecure) BOSH connection.
238 * We request STARTTLS for standard XMPP connections, but we do nothing for insecure
239 * BOSH connections, per XEP-0206. */
242 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1);
246 /* It's an insecure standard XMPP connection, or an insecure BOSH connection, let's
247 * ignore STARTTLS even it's required by the server to prevent disabling HTTP BOSH
248 * entirely (sysadmin is responsible to provide HTTPS-only BOSH if security is required),
249 * and emit errors if encryption is required by the user. */
250 starttls
= purple_xmlnode_get_child(packet
, "starttls");
251 if(!js
->bosh
&& 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 account
= purple_connection_get_account(js
->gc
);
259 if (purple_strequal("require_tls", purple_account_get_string(account
, "connection_security", JABBER_DEFAULT_REQUIRE_TLS
))) {
260 purple_connection_error(js
->gc
,
261 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
262 _("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 (purple_strequal(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(purple_strequal((*packet
)->name
, "iq")) {
348 jabber_iq_parse(js
, *packet
);
349 } else if(purple_strequal((*packet
)->name
, "presence")) {
350 jabber_presence_parse(js
, *packet
);
351 } else if(purple_strequal((*packet
)->name
, "message")) {
352 jabber_message_parse(js
, *packet
);
353 } else if (purple_strequal(xmlns
, NS_XMPP_STREAMS
)) {
354 if (purple_strequal(name
, "features"))
355 jabber_stream_features_parse(js
, *packet
);
356 else if (purple_strequal(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 (purple_strequal(name
, "challenge"))
363 jabber_auth_handle_challenge(js
, *packet
);
364 else if (purple_strequal(name
, "success"))
365 jabber_auth_handle_success(js
, *packet
);
366 else if (purple_strequal(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 (purple_strequal(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 (!purple_strequal(data
, "\t")) {
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 (purple_strequal((*packet
)->name
, "message") ||
612 purple_strequal((*packet
)->name
, "iq") ||
613 purple_strequal((*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
);
638 if (js
->keepalive_timeout
== 0) {
639 jabber_keepalive_ping(js
);
640 js
->keepalive_timeout
= g_timeout_add_seconds(120,
641 (GSourceFunc
)(jabber_keepalive_timeout
), gc
);
645 static int jabber_get_keepalive_interval(void)
651 jabber_recv_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
652 PurpleInputCondition cond
)
654 PurpleConnection
*gc
= data
;
655 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
657 static char buf
[4096];
659 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
661 while((len
= purple_ssl_read(gsc
, buf
, sizeof(buf
) - 1)) > 0) {
662 purple_connection_update_last_received(gc
);
664 purple_debug_misc("jabber", "Recv (ssl)(%d): %s", len
, buf
);
665 jabber_parser_process(js
, buf
, len
);
667 jabber_stream_init(js
);
670 if(len
< 0 && errno
== EAGAIN
)
675 tmp
= g_strdup(_("Server closed the connection"));
677 tmp
= g_strdup_printf(_("Lost connection with server: %s"),
679 purple_connection_error(js
->gc
,
680 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
686 jabber_recv_cb(gpointer data
, gint source
, PurpleInputCondition condition
)
688 PurpleConnection
*gc
= data
;
689 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
691 static char buf
[4096];
693 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
695 if((len
= read(js
->fd
, buf
, sizeof(buf
) - 1)) > 0) {
696 purple_connection_update_last_received(gc
);
697 #ifdef HAVE_CYRUS_SASL
698 if (js
->sasl_maxbuf
> 0) {
703 rc
= sasl_decode(js
->sasl
, buf
, len
, &out
, &olen
);
706 g_strdup_printf(_("SASL error: %s"),
707 sasl_errdetail(js
->sasl
));
708 purple_debug_error("jabber",
709 "sasl_decode_error %d: %s\n", rc
,
710 sasl_errdetail(js
->sasl
));
711 purple_connection_error(gc
,
712 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
714 } else if (olen
> 0) {
715 purple_debug_info("jabber", "RecvSASL (%u): %s\n", olen
, out
);
716 jabber_parser_process(js
, out
, olen
);
718 jabber_stream_init(js
);
724 purple_debug_misc("jabber", "Recv (%d): %s", len
, buf
);
725 jabber_parser_process(js
, buf
, len
);
727 jabber_stream_init(js
);
728 } else if(len
< 0 && errno
== EAGAIN
) {
733 tmp
= g_strdup(_("Server closed the connection"));
735 tmp
= g_strdup_printf(_("Lost connection with server: %s"),
737 purple_connection_error(js
->gc
,
738 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
744 jabber_login_callback_ssl(gpointer data
, PurpleSslConnection
*gsc
,
745 PurpleInputCondition cond
)
747 PurpleConnection
*gc
= data
;
750 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
752 js
= purple_connection_get_protocol_data(gc
);
754 if(js
->state
== JABBER_STREAM_CONNECTING
)
755 jabber_send_raw(js
, "<?xml version='1.0' ?>", -1);
756 jabber_stream_set_state(js
, JABBER_STREAM_INITIALIZING
);
757 purple_ssl_input_add(gsc
, jabber_recv_cb_ssl
, gc
);
759 /* Tell the app that we're doing encryption */
760 jabber_stream_set_state(js
, JABBER_STREAM_INITIALIZING_ENCRYPTION
);
764 txt_resolved_cb(GObject
*sender
, GAsyncResult
*result
, gpointer data
)
766 GError
*error
= NULL
;
767 GList
*records
= NULL
, *l
= NULL
;
768 JabberStream
*js
= data
;
769 gboolean found
= FALSE
;
771 records
= g_resolver_lookup_records_finish(G_RESOLVER(sender
),
774 purple_debug_warning("jabber", "Unable to find alternative XMPP connection "
775 "methods after failing to connect directly. : %s\n",
778 purple_connection_error(js
->gc
,
779 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
780 _("Unable to connect"));
787 for(l
= records
; l
; l
= l
->next
) {
788 GVariantIter
*iter
= NULL
;
791 g_variant_get((GVariant
*)l
->data
, "(as)", &iter
);
792 while(g_variant_iter_loop(iter
, "s", &str
)) {
793 gchar
**token
= g_strsplit(str
, "=", 2);
795 if(!g_ascii_strcasecmp(token
[0], "_xmpp-client-xbosh")) {
796 purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token
[0], token
[1]);
798 js
->bosh
= jabber_bosh_connection_new(js
, token
[1]);
808 g_variant_iter_free(iter
);
811 g_list_free_full(records
, (GDestroyNotify
)g_variant_unref
);
817 purple_debug_warning("jabber", "Unable to find alternative XMPP connection "
818 "methods after failing to connect directly.\n");
819 purple_connection_error(js
->gc
,
820 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
821 _("Unable to connect"));
827 jabber_login_callback(gpointer data
, gint source
, const gchar
*error
)
829 PurpleConnection
*gc
= data
;
830 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
833 GResolver
*resolver
= g_resolver_get_default();
834 gchar
*name
= g_strdup_printf("_xmppconnect.%s", js
->user
->domain
);
836 purple_debug_info("jabber", "Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js
->user
->domain
);
838 g_resolver_lookup_records_async(resolver
,
840 G_RESOLVER_RECORD_TXT
,
845 g_object_unref(resolver
);
852 if(js
->state
== JABBER_STREAM_CONNECTING
)
853 jabber_send_raw(js
, "<?xml version='1.0' ?>", -1);
855 jabber_stream_set_state(js
, JABBER_STREAM_INITIALIZING
);
856 js
->inpa
= purple_input_add(js
->fd
, PURPLE_INPUT_READ
, jabber_recv_cb
, gc
);
860 jabber_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
863 PurpleConnection
*gc
= data
;
866 PURPLE_ASSERT_CONNECTION_IS_VALID(gc
);
868 js
= purple_connection_get_protocol_data(gc
);
871 purple_connection_ssl_error (gc
, error
);
874 static void tls_init(JabberStream
*js
)
876 purple_input_remove(js
->inpa
);
878 js
->gsc
= purple_ssl_connect_with_host_fd(purple_connection_get_account(js
->gc
), js
->fd
,
879 jabber_login_callback_ssl
, jabber_ssl_connect_failure
, js
->certificate_CN
, js
->gc
);
880 /* The fd is no longer our concern */
884 static gboolean
jabber_login_connect(JabberStream
*js
, const char *domain
, const char *host
, int port
,
885 gboolean fatal_failure
)
887 /* host should be used in preference to domain to
888 * allow SASL authentication to work with FQDN of the server,
889 * but we use domain as fallback for when users enter IP address
890 * in connect server */
891 g_free(js
->serverFQDN
);
892 if (purple_ip_address_is_valid(host
))
893 js
->serverFQDN
= g_strdup(domain
);
895 js
->serverFQDN
= g_strdup(host
);
897 if (purple_proxy_connect(js
->gc
, purple_connection_get_account(js
->gc
),
898 host
, port
, jabber_login_callback
, js
->gc
) == NULL
) {
900 purple_connection_error(js
->gc
,
901 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
902 _("Unable to connect"));
912 srv_resolved_cb(GObject
*sender
, GAsyncResult
*result
, gpointer data
)
914 GError
*error
= NULL
;
915 GList
*targets
= NULL
, *l
= NULL
;
916 JabberStream
*js
= data
;
918 targets
= g_resolver_lookup_service_finish(G_RESOLVER(sender
),
921 purple_debug_warning("jabber",
922 "SRV lookup failed, proceeding with normal connection : %s",
927 jabber_login_connect(js
, js
->user
->domain
, js
->user
->domain
,
928 purple_account_get_int(purple_connection_get_account(js
->gc
), "port", 5222),
932 for(l
= targets
; l
; l
= l
->next
) {
933 GSrvTarget
*target
= (GSrvTarget
*)l
->data
;
934 const gchar
*hostname
= g_srv_target_get_hostname(target
);
935 guint port
= g_srv_target_get_port(target
);
937 if(jabber_login_connect(js
, hostname
, hostname
, port
, FALSE
)) {
938 g_resolver_free_targets(targets
);
944 g_resolver_free_targets(targets
);
946 jabber_login_connect(js
, js
->user
->domain
, js
->user
->domain
,
947 purple_account_get_int(purple_connection_get_account(js
->gc
), "port", 5222),
952 static JabberStream
*
953 jabber_stream_new(PurpleAccount
*account
)
955 PurpleConnection
*gc
= purple_account_get_connection(account
);
957 PurplePresence
*presence
;
961 js
= g_new0(JabberStream
, 1);
962 purple_connection_set_protocol_data(gc
, js
);
965 js
->http_conns
= purple_http_connection_set_new();
967 /* we might want to expose this at some point */
968 js
->cancellable
= g_cancellable_new();
970 user
= g_strdup(purple_account_get_username(account
));
971 /* jabber_id_new doesn't accept "user@domain/" as valid */
972 slash
= strchr(user
, '/');
973 if (slash
&& *(slash
+ 1) == '\0')
975 js
->user
= jabber_id_new(user
);
978 purple_connection_error(gc
,
979 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
980 _("Invalid XMPP ID"));
982 /* Destroying the connection will free the JabberStream */
986 if (!js
->user
->node
|| *(js
->user
->node
) == '\0') {
987 purple_connection_error(gc
,
988 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
989 _("Invalid XMPP ID. Username portion must be set."));
991 /* Destroying the connection will free the JabberStream */
995 if (!js
->user
->domain
|| *(js
->user
->domain
) == '\0') {
996 purple_connection_error(gc
,
997 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
998 _("Invalid XMPP ID. Domain must be set."));
1000 /* Destroying the connection will free the JabberStream */
1004 js
->buddies
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1005 g_free
, (GDestroyNotify
)jabber_buddy_free
);
1007 /* This is overridden during binding, but we need it here
1008 * in case the server only does legacy non-sasl auth!.
1010 purple_connection_set_display_name(gc
, user
);
1012 js
->user_jb
= jabber_buddy_find(js
, user
, TRUE
);
1015 /* This basically *can't* fail, but for good measure... */
1016 purple_connection_error(gc
,
1017 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
1018 _("Invalid XMPP ID"));
1019 /* Destroying the connection will free the JabberStream */
1020 g_return_val_if_reached(NULL
);
1023 js
->user_jb
->subscription
|= JABBER_SUB_BOTH
;
1025 js
->iq_callbacks
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1026 g_free
, (GDestroyNotify
)jabber_iq_callbackdata_free
);
1027 js
->chats
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1028 g_free
, (GDestroyNotify
)jabber_chat_free
);
1029 js
->next_id
= g_random_int();
1030 js
->write_buffer
= purple_circular_buffer_new(512);
1032 js
->keepalive_timeout
= 0;
1033 js
->max_inactivity
= DEFAULT_INACTIVITY_TIME
;
1034 /* Set the default protocol version to 1.0. Overridden in parser.c. */
1035 js
->protocol_version
.major
= 1;
1036 js
->protocol_version
.minor
= 0;
1037 js
->sessions
= NULL
;
1040 js
->google_relay_token
= NULL
;
1041 js
->google_relay_host
= NULL
;
1043 /* if we are idle, set idle-ness on the stream (this could happen if we get
1044 disconnected and the reconnects while being idle. I don't think it makes
1045 sense to do this when registering a new account... */
1046 presence
= purple_account_get_presence(account
);
1047 if (purple_presence_is_idle(presence
))
1048 js
->idle
= purple_presence_get_idle_time(presence
);
1054 jabber_stream_connect(JabberStream
*js
)
1056 PurpleConnection
*gc
= js
->gc
;
1057 PurpleAccount
*account
= purple_connection_get_account(gc
);
1058 const char *connect_server
= purple_account_get_string(account
,
1059 "connect_server", "");
1060 const char *bosh_url
= purple_account_get_string(account
,
1063 jabber_stream_set_state(js
, JABBER_STREAM_CONNECTING
);
1065 /* If both BOSH and a Connect Server are specified, we prefer BOSH. I'm not
1066 * attached to that choice, though.
1069 js
->bosh
= jabber_bosh_connection_new(js
, bosh_url
);
1071 purple_connection_error(gc
,
1072 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
1073 _("Malformed BOSH URL"));
1079 js
->certificate_CN
= g_strdup(connect_server
[0] ? connect_server
: js
->user
->domain
);
1081 /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */
1082 if (purple_strequal("old_ssl", purple_account_get_string(account
, "connection_security", JABBER_DEFAULT_REQUIRE_TLS
))) {
1083 js
->gsc
= purple_ssl_connect(account
, js
->certificate_CN
,
1084 purple_account_get_int(account
, "port", 5223),
1085 jabber_login_callback_ssl
, jabber_ssl_connect_failure
, gc
);
1087 purple_connection_error(gc
,
1088 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
1089 _("Unable to establish SSL connection"));
1095 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll
1096 * invoke the magic of SRV lookups, to figure out host and port */
1097 if(connect_server
[0]) {
1098 jabber_login_connect(js
, js
->user
->domain
, connect_server
,
1099 purple_account_get_int(account
, "port", 5222), TRUE
);
1101 GResolver
*resolver
= g_resolver_get_default();
1102 g_resolver_lookup_service_async(resolver
,
1109 g_object_unref(resolver
);
1114 jabber_login(PurpleAccount
*account
)
1116 PurpleConnection
*gc
= purple_account_get_connection(account
);
1120 purple_connection_set_flags(gc
, PURPLE_CONNECTION_FLAG_HTML
|
1121 PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY
|
1122 PURPLE_CONNECTION_FLAG_NO_IMAGES
);
1123 js
= jabber_stream_new(account
);
1127 /* replace old default proxies with the new default: NULL
1128 * TODO: these can eventually be removed */
1129 if (purple_strequal("proxy.jabber.org", purple_account_get_string(account
, "ft_proxies", ""))
1130 || purple_strequal("proxy.eu.jabber.org", purple_account_get_string(account
, "ft_proxies", "")))
1131 purple_account_set_string(account
, "ft_proxies", NULL
);
1134 * Calculate the avatar hash for our current image so we know (when we
1135 * fetch our vCard and PEP avatar) if we should send our avatar to the
1138 image
= purple_buddy_icons_find_account_icon(account
);
1139 if (image
!= NULL
) {
1140 js
->initial_avatar_hash
= g_compute_checksum_for_data(
1142 purple_image_get_data(image
),
1143 purple_image_get_data_size(image
)
1145 g_object_unref(image
);
1148 jabber_stream_connect(js
);
1153 conn_close_cb(gpointer data
)
1155 JabberStream
*js
= data
;
1156 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1158 jabber_parser_free(js
);
1160 purple_account_disconnect(account
);
1162 js
->conn_close_timeout
= 0;
1168 jabber_connection_schedule_close(JabberStream
*js
)
1170 js
->conn_close_timeout
= g_timeout_add(0, conn_close_cb
, js
);
1174 jabber_registration_result_cb(JabberStream
*js
, const char *from
,
1175 JabberIqType type
, const char *id
,
1176 PurpleXmlNode
*packet
, gpointer data
)
1178 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1182 if (type
== JABBER_IQ_RESULT
) {
1183 if(js
->registration
) {
1184 buf
= g_strdup_printf(_("Registration of %s@%s successful"),
1185 js
->user
->node
, js
->user
->domain
);
1186 purple_account_register_completed(account
, TRUE
);
1188 g_return_if_fail(to
!= NULL
);
1189 buf
= g_strdup_printf(_("Registration to %s successful"),
1192 purple_notify_info(NULL
, _("Registration Successful"),
1193 _("Registration Successful"), buf
,
1194 purple_request_cpar_from_connection(js
->gc
));
1197 char *msg
= jabber_parse_error(js
, packet
, NULL
);
1200 msg
= g_strdup(_("Unknown Error"));
1202 purple_notify_error(NULL
, _("Registration Failed"),
1203 _("Registration Failed"), msg
,
1204 purple_request_cpar_from_connection(js
->gc
));
1206 purple_account_register_completed(account
, FALSE
);
1209 if(js
->registration
)
1210 jabber_connection_schedule_close(js
);
1214 jabber_unregistration_result_cb(JabberStream
*js
, const char *from
,
1215 JabberIqType type
, const char *id
,
1216 PurpleXmlNode
*packet
, gpointer data
)
1221 /* This function is never called for unregistering our XMPP account from
1222 * the server, so there should always be a 'to' address. */
1223 g_return_if_fail(to
!= NULL
);
1225 if (type
== JABBER_IQ_RESULT
) {
1226 buf
= g_strdup_printf(_("Registration from %s successfully removed"),
1228 purple_notify_info(NULL
, _("Unregistration Successful"),
1229 _("Unregistration Successful"), buf
,
1230 purple_request_cpar_from_connection(js
->gc
));
1233 char *msg
= jabber_parse_error(js
, packet
, NULL
);
1236 msg
= g_strdup(_("Unknown Error"));
1238 purple_notify_error(NULL
, _("Unregistration Failed"),
1239 _("Unregistration Failed"), msg
,
1240 purple_request_cpar_from_connection(js
->gc
));
1249 } JabberRegisterCBData
;
1252 jabber_register_cb(JabberRegisterCBData
*cbdata
, PurpleRequestFields
*fields
)
1254 GList
*groups
, *flds
;
1255 PurpleXmlNode
*query
, *y
;
1259 iq
= jabber_iq_new_query(cbdata
->js
, JABBER_IQ_SET
, "jabber:iq:register");
1260 query
= purple_xmlnode_get_child(iq
->node
, "query");
1262 purple_xmlnode_set_attrib(iq
->node
, "to", cbdata
->who
);
1264 for(groups
= purple_request_fields_get_groups(fields
); groups
;
1265 groups
= groups
->next
) {
1266 for(flds
= purple_request_field_group_get_fields(groups
->data
);
1267 flds
; flds
= flds
->next
) {
1268 PurpleRequestField
*field
= flds
->data
;
1269 const char *id
= purple_request_field_get_id(field
);
1270 if(purple_strequal(id
,"unregister")) {
1271 gboolean value
= purple_request_field_bool_get_value(field
);
1273 /* unregister from service. this doesn't include any of the fields, so remove them from the stanza by recreating it
1274 (there's no "remove child" function for PurpleXmlNode) */
1276 iq
= jabber_iq_new_query(cbdata
->js
, JABBER_IQ_SET
, "jabber:iq:register");
1277 query
= purple_xmlnode_get_child(iq
->node
, "query");
1279 purple_xmlnode_set_attrib(iq
->node
,"to",cbdata
->who
);
1280 purple_xmlnode_new_child(query
, "remove");
1282 jabber_iq_set_callback(iq
, jabber_unregistration_result_cb
, cbdata
->who
);
1289 const char *ids
[] = {"username", "password", "name", "email", "nick", "first",
1290 "last", "address", "city", "state", "zip", "phone", "url", "date",
1292 const char *value
= purple_request_field_string_get_value(field
);
1294 for (i
= 0; ids
[i
]; i
++) {
1295 if (purple_strequal(id
, ids
[i
]))
1301 y
= purple_xmlnode_new_child(query
, ids
[i
]);
1302 purple_xmlnode_insert_data(y
, value
, -1);
1303 if(cbdata
->js
->registration
&& purple_strequal(id
, "username")) {
1304 g_free(cbdata
->js
->user
->node
);
1305 cbdata
->js
->user
->node
= g_strdup(value
);
1307 if(cbdata
->js
->registration
&& purple_strequal(id
, "password"))
1308 purple_account_set_password(purple_connection_get_account(cbdata
->js
->gc
), value
, NULL
, NULL
);
1313 if(cbdata
->js
->registration
) {
1314 username
= g_strdup_printf("%s@%s%s%s", cbdata
->js
->user
->node
, cbdata
->js
->user
->domain
,
1315 cbdata
->js
->user
->resource
? "/" : "",
1316 cbdata
->js
->user
->resource
? cbdata
->js
->user
->resource
: "");
1317 purple_account_set_username(purple_connection_get_account(cbdata
->js
->gc
), username
);
1321 jabber_iq_set_callback(iq
, jabber_registration_result_cb
, cbdata
->who
);
1328 jabber_register_cancel_cb(JabberRegisterCBData
*cbdata
, PurpleRequestFields
*fields
)
1330 PurpleAccount
*account
= purple_connection_get_account(cbdata
->js
->gc
);
1331 if(account
&& cbdata
->js
->registration
) {
1332 purple_account_register_completed(account
, FALSE
);
1333 jabber_connection_schedule_close(cbdata
->js
);
1335 g_free(cbdata
->who
);
1339 static void jabber_register_x_data_cb(JabberStream
*js
, PurpleXmlNode
*result
, gpointer data
)
1341 PurpleXmlNode
*query
;
1345 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
1346 query
= purple_xmlnode_get_child(iq
->node
, "query");
1348 purple_xmlnode_set_attrib(iq
->node
,"to",to
);
1350 purple_xmlnode_insert_child(query
, result
);
1352 jabber_iq_set_callback(iq
, jabber_registration_result_cb
, to
);
1356 static const struct {
1359 } registration_fields
[] = {
1360 { "email", N_("Email") },
1361 { "nick", N_("Nickname") },
1362 { "first", N_("First name") },
1363 { "last", N_("Last name") },
1364 { "address", N_("Address") },
1365 { "city", N_("City") },
1366 { "state", N_("State") },
1367 { "zip", N_("Postal code") },
1368 { "phone", N_("Phone") },
1369 { "url", N_("URL") },
1370 { "date", N_("Date") },
1374 void jabber_register_parse(JabberStream
*js
, const char *from
, JabberIqType type
,
1375 const char *id
, PurpleXmlNode
*query
)
1377 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1378 PurpleRequestFields
*fields
;
1379 PurpleRequestFieldGroup
*group
;
1380 PurpleRequestField
*field
;
1381 PurpleXmlNode
*x
, *y
, *node
;
1383 JabberRegisterCBData
*cbdata
;
1384 gboolean registered
= FALSE
;
1387 if (type
!= JABBER_IQ_RESULT
)
1390 if(js
->registration
) {
1391 /* get rid of the login thingy */
1392 purple_connection_set_state(js
->gc
, PURPLE_CONNECTION_CONNECTED
);
1395 if(purple_xmlnode_get_child(query
, "registered")) {
1398 if(js
->registration
) {
1399 purple_notify_error(NULL
, _("Already Registered"),
1400 _("Already Registered"), NULL
,
1401 purple_request_cpar_from_connection(js
->gc
));
1402 purple_account_register_completed(account
, FALSE
);
1403 jabber_connection_schedule_close(js
);
1408 if((x
= purple_xmlnode_get_child_with_namespace(query
, "x", "jabber:x:data"))) {
1409 jabber_x_data_request(js
, x
, jabber_register_x_data_cb
, g_strdup(from
));
1412 } else if((x
= purple_xmlnode_get_child_with_namespace(query
, "x", NS_OOB_X_DATA
))) {
1415 if((url
= purple_xmlnode_get_child(x
, "url"))) {
1417 if((href
= purple_xmlnode_get_data(url
))) {
1418 purple_notify_uri(NULL
, href
);
1421 if(js
->registration
) {
1422 /* succeeded, but we have no login info */
1423 purple_account_register_completed(account
, TRUE
);
1424 purple_connection_error(js
->gc
, PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
1425 _("Registration completed successfully. Please reconnect to continue."));
1426 jabber_connection_schedule_close(js
);
1433 /* as a last resort, use the old jabber:iq:register syntax */
1435 fields
= purple_request_fields_new();
1436 group
= purple_request_field_group_new(NULL
);
1437 purple_request_fields_add_group(fields
, group
);
1439 if((node
= purple_xmlnode_get_child(query
, "username"))) {
1440 char *data
= purple_xmlnode_get_data(node
);
1441 if(js
->registration
)
1442 field
= purple_request_field_string_new("username", _("Username"), data
? data
: js
->user
->node
, FALSE
);
1444 field
= purple_request_field_string_new("username", _("Username"), data
, FALSE
);
1446 purple_request_field_group_add_field(group
, field
);
1449 if((node
= purple_xmlnode_get_child(query
, "password"))) {
1450 if(js
->registration
)
1451 field
= purple_request_field_string_new("password", _("Password"),
1452 purple_connection_get_password(js
->gc
), FALSE
);
1454 char *data
= purple_xmlnode_get_data(node
);
1455 field
= purple_request_field_string_new("password", _("Password"), data
, FALSE
);
1459 purple_request_field_string_set_masked(field
, TRUE
);
1460 purple_request_field_group_add_field(group
, field
);
1463 if((node
= purple_xmlnode_get_child(query
, "name"))) {
1464 if(js
->registration
)
1465 field
= purple_request_field_string_new("name", _("Name"),
1466 purple_account_get_private_alias(purple_connection_get_account(js
->gc
)), FALSE
);
1468 char *data
= purple_xmlnode_get_data(node
);
1469 field
= purple_request_field_string_new("name", _("Name"), data
, FALSE
);
1472 purple_request_field_group_add_field(group
, field
);
1475 for (i
= 0; registration_fields
[i
].name
!= NULL
; ++i
) {
1476 if ((node
= purple_xmlnode_get_child(query
, registration_fields
[i
].name
))) {
1477 char *data
= purple_xmlnode_get_data(node
);
1478 field
= purple_request_field_string_new(registration_fields
[i
].name
,
1479 _(registration_fields
[i
].label
),
1481 purple_request_field_group_add_field(group
, field
);
1487 field
= purple_request_field_bool_new("unregister", _("Unregister"), FALSE
);
1488 purple_request_field_group_add_field(group
, field
);
1491 if((y
= purple_xmlnode_get_child(query
, "instructions")))
1492 instructions
= purple_xmlnode_get_data(y
);
1494 instructions
= g_strdup(_("Please fill out the information below "
1495 "to change your account registration."));
1497 instructions
= g_strdup(_("Please fill out the information below "
1498 "to register your new account."));
1500 cbdata
= g_new0(JabberRegisterCBData
, 1);
1502 cbdata
->who
= g_strdup(from
);
1504 if(js
->registration
)
1505 purple_request_fields(js
->gc
, _("Register New XMPP Account"),
1506 _("Register New XMPP Account"), instructions
, fields
,
1507 _("Register"), G_CALLBACK(jabber_register_cb
),
1508 _("Cancel"), G_CALLBACK(jabber_register_cancel_cb
),
1509 purple_request_cpar_from_connection(js
->gc
),
1513 g_return_if_fail(from
!= NULL
);
1514 title
= registered
? g_strdup_printf(_("Change Account Registration at %s"), from
)
1515 :g_strdup_printf(_("Register New Account at %s"), from
);
1516 purple_request_fields(js
->gc
, title
, title
, instructions
,
1517 fields
, (registered
? _("Change Registration") :
1518 _("Register")), G_CALLBACK(jabber_register_cb
),
1519 _("Cancel"), G_CALLBACK(jabber_register_cancel_cb
),
1520 purple_request_cpar_from_connection(js
->gc
), cbdata
);
1524 g_free(instructions
);
1527 void jabber_register_start(JabberStream
*js
)
1531 iq
= jabber_iq_new_query(js
, JABBER_IQ_GET
, "jabber:iq:register");
1535 void jabber_register_gateway(JabberStream
*js
, const char *gateway
) {
1538 iq
= jabber_iq_new_query(js
, JABBER_IQ_GET
, "jabber:iq:register");
1539 purple_xmlnode_set_attrib(iq
->node
, "to", gateway
);
1543 void jabber_register_account(PurpleAccount
*account
)
1547 js
= jabber_stream_new(account
);
1551 js
->registration
= TRUE
;
1552 jabber_stream_connect(js
);
1556 jabber_unregister_account_iq_cb(JabberStream
*js
, const char *from
,
1557 JabberIqType type
, const char *id
,
1558 PurpleXmlNode
*packet
, gpointer data
)
1560 PurpleAccount
*account
= purple_connection_get_account(js
->gc
);
1562 if (type
== JABBER_IQ_ERROR
) {
1563 char *msg
= jabber_parse_error(js
, packet
, NULL
);
1565 purple_notify_error(js
->gc
, _("Error unregistering account"),
1566 _("Error unregistering account"), msg
,
1567 purple_request_cpar_from_connection(js
->gc
));
1569 if(js
->unregistration_cb
)
1570 js
->unregistration_cb(account
, FALSE
, js
->unregistration_user_data
);
1572 purple_notify_info(js
->gc
, _("Account successfully "
1573 "unregistered"), _("Account successfully unregistered"),
1574 NULL
, purple_request_cpar_from_connection(js
->gc
));
1575 if(js
->unregistration_cb
)
1576 js
->unregistration_cb(account
, TRUE
, js
->unregistration_user_data
);
1580 static void jabber_unregister_account_cb(JabberStream
*js
) {
1582 PurpleXmlNode
*query
;
1584 g_return_if_fail(js
->unregistration
);
1586 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
1588 query
= purple_xmlnode_get_child_with_namespace(iq
->node
, "query", "jabber:iq:register");
1590 purple_xmlnode_new_child(query
, "remove");
1591 purple_xmlnode_set_attrib(iq
->node
, "to", js
->user
->domain
);
1593 jabber_iq_set_callback(iq
, jabber_unregister_account_iq_cb
, NULL
);
1597 void jabber_unregister_account(PurpleAccount
*account
, PurpleAccountUnregistrationCb cb
, void *user_data
) {
1598 PurpleConnection
*gc
= purple_account_get_connection(account
);
1601 if (purple_connection_get_state(gc
) != PURPLE_CONNECTION_CONNECTED
) {
1602 if (purple_connection_get_state(gc
) != PURPLE_CONNECTION_CONNECTING
)
1603 jabber_login(account
);
1604 js
= purple_connection_get_protocol_data(gc
);
1605 js
->unregistration
= TRUE
;
1606 js
->unregistration_cb
= cb
;
1607 js
->unregistration_user_data
= user_data
;
1611 js
= purple_connection_get_protocol_data(gc
);
1613 if (js
->unregistration
) {
1614 purple_debug_error("jabber", "Unregistration in process; ignoring duplicate request.\n");
1618 js
->unregistration
= TRUE
;
1619 js
->unregistration_cb
= cb
;
1620 js
->unregistration_user_data
= user_data
;
1622 jabber_unregister_account_cb(js
);
1625 /* TODO: As Will pointed out in IRC, after being notified by the core to
1626 * shutdown, we should async. wait for the server to send us the stream
1627 * termination before destorying everything. That seems like it would require
1628 * changing the semantics of protocol's close(), so it's a good idea for 3.0.0.
1630 void jabber_close(PurpleConnection
*gc
)
1632 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
1634 /* Close all of the open Jingle sessions on this stream */
1635 jingle_terminate_sessions(js
);
1638 jabber_bosh_connection_destroy(js
->bosh
);
1640 } else if ((js
->gsc
&& js
->gsc
->fd
> 0) || js
->fd
> 0)
1641 jabber_send_raw(js
, "</stream:stream>", -1);
1644 purple_ssl_close(js
->gsc
);
1645 } else if (js
->fd
> 0) {
1647 purple_input_remove(js
->inpa
);
1653 jabber_buddy_remove_all_pending_buddy_info_requests(js
);
1655 jabber_parser_free(js
);
1657 if(js
->iq_callbacks
)
1658 g_hash_table_destroy(js
->iq_callbacks
);
1660 g_hash_table_destroy(js
->buddies
);
1662 g_hash_table_destroy(js
->chats
);
1664 while(js
->chat_servers
) {
1665 g_free(js
->chat_servers
->data
);
1666 js
->chat_servers
= g_list_delete_link(js
->chat_servers
, js
->chat_servers
);
1669 while(js
->user_directories
) {
1670 g_free(js
->user_directories
->data
);
1671 js
->user_directories
= g_list_delete_link(js
->user_directories
, js
->user_directories
);
1674 while(js
->bs_proxies
) {
1675 JabberBytestreamsStreamhost
*sh
= js
->bs_proxies
->data
;
1678 g_free(sh
->zeroconf
);
1680 js
->bs_proxies
= g_list_delete_link(js
->bs_proxies
, js
->bs_proxies
);
1683 purple_http_connection_set_destroy(js
->http_conns
);
1685 g_free(js
->stream_id
);
1687 jabber_id_free(js
->user
);
1688 g_free(js
->initial_avatar_hash
);
1689 g_free(js
->avatar_hash
);
1690 g_free(js
->caps_hash
);
1692 if (js
->write_buffer
)
1693 g_object_unref(G_OBJECT(js
->write_buffer
));
1695 purple_input_remove(js
->writeh
);
1696 if (js
->auth_mech
&& js
->auth_mech
->dispose
)
1697 js
->auth_mech
->dispose(js
);
1698 #ifdef HAVE_CYRUS_SASL
1700 sasl_dispose(&js
->sasl
);
1702 g_string_free(js
->sasl_mechs
, TRUE
);
1703 g_free(js
->sasl_cb
);
1704 /* Note: _not_ g_free. See auth_cyrus.c:jabber_sasl_cb_secret */
1705 free(js
->sasl_secret
);
1706 g_free(js
->sasl_password
);
1708 g_free(js
->serverFQDN
);
1709 while(js
->commands
) {
1710 JabberAdHocCommands
*cmd
= js
->commands
->data
;
1715 js
->commands
= g_list_delete_link(js
->commands
, js
->commands
);
1717 g_free(js
->server_name
);
1718 g_free(js
->certificate_CN
);
1719 g_free(js
->gmail_last_time
);
1720 g_free(js
->gmail_last_tid
);
1721 g_free(js
->old_msg
);
1722 g_free(js
->old_avatarhash
);
1723 g_free(js
->old_artist
);
1724 g_free(js
->old_title
);
1725 g_free(js
->old_source
);
1726 g_free(js
->old_uri
);
1727 g_free(js
->old_track
);
1729 if (js
->vcard_timer
!= 0)
1730 g_source_remove(js
->vcard_timer
);
1732 if (js
->keepalive_timeout
!= 0)
1733 g_source_remove(js
->keepalive_timeout
);
1734 if (js
->inactivity_timer
!= 0)
1735 g_source_remove(js
->inactivity_timer
);
1736 if (js
->conn_close_timeout
!= 0)
1737 g_source_remove(js
->conn_close_timeout
);
1739 g_cancellable_cancel(js
->cancellable
);
1740 g_object_unref(G_OBJECT(js
->cancellable
));
1742 g_free(js
->stun_ip
);
1744 /* remove Google relay-related stuff */
1745 g_free(js
->google_relay_token
);
1746 g_free(js
->google_relay_host
);
1750 purple_connection_set_protocol_data(gc
, NULL
);
1753 void jabber_stream_set_state(JabberStream
*js
, JabberStreamState state
)
1755 #define JABBER_CONNECT_STEPS ((js->gsc || js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION) ? 9 : 5)
1759 case JABBER_STREAM_OFFLINE
:
1761 case JABBER_STREAM_CONNECTING
:
1762 purple_connection_update_progress(js
->gc
, _("Connecting"), 1,
1763 JABBER_CONNECT_STEPS
);
1765 case JABBER_STREAM_INITIALIZING
:
1766 purple_connection_update_progress(js
->gc
, _("Initializing Stream"),
1767 js
->gsc
? 5 : 2, JABBER_CONNECT_STEPS
);
1768 jabber_stream_init(js
);
1770 case JABBER_STREAM_INITIALIZING_ENCRYPTION
:
1771 purple_connection_update_progress(js
->gc
, _("Initializing SSL/TLS"),
1772 6, JABBER_CONNECT_STEPS
);
1774 case JABBER_STREAM_AUTHENTICATING
:
1775 purple_connection_update_progress(js
->gc
, _("Authenticating"),
1776 js
->gsc
? 7 : 3, JABBER_CONNECT_STEPS
);
1778 case JABBER_STREAM_POST_AUTH
:
1779 purple_connection_update_progress(js
->gc
, _("Re-initializing Stream"),
1780 (js
->gsc
? 8 : 4), JABBER_CONNECT_STEPS
);
1783 case JABBER_STREAM_CONNECTED
:
1784 /* Send initial presence */
1785 jabber_presence_send(js
, TRUE
);
1786 /* Start up the inactivity timer */
1787 jabber_stream_restart_inactivity_timer(js
);
1789 purple_connection_set_state(js
->gc
, PURPLE_CONNECTION_CONNECTED
);
1793 #undef JABBER_CONNECT_STEPS
1796 char *jabber_get_next_id(JabberStream
*js
)
1798 return g_strdup_printf("purple%x", js
->next_id
++);
1802 void jabber_idle_set(PurpleConnection
*gc
, int idle
)
1804 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
1806 js
->idle
= idle
? time(NULL
) - idle
: idle
;
1808 /* send out an updated prescence */
1809 purple_debug_info("jabber", "sending updated presence for idle\n");
1810 jabber_presence_send(js
, FALSE
);
1813 void jabber_blocklist_parse_push(JabberStream
*js
, const char *from
,
1814 JabberIqType type
, const char *id
,
1815 PurpleXmlNode
*child
)
1818 PurpleXmlNode
*item
;
1819 PurpleAccount
*account
;
1823 if (!jabber_is_own_account(js
, from
)) {
1824 PurpleXmlNode
*error
, *x
;
1825 result
= jabber_iq_new(js
, JABBER_IQ_ERROR
);
1826 purple_xmlnode_set_attrib(result
->node
, "id", id
);
1828 purple_xmlnode_set_attrib(result
->node
, "to", from
);
1830 error
= purple_xmlnode_new_child(result
->node
, "error");
1831 purple_xmlnode_set_attrib(error
, "type", "cancel");
1832 x
= purple_xmlnode_new_child(error
, "not-allowed");
1833 purple_xmlnode_set_namespace(x
, NS_XMPP_STANZAS
);
1835 jabber_iq_send(result
);
1839 account
= purple_connection_get_account(js
->gc
);
1840 is_block
= purple_strequal(child
->name
, "block");
1842 item
= purple_xmlnode_get_child(child
, "item");
1843 if (!is_block
&& item
== NULL
) {
1844 /* Unblock everyone */
1845 purple_debug_info("jabber", "Received unblock push. Unblocking everyone.\n");
1847 while ((deny
= purple_account_privacy_get_denied(account
)) != NULL
) {
1848 purple_account_privacy_deny_remove(account
, deny
->data
, TRUE
);
1850 } else if (item
== NULL
) {
1851 /* An empty <block/> is bogus */
1852 PurpleXmlNode
*error
, *x
;
1853 result
= jabber_iq_new(js
, JABBER_IQ_ERROR
);
1854 purple_xmlnode_set_attrib(result
->node
, "id", id
);
1856 error
= purple_xmlnode_new_child(result
->node
, "error");
1857 purple_xmlnode_set_attrib(error
, "type", "modify");
1858 x
= purple_xmlnode_new_child(error
, "bad-request");
1859 purple_xmlnode_set_namespace(x
, NS_XMPP_STANZAS
);
1861 jabber_iq_send(result
);
1864 for ( ; item
; item
= purple_xmlnode_get_next_twin(item
)) {
1865 const char *jid
= purple_xmlnode_get_attrib(item
, "jid");
1866 if (jid
== NULL
|| *jid
== '\0')
1870 purple_account_privacy_deny_add(account
, jid
, TRUE
);
1872 purple_account_privacy_deny_remove(account
, jid
, TRUE
);
1876 result
= jabber_iq_new(js
, JABBER_IQ_RESULT
);
1877 purple_xmlnode_set_attrib(result
->node
, "id", id
);
1878 jabber_iq_send(result
);
1881 static void jabber_blocklist_parse(JabberStream
*js
, const char *from
,
1882 JabberIqType type
, const char *id
,
1883 PurpleXmlNode
*packet
, gpointer data
)
1885 PurpleXmlNode
*blocklist
, *item
;
1886 PurpleAccount
*account
;
1889 blocklist
= purple_xmlnode_get_child_with_namespace(packet
,
1890 "blocklist", NS_SIMPLE_BLOCKING
);
1891 account
= purple_connection_get_account(js
->gc
);
1893 if (type
== JABBER_IQ_ERROR
|| blocklist
== NULL
)
1896 /* This is the only privacy method supported by XEP-0191 */
1897 purple_account_set_privacy_type(account
, PURPLE_ACCOUNT_PRIVACY_DENY_USERS
);
1900 * TODO: When account->deny is something more than a hash table, this can
1901 * be re-written to find the set intersection and difference.
1903 while ((deny
= purple_account_privacy_get_denied(account
)))
1904 purple_account_privacy_deny_remove(account
, deny
->data
, TRUE
);
1906 item
= purple_xmlnode_get_child(blocklist
, "item");
1907 while (item
!= NULL
) {
1908 const char *jid
= purple_xmlnode_get_attrib(item
, "jid");
1909 purple_account_privacy_deny_add(account
, jid
, TRUE
);
1910 item
= purple_xmlnode_get_next_twin(item
);
1914 void jabber_request_block_list(JabberStream
*js
)
1917 PurpleXmlNode
*blocklist
;
1919 iq
= jabber_iq_new(js
, JABBER_IQ_GET
);
1921 blocklist
= purple_xmlnode_new_child(iq
->node
, "blocklist");
1922 purple_xmlnode_set_namespace(blocklist
, NS_SIMPLE_BLOCKING
);
1924 jabber_iq_set_callback(iq
, jabber_blocklist_parse
, NULL
);
1929 void jabber_add_deny(PurpleConnection
*gc
, const char *who
)
1933 PurpleXmlNode
*block
, *item
;
1935 g_return_if_fail(who
!= NULL
&& *who
!= '\0');
1937 js
= purple_connection_get_protocol_data(gc
);
1941 if (js
->server_caps
& JABBER_CAP_GOOGLE_ROSTER
)
1943 jabber_google_roster_add_deny(js
, who
);
1947 if (!(js
->server_caps
& JABBER_CAP_BLOCKING
))
1949 purple_notify_error(NULL
, _("Server doesn't support blocking"),
1950 _("Server doesn't support blocking"), NULL
,
1951 purple_request_cpar_from_connection(gc
));
1955 iq
= jabber_iq_new(js
, JABBER_IQ_SET
);
1957 block
= purple_xmlnode_new_child(iq
->node
, "block");
1958 purple_xmlnode_set_namespace(block
, NS_SIMPLE_BLOCKING
);
1960 item
= purple_xmlnode_new_child(block
, "item");
1961 purple_xmlnode_set_attrib(item
, "jid", who
);
1966 void jabber_rem_deny(PurpleConnection
*gc
, const char *who
)
1970 PurpleXmlNode
*unblock
, *item
;
1972 g_return_if_fail(who
!= NULL
&& *who
!= '\0');
1974 js
= purple_connection_get_protocol_data(gc
);
1978 if (js
->server_caps
& JABBER_CAP_GOOGLE_ROSTER
)
1980 jabber_google_roster_rem_deny(js
, who
);
1984 if (!(js
->server_caps
& JABBER_CAP_BLOCKING
))
1987 iq
= jabber_iq_new(js
, JABBER_IQ_SET
);
1989 unblock
= purple_xmlnode_new_child(iq
->node
, "unblock");
1990 purple_xmlnode_set_namespace(unblock
, NS_SIMPLE_BLOCKING
);
1992 item
= purple_xmlnode_new_child(unblock
, "item");
1993 purple_xmlnode_set_attrib(item
, "jid", who
);
1998 void jabber_add_feature(const char *namespace, JabberFeatureEnabled cb
) {
1999 JabberFeature
*feat
;
2001 g_return_if_fail(namespace != NULL
);
2003 feat
= g_new0(JabberFeature
,1);
2004 feat
->namespace = g_strdup(namespace);
2005 feat
->is_enabled
= cb
;
2007 /* try to remove just in case it already exists in the list */
2008 jabber_remove_feature(namespace);
2010 jabber_features
= g_list_append(jabber_features
, feat
);
2013 void jabber_remove_feature(const char *namespace) {
2015 for(feature
= jabber_features
; feature
; feature
= feature
->next
) {
2016 JabberFeature
*feat
= (JabberFeature
*)feature
->data
;
2017 if(purple_strequal(feat
->namespace, namespace)) {
2018 g_free(feat
->namespace);
2019 g_free(feature
->data
);
2020 jabber_features
= g_list_delete_link(jabber_features
, feature
);
2026 static void jabber_features_destroy(void)
2028 while (jabber_features
) {
2029 JabberFeature
*feature
= jabber_features
->data
;
2030 g_free(feature
->namespace);
2032 jabber_features
= g_list_delete_link(jabber_features
, jabber_features
);
2037 jabber_identity_compare(gconstpointer a
, gconstpointer b
)
2039 const JabberIdentity
*ac
;
2040 const JabberIdentity
*bc
;
2047 cat_cmp
= g_strcmp0(ac
->category
, bc
->category
);
2052 typ_cmp
= g_strcmp0(ac
->type
, bc
->type
);
2057 return g_strcmp0(ac
->lang
, bc
->lang
);
2060 void jabber_add_identity(const gchar
*category
, const gchar
*type
,
2061 const gchar
*lang
, const gchar
*name
)
2064 JabberIdentity
*ident
;
2066 /* both required according to XEP-0030 */
2067 g_return_if_fail(category
!= NULL
);
2068 g_return_if_fail(type
!= NULL
);
2070 /* Check if this identity is already there... */
2071 for (identity
= jabber_identities
; identity
; identity
= identity
->next
) {
2072 JabberIdentity
*id
= identity
->data
;
2073 if (purple_strequal(id
->category
, category
) &&
2074 purple_strequal(id
->type
, type
) &&
2075 purple_strequal(id
->lang
, lang
))
2079 ident
= g_new0(JabberIdentity
, 1);
2080 ident
->category
= g_strdup(category
);
2081 ident
->type
= g_strdup(type
);
2082 ident
->lang
= g_strdup(lang
);
2083 ident
->name
= g_strdup(name
);
2084 jabber_identities
= g_list_insert_sorted(jabber_identities
, ident
,
2085 jabber_identity_compare
);
2088 static void jabber_identities_destroy(void)
2090 while (jabber_identities
) {
2091 JabberIdentity
*id
= jabber_identities
->data
;
2092 g_free(id
->category
);
2097 jabber_identities
= g_list_delete_link(jabber_identities
, jabber_identities
);
2101 gboolean
jabber_stream_is_ssl(JabberStream
*js
)
2103 return (js
->bosh
&& jabber_bosh_connection_is_ssl(js
->bosh
)) ||
2104 (!js
->bosh
&& js
->gsc
);
2108 inactivity_cb(gpointer data
)
2110 JabberStream
*js
= data
;
2112 /* We want whatever is sent to set this. It's okay because
2113 * the eventloop unsets it via the return FALSE.
2115 js
->inactivity_timer
= 0;
2118 jabber_bosh_connection_send_keepalive(js
->bosh
);
2120 jabber_send_raw(js
, "\t", 1);
2125 void jabber_stream_restart_inactivity_timer(JabberStream
*js
)
2127 if (js
->inactivity_timer
!= 0) {
2128 g_source_remove(js
->inactivity_timer
);
2129 js
->inactivity_timer
= 0;
2132 g_return_if_fail(js
->max_inactivity
> 0);
2134 js
->inactivity_timer
=
2135 g_timeout_add_seconds(js
->max_inactivity
,
2139 const char *jabber_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
2144 const char* jabber_list_emblem(PurpleBuddy
*b
)
2147 JabberBuddy
*jb
= NULL
;
2148 PurpleConnection
*gc
= purple_account_get_connection(purple_buddy_get_account(b
));
2153 js
= purple_connection_get_protocol_data(gc
);
2155 jb
= jabber_buddy_find(js
, purple_buddy_get_name(b
), FALSE
);
2157 if(!PURPLE_BUDDY_IS_ONLINE(b
)) {
2158 if(jb
&& (jb
->subscription
& JABBER_SUB_PENDING
||
2159 !(jb
->subscription
& JABBER_SUB_TO
)))
2160 return "not-authorized";
2164 JabberBuddyResource
*jbr
= jabber_buddy_find_resource(jb
, NULL
);
2166 const gchar
*client_type
=
2167 jabber_resource_get_identity_category_type(jbr
, "client");
2170 if (purple_strequal(client_type
, "phone")) {
2172 } else if (purple_strequal(client_type
, "web")) {
2174 } else if (purple_strequal(client_type
, "handheld")) {
2176 } else if (purple_strequal(client_type
, "bot")) {
2179 /* the default value "pc" falls through and has no emblem */
2187 char *jabber_status_text(PurpleBuddy
*b
)
2190 JabberBuddy
*jb
= NULL
;
2191 PurpleAccount
*account
= purple_buddy_get_account(b
);
2192 PurpleConnection
*gc
= purple_account_get_connection(account
);
2194 if (gc
&& purple_connection_get_protocol_data(gc
))
2195 jb
= jabber_buddy_find(purple_connection_get_protocol_data(gc
), purple_buddy_get_name(b
), FALSE
);
2197 if(jb
&& !PURPLE_BUDDY_IS_ONLINE(b
) && (jb
->subscription
& JABBER_SUB_PENDING
|| !(jb
->subscription
& JABBER_SUB_TO
))) {
2198 ret
= g_strdup(_("Not Authorized"));
2199 } else if(jb
&& !PURPLE_BUDDY_IS_ONLINE(b
) && jb
->error_msg
) {
2200 ret
= g_strdup(jb
->error_msg
);
2202 PurplePresence
*presence
= purple_buddy_get_presence(b
);
2203 PurpleStatus
*status
= purple_presence_get_active_status(presence
);
2204 const char *message
;
2206 if((message
= purple_status_get_attr_string(status
, "message"))) {
2207 ret
= g_markup_escape_text(message
, -1);
2208 } else if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_TUNE
)) {
2209 PurpleStatus
*status
= purple_presence_get_status(presence
, "tune");
2210 const char *title
= purple_status_get_attr_string(status
, PURPLE_TUNE_TITLE
);
2211 const char *artist
= purple_status_get_attr_string(status
, PURPLE_TUNE_ARTIST
);
2212 const char *album
= purple_status_get_attr_string(status
, PURPLE_TUNE_ALBUM
);
2213 ret
= purple_util_format_song_info(title
, artist
, album
, NULL
);
2221 jabber_tooltip_add_resource_text(JabberBuddyResource
*jbr
,
2222 PurpleNotifyUserInfo
*user_info
, gboolean multiple_resources
)
2226 char *label
, *value
;
2230 text
= g_markup_escape_text(jbr
->status
, -1);
2234 res
= g_strdup_printf(" (%s)", jbr
->name
);
2236 state
= jabber_buddy_state_get_name(jbr
->state
);
2237 if (text
!= NULL
&& !purple_utf8_strcasecmp(state
, text
)) {
2242 label
= g_strdup_printf("%s%s", _("Status"), (res
? res
: ""));
2243 value
= g_strdup_printf("%s%s%s", state
, (text
? ": " : ""), (text
? text
: ""));
2245 purple_notify_user_info_add_pair_html(user_info
, label
, value
);
2250 /* if the resource is idle, show that */
2251 /* only show it if there is more than one resource available for
2252 the buddy, since the "general" idleness will be shown anyway,
2253 this way we can see see the idleness of lower-priority resources */
2254 if (jbr
->idle
&& multiple_resources
) {
2256 purple_str_seconds_to_string(time(NULL
) - jbr
->idle
);
2257 label
= g_strdup_printf("%s%s", _("Idle"), (res
? res
: ""));
2258 purple_notify_user_info_add_pair_plaintext(user_info
, label
, idle_str
);
2265 void jabber_tooltip_text(PurpleBuddy
*b
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
2268 PurpleAccount
*account
;
2269 PurpleConnection
*gc
;
2272 g_return_if_fail(b
!= NULL
);
2274 account
= purple_buddy_get_account(b
);
2275 g_return_if_fail(account
!= NULL
);
2277 gc
= purple_account_get_connection(account
);
2278 g_return_if_fail(gc
!= NULL
);
2280 js
= purple_connection_get_protocol_data(gc
);
2281 g_return_if_fail(js
!= NULL
);
2283 jb
= jabber_buddy_find(js
, purple_buddy_get_name(b
), FALSE
);
2286 JabberBuddyResource
*jbr
= NULL
;
2287 PurplePresence
*presence
= purple_buddy_get_presence(b
);
2291 gboolean multiple_resources
=
2292 jb
->resources
&& g_list_next(jb
->resources
);
2293 JabberBuddyResource
*top_jbr
= jabber_buddy_find_resource(jb
, NULL
);
2295 /* resource-specific info for the top resource */
2297 jabber_tooltip_add_resource_text(top_jbr
, user_info
,
2298 multiple_resources
);
2301 for(l
=jb
->resources
; l
; l
= l
->next
) {
2303 /* the remaining resources */
2304 if (jbr
!= top_jbr
) {
2305 jabber_tooltip_add_resource_text(jbr
, user_info
,
2306 multiple_resources
);
2311 PurpleStatus
*status
;
2313 status
= purple_presence_get_status(presence
, "mood");
2314 mood
= purple_status_get_attr_string(status
, PURPLE_MOOD_NAME
);
2316 const char *moodtext
;
2318 PurpleMood
*moods
= jabber_get_moods(account
);
2319 const char *description
= NULL
;
2321 for (; moods
->mood
; moods
++) {
2322 if (purple_strequal(moods
->mood
, mood
)) {
2323 description
= moods
->description
;
2328 moodtext
= purple_status_get_attr_string(status
, PURPLE_MOOD_COMMENT
);
2329 if(moodtext
&& *moodtext
) {
2330 char *moodplustext
=
2331 g_strdup_printf("%s (%s)", description
? _(description
) : mood
, moodtext
);
2333 purple_notify_user_info_add_pair_html(user_info
, _("Mood"), moodplustext
);
2334 g_free(moodplustext
);
2336 purple_notify_user_info_add_pair_html(user_info
, _("Mood"),
2337 description
? _(description
) : mood
);
2339 if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_TUNE
)) {
2340 PurpleStatus
*tune
= purple_presence_get_status(presence
, "tune");
2341 const char *title
= purple_status_get_attr_string(tune
, PURPLE_TUNE_TITLE
);
2342 const char *artist
= purple_status_get_attr_string(tune
, PURPLE_TUNE_ARTIST
);
2343 const char *album
= purple_status_get_attr_string(tune
, PURPLE_TUNE_ALBUM
);
2344 char *playing
= purple_util_format_song_info(title
, artist
, album
, NULL
);
2346 purple_notify_user_info_add_pair_html(user_info
, _("Now Listening"), playing
);
2351 if(jb
->subscription
& JABBER_SUB_FROM
) {
2352 if(jb
->subscription
& JABBER_SUB_TO
)
2354 else if(jb
->subscription
& JABBER_SUB_PENDING
)
2355 sub
= _("From (To pending)");
2359 if(jb
->subscription
& JABBER_SUB_TO
)
2361 else if(jb
->subscription
& JABBER_SUB_PENDING
)
2362 sub
= _("None (To pending)");
2367 purple_notify_user_info_add_pair_html(user_info
, _("Subscription"), sub
);
2371 if(!PURPLE_BUDDY_IS_ONLINE(b
) && jb
->error_msg
) {
2372 purple_notify_user_info_add_pair_html(user_info
, _("Error"), jb
->error_msg
);
2377 GList
*jabber_status_types(PurpleAccount
*account
)
2379 PurpleStatusType
*type
;
2380 GList
*types
= NULL
;
2381 GValue
*priority_value
;
2382 GValue
*buzz_enabled
;
2384 priority_value
= purple_value_new(G_TYPE_INT
);
2385 g_value_set_int(priority_value
, 1);
2386 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2387 g_value_set_boolean(buzz_enabled
, TRUE
);
2388 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE
,
2389 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE
),
2390 NULL
, TRUE
, TRUE
, FALSE
,
2391 "priority", _("Priority"), priority_value
,
2392 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2393 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2394 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2395 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2396 "buzz", _("Allow Buzz"), buzz_enabled
,
2398 types
= g_list_prepend(types
, type
);
2401 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD
,
2402 "mood", NULL
, TRUE
, TRUE
, TRUE
,
2403 PURPLE_MOOD_NAME
, _("Mood Name"), purple_value_new(G_TYPE_STRING
),
2404 PURPLE_MOOD_COMMENT
, _("Mood Comment"), purple_value_new(G_TYPE_STRING
),
2406 types
= g_list_prepend(types
, type
);
2408 priority_value
= purple_value_new(G_TYPE_INT
);
2409 g_value_set_int(priority_value
, 1);
2410 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2411 g_value_set_boolean(buzz_enabled
, TRUE
);
2412 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE
,
2413 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_CHAT
),
2414 _("Chatty"), TRUE
, TRUE
, FALSE
,
2415 "priority", _("Priority"), priority_value
,
2416 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2417 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2418 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2419 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2420 "buzz", _("Allow Buzz"), buzz_enabled
,
2422 types
= g_list_prepend(types
, type
);
2424 priority_value
= purple_value_new(G_TYPE_INT
);
2425 g_value_set_int(priority_value
, 0);
2426 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2427 g_value_set_boolean(buzz_enabled
, TRUE
);
2428 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY
,
2429 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY
),
2430 NULL
, TRUE
, TRUE
, FALSE
,
2431 "priority", _("Priority"), priority_value
,
2432 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2433 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2434 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2435 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2436 "buzz", _("Allow Buzz"), buzz_enabled
,
2438 types
= g_list_prepend(types
, type
);
2440 priority_value
= purple_value_new(G_TYPE_INT
);
2441 g_value_set_int(priority_value
, 0);
2442 buzz_enabled
= purple_value_new(G_TYPE_BOOLEAN
);
2443 g_value_set_boolean(buzz_enabled
, TRUE
);
2444 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY
,
2445 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_XA
),
2446 NULL
, TRUE
, TRUE
, FALSE
,
2447 "priority", _("Priority"), priority_value
,
2448 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2449 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2450 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2451 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2452 "buzz", _("Allow Buzz"), buzz_enabled
,
2454 types
= g_list_prepend(types
, type
);
2456 priority_value
= purple_value_new(G_TYPE_INT
);
2457 g_value_set_int(priority_value
, 0);
2458 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE
,
2459 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_DND
),
2460 _("Do Not Disturb"), TRUE
, TRUE
, FALSE
,
2461 "priority", _("Priority"), priority_value
,
2462 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2463 "mood", _("Mood"), purple_value_new(G_TYPE_STRING
),
2464 "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING
),
2465 "nick", _("Nickname"), purple_value_new(G_TYPE_STRING
),
2467 types
= g_list_prepend(types
, type
);
2470 if(js->protocol_version == JABBER_PROTO_0_9)
2474 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE
,
2475 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE
),
2476 NULL
, TRUE
, TRUE
, FALSE
,
2477 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2479 types
= g_list_prepend(types
, type
);
2481 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE
,
2482 "tune", NULL
, FALSE
, TRUE
, TRUE
,
2483 PURPLE_TUNE_ARTIST
, _("Tune Artist"), purple_value_new(G_TYPE_STRING
),
2484 PURPLE_TUNE_TITLE
, _("Tune Title"), purple_value_new(G_TYPE_STRING
),
2485 PURPLE_TUNE_ALBUM
, _("Tune Album"), purple_value_new(G_TYPE_STRING
),
2486 PURPLE_TUNE_GENRE
, _("Tune Genre"), purple_value_new(G_TYPE_STRING
),
2487 PURPLE_TUNE_COMMENT
, _("Tune Comment"), purple_value_new(G_TYPE_STRING
),
2488 PURPLE_TUNE_TRACK
, _("Tune Track"), purple_value_new(G_TYPE_STRING
),
2489 PURPLE_TUNE_TIME
, _("Tune Time"), purple_value_new(G_TYPE_INT
),
2490 PURPLE_TUNE_YEAR
, _("Tune Year"), purple_value_new(G_TYPE_INT
),
2491 PURPLE_TUNE_URL
, _("Tune URL"), purple_value_new(G_TYPE_STRING
),
2493 types
= g_list_prepend(types
, type
);
2495 return g_list_reverse(types
);
2499 jabber_password_change_result_cb(JabberStream
*js
, const char *from
,
2500 JabberIqType type
, const char *id
,
2501 PurpleXmlNode
*packet
, gpointer data
)
2503 if (type
== JABBER_IQ_RESULT
) {
2504 purple_notify_info(js
->gc
, _("Password Changed"), _("Password "
2505 "Changed"), _("Your password has been changed."),
2506 purple_request_cpar_from_connection(js
->gc
));
2508 purple_account_set_password(purple_connection_get_account(js
->gc
), (const char *)data
, NULL
, NULL
);
2510 char *msg
= jabber_parse_error(js
, packet
, NULL
);
2512 purple_notify_error(js
->gc
, _("Error changing password"),
2513 _("Error changing password"), msg
,
2514 purple_request_cpar_from_connection(js
->gc
));
2521 static void jabber_password_change_cb(JabberStream
*js
,
2522 PurpleRequestFields
*fields
)
2524 const char *p1
, *p2
;
2526 PurpleXmlNode
*query
, *y
;
2528 p1
= purple_request_fields_get_string(fields
, "password1");
2529 p2
= purple_request_fields_get_string(fields
, "password2");
2531 if(!purple_strequal(p1
, p2
)) {
2532 purple_notify_error(js
->gc
, NULL
,
2533 _("New passwords do not match."), NULL
,
2534 purple_request_cpar_from_connection(js
->gc
));
2538 iq
= jabber_iq_new_query(js
, JABBER_IQ_SET
, "jabber:iq:register");
2540 purple_xmlnode_set_attrib(iq
->node
, "to", js
->user
->domain
);
2542 query
= purple_xmlnode_get_child(iq
->node
, "query");
2544 y
= purple_xmlnode_new_child(query
, "username");
2545 purple_xmlnode_insert_data(y
, js
->user
->node
, -1);
2546 y
= purple_xmlnode_new_child(query
, "password");
2547 purple_xmlnode_insert_data(y
, p1
, -1);
2549 jabber_iq_set_callback(iq
, jabber_password_change_result_cb
, g_strdup(p1
));
2554 static void jabber_password_change(PurpleProtocolAction
*action
)
2557 PurpleConnection
*gc
= (PurpleConnection
*) action
->connection
;
2558 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
2559 PurpleRequestFields
*fields
;
2560 PurpleRequestFieldGroup
*group
;
2561 PurpleRequestField
*field
;
2563 fields
= purple_request_fields_new();
2564 group
= purple_request_field_group_new(NULL
);
2565 purple_request_fields_add_group(fields
, group
);
2567 field
= purple_request_field_string_new("password1", _("Password"),
2569 purple_request_field_string_set_masked(field
, TRUE
);
2570 purple_request_field_set_required(field
, TRUE
);
2571 purple_request_field_group_add_field(group
, field
);
2573 field
= purple_request_field_string_new("password2", _("Password (again)"),
2575 purple_request_field_string_set_masked(field
, TRUE
);
2576 purple_request_field_set_required(field
, TRUE
);
2577 purple_request_field_group_add_field(group
, field
);
2579 purple_request_fields(js
->gc
, _("Change XMPP Password"),
2580 _("Change XMPP Password"), _("Please enter your new password"),
2581 fields
, _("OK"), G_CALLBACK(jabber_password_change_cb
),
2583 purple_request_cpar_from_connection(gc
), js
);
2586 GList
*jabber_get_actions(PurpleConnection
*gc
)
2588 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
2590 PurpleProtocolAction
*act
;
2592 act
= purple_protocol_action_new(_("Set User Info..."),
2593 jabber_setup_set_info
);
2594 m
= g_list_append(m
, act
);
2596 /* if (js->account_options & CHANGE_PASSWORD) { */
2597 act
= purple_protocol_action_new(_("Change Password..."),
2598 jabber_password_change
);
2599 m
= g_list_append(m
, act
);
2602 act
= purple_protocol_action_new(_("Search for Users..."),
2603 jabber_user_search_begin
);
2604 m
= g_list_append(m
, act
);
2606 purple_debug_info("jabber", "jabber_get_actions: have pep: %s\n", js
->pep
?"YES":"NO");
2609 jabber_pep_init_actions(&m
);
2612 jabber_adhoc_init_server_commands(js
, &m
);
2617 PurpleChat
*jabber_find_blist_chat(PurpleAccount
*account
, const char *name
)
2619 PurpleBlistNode
*gnode
, *cnode
;
2622 if(!(jid
= jabber_id_new(name
)))
2625 for (gnode
= purple_blist_get_default_root(); gnode
;
2626 gnode
= purple_blist_node_get_sibling_next(gnode
)) {
2627 for(cnode
= purple_blist_node_get_first_child(gnode
);
2629 cnode
= purple_blist_node_get_sibling_next(cnode
)) {
2630 PurpleChat
*chat
= (PurpleChat
*)cnode
;
2631 const char *room
, *server
;
2632 GHashTable
*components
;
2633 if(!PURPLE_IS_CHAT(cnode
))
2636 if (purple_chat_get_account(chat
) != account
)
2639 components
= purple_chat_get_components(chat
);
2640 if(!(room
= g_hash_table_lookup(components
, "room")))
2642 if(!(server
= g_hash_table_lookup(components
, "server")))
2645 /* FIXME: Collate is wrong in a few cases here; this should be prepped */
2646 if(jid
->node
&& jid
->domain
&&
2647 !g_utf8_collate(room
, jid
->node
) && !g_utf8_collate(server
, jid
->domain
)) {
2648 jabber_id_free(jid
);
2653 jabber_id_free(jid
);
2657 void jabber_convo_closed(PurpleConnection
*gc
, const char *who
)
2659 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
2662 JabberBuddyResource
*jbr
;
2664 if(!(jid
= jabber_id_new(who
)))
2667 if((jb
= jabber_buddy_find(js
, who
, TRUE
)) &&
2668 (jbr
= jabber_buddy_find_resource(jb
, jid
->resource
))) {
2669 g_free(jbr
->thread_id
);
2670 jbr
->thread_id
= NULL
;
2673 jabber_id_free(jid
);
2677 char *jabber_parse_error(JabberStream
*js
,
2678 PurpleXmlNode
*packet
,
2679 PurpleConnectionError
*reason
)
2681 PurpleXmlNode
*error
;
2682 const char *code
= NULL
, *text
= NULL
;
2683 const char *xmlns
= purple_xmlnode_get_namespace(packet
);
2686 #define SET_REASON(x) \
2687 if(reason != NULL) { *reason = x; }
2689 if((error
= purple_xmlnode_get_child(packet
, "error"))) {
2690 PurpleXmlNode
*t
= purple_xmlnode_get_child_with_namespace(error
, "text", NS_XMPP_STANZAS
);
2692 cdata
= purple_xmlnode_get_data(t
);
2694 code
= purple_xmlnode_get_attrib(error
, "code");
2697 if(purple_xmlnode_get_child(error
, "bad-request")) {
2698 text
= _("Bad Request");
2699 } else if(purple_xmlnode_get_child(error
, "conflict")) {
2700 SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE
);
2701 text
= _("Conflict");
2702 } else if(purple_xmlnode_get_child(error
, "feature-not-implemented")) {
2703 text
= _("Feature Not Implemented");
2704 } else if(purple_xmlnode_get_child(error
, "forbidden")) {
2705 text
= _("Forbidden");
2706 } else if(purple_xmlnode_get_child(error
, "gone")) {
2708 } else if(purple_xmlnode_get_child(error
, "internal-server-error")) {
2709 text
= _("Internal Server Error");
2710 } else if(purple_xmlnode_get_child(error
, "item-not-found")) {
2711 text
= _("Item Not Found");
2712 } else if(purple_xmlnode_get_child(error
, "jid-malformed")) {
2713 text
= _("Malformed XMPP ID");
2714 } else if(purple_xmlnode_get_child(error
, "not-acceptable")) {
2715 text
= _("Not Acceptable");
2716 } else if(purple_xmlnode_get_child(error
, "not-allowed")) {
2717 text
= _("Not Allowed");
2718 } else if(purple_xmlnode_get_child(error
, "not-authorized")) {
2719 text
= _("Not Authorized");
2720 } else if(purple_xmlnode_get_child(error
, "payment-required")) {
2721 text
= _("Payment Required");
2722 } else if(purple_xmlnode_get_child(error
, "recipient-unavailable")) {
2723 text
= _("Recipient Unavailable");
2724 } else if(purple_xmlnode_get_child(error
, "redirect")) {
2726 } else if(purple_xmlnode_get_child(error
, "registration-required")) {
2727 text
= _("Registration Required");
2728 } else if(purple_xmlnode_get_child(error
, "remote-server-not-found")) {
2729 text
= _("Remote Server Not Found");
2730 } else if(purple_xmlnode_get_child(error
, "remote-server-timeout")) {
2731 text
= _("Remote Server Timeout");
2732 } else if(purple_xmlnode_get_child(error
, "resource-constraint")) {
2733 text
= _("Server Overloaded");
2734 } else if(purple_xmlnode_get_child(error
, "service-unavailable")) {
2735 text
= _("Service Unavailable");
2736 } else if(purple_xmlnode_get_child(error
, "subscription-required")) {
2737 text
= _("Subscription Required");
2738 } else if(purple_xmlnode_get_child(error
, "unexpected-request")) {
2739 text
= _("Unexpected Request");
2740 } else if(purple_xmlnode_get_child(error
, "undefined-condition")) {
2741 text
= _("Unknown Error");
2743 } else if(purple_strequal(xmlns
, NS_XMPP_SASL
)) {
2744 /* Most common reason can be the default */
2745 SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR
);
2746 if(purple_xmlnode_get_child(packet
, "aborted")) {
2747 text
= _("Authorization Aborted");
2748 } else if(purple_xmlnode_get_child(packet
, "incorrect-encoding")) {
2749 text
= _("Incorrect encoding in authorization");
2750 } else if(purple_xmlnode_get_child(packet
, "invalid-authzid")) {
2751 text
= _("Invalid authzid");
2752 } else if(purple_xmlnode_get_child(packet
, "invalid-mechanism")) {
2753 text
= _("Invalid Authorization Mechanism");
2754 } else if(purple_xmlnode_get_child(packet
, "mechanism-too-weak")) {
2755 SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
);
2756 text
= _("Authorization mechanism too weak");
2757 } else if(purple_xmlnode_get_child(packet
, "not-authorized")) {
2758 SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
);
2759 /* Clear the pasword if it isn't being saved */
2760 if (!purple_account_get_remember_password(purple_connection_get_account(js
->gc
)))
2761 purple_account_set_password(purple_connection_get_account(js
->gc
), NULL
, NULL
, NULL
);
2762 text
= _("Not Authorized");
2763 } else if(purple_xmlnode_get_child(packet
, "temporary-auth-failure")) {
2764 text
= _("Temporary Authentication Failure");
2766 SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
);
2767 text
= _("Authentication Failure");
2769 } else if(purple_strequal(packet
->name
, "stream:error") ||
2770 (purple_strequal(packet
->name
, "error") &&
2771 purple_strequal(xmlns
, NS_XMPP_STREAMS
))) {
2772 /* Most common reason as default: */
2773 SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR
);
2774 if(purple_xmlnode_get_child(packet
, "bad-format")) {
2775 text
= _("Bad Format");
2776 } else if(purple_xmlnode_get_child(packet
, "bad-namespace-prefix")) {
2777 text
= _("Bad Namespace Prefix");
2778 } else if(purple_xmlnode_get_child(packet
, "conflict")) {
2779 SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE
);
2780 text
= _("Resource Conflict");
2781 } else if(purple_xmlnode_get_child(packet
, "connection-timeout")) {
2782 text
= _("Connection Timeout");
2783 } else if(purple_xmlnode_get_child(packet
, "host-gone")) {
2784 text
= _("Host Gone");
2785 } else if(purple_xmlnode_get_child(packet
, "host-unknown")) {
2786 text
= _("Host Unknown");
2787 } else if(purple_xmlnode_get_child(packet
, "improper-addressing")) {
2788 text
= _("Improper Addressing");
2789 } else if(purple_xmlnode_get_child(packet
, "internal-server-error")) {
2790 text
= _("Internal Server Error");
2791 } else if(purple_xmlnode_get_child(packet
, "invalid-id")) {
2792 text
= _("Invalid ID");
2793 } else if(purple_xmlnode_get_child(packet
, "invalid-namespace")) {
2794 text
= _("Invalid Namespace");
2795 } else if(purple_xmlnode_get_child(packet
, "invalid-xml")) {
2796 text
= _("Invalid XML");
2797 } else if(purple_xmlnode_get_child(packet
, "nonmatching-hosts")) {
2798 text
= _("Non-matching Hosts");
2799 } else if(purple_xmlnode_get_child(packet
, "not-authorized")) {
2800 text
= _("Not Authorized");
2801 } else if(purple_xmlnode_get_child(packet
, "policy-violation")) {
2802 text
= _("Policy Violation");
2803 } else if(purple_xmlnode_get_child(packet
, "remote-connection-failed")) {
2804 text
= _("Remote Connection Failed");
2805 } else if(purple_xmlnode_get_child(packet
, "resource-constraint")) {
2806 text
= _("Resource Constraint");
2807 } else if(purple_xmlnode_get_child(packet
, "restricted-xml")) {
2808 text
= _("Restricted XML");
2809 } else if(purple_xmlnode_get_child(packet
, "see-other-host")) {
2810 text
= _("See Other Host");
2811 } else if(purple_xmlnode_get_child(packet
, "system-shutdown")) {
2812 text
= _("System Shutdown");
2813 } else if(purple_xmlnode_get_child(packet
, "undefined-condition")) {
2814 text
= _("Undefined Condition");
2815 } else if(purple_xmlnode_get_child(packet
, "unsupported-encoding")) {
2816 text
= _("Unsupported Encoding");
2817 } else if(purple_xmlnode_get_child(packet
, "unsupported-stanza-type")) {
2818 text
= _("Unsupported Stanza Type");
2819 } else if(purple_xmlnode_get_child(packet
, "unsupported-version")) {
2820 text
= _("Unsupported Version");
2821 } else if(purple_xmlnode_get_child(packet
, "xml-not-well-formed")) {
2822 text
= _("XML Not Well Formed");
2824 text
= _("Stream Error");
2831 char *ret
= g_strdup_printf("%s%s%s", code
? code
: "",
2832 code
? ": " : "", text
? text
: cdata
);
2840 static PurpleCmdRet
jabber_cmd_chat_config(PurpleConversation
*conv
,
2841 const char *cmd
, char **args
, char **error
, void *data
)
2843 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2846 return PURPLE_CMD_RET_FAILED
;
2848 jabber_chat_request_room_configure(chat
);
2849 return PURPLE_CMD_RET_OK
;
2852 static PurpleCmdRet
jabber_cmd_chat_register(PurpleConversation
*conv
,
2853 const char *cmd
, char **args
, char **error
, void *data
)
2855 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2858 return PURPLE_CMD_RET_FAILED
;
2860 jabber_chat_register(chat
);
2861 return PURPLE_CMD_RET_OK
;
2864 static PurpleCmdRet
jabber_cmd_chat_topic(PurpleConversation
*conv
,
2865 const char *cmd
, char **args
, char **error
, void *data
)
2867 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2870 return PURPLE_CMD_RET_FAILED
;
2872 if (args
&& args
[0] && *args
[0])
2873 jabber_chat_change_topic(chat
, args
[0]);
2875 const char *cur
= purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv
));
2876 char *buf
, *tmp
, *tmp2
;
2879 tmp
= g_markup_escape_text(cur
, -1);
2880 tmp2
= purple_markup_linkify(tmp
);
2881 buf
= g_strdup_printf(_("current topic is: %s"), tmp2
);
2885 buf
= g_strdup(_("No topic is set"));
2886 purple_conversation_write_system_message(conv
, buf
, PURPLE_MESSAGE_NO_LOG
);
2890 return PURPLE_CMD_RET_OK
;
2893 static PurpleCmdRet
jabber_cmd_chat_nick(PurpleConversation
*conv
,
2894 const char *cmd
, char **args
, char **error
, void *data
)
2896 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2898 if(!chat
|| !args
|| !args
[0])
2899 return PURPLE_CMD_RET_FAILED
;
2901 if (!jabber_resourceprep_validate(args
[0])) {
2902 *error
= g_strdup(_("Invalid nickname"));
2903 return PURPLE_CMD_RET_FAILED
;
2906 if (jabber_chat_change_nick(chat
, args
[0]))
2907 return PURPLE_CMD_RET_OK
;
2909 return PURPLE_CMD_RET_FAILED
;
2912 static PurpleCmdRet
jabber_cmd_chat_part(PurpleConversation
*conv
,
2913 const char *cmd
, char **args
, char **error
, void *data
)
2915 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2918 return PURPLE_CMD_RET_FAILED
;
2920 jabber_chat_part(chat
, args
? args
[0] : NULL
);
2921 return PURPLE_CMD_RET_OK
;
2924 static PurpleCmdRet
jabber_cmd_chat_ban(PurpleConversation
*conv
,
2925 const char *cmd
, char **args
, char **error
, void *data
)
2927 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2929 if(!chat
|| !args
|| !args
[0])
2930 return PURPLE_CMD_RET_FAILED
;
2932 if(!jabber_chat_ban_user(chat
, args
[0], args
[1])) {
2933 *error
= g_strdup_printf(_("Unable to ban user %s"), args
[0]);
2934 return PURPLE_CMD_RET_FAILED
;
2937 return PURPLE_CMD_RET_OK
;
2940 static PurpleCmdRet
jabber_cmd_chat_affiliate(PurpleConversation
*conv
,
2941 const char *cmd
, char **args
, char **error
, void *data
)
2943 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2945 if (!chat
|| !args
|| !args
[0])
2946 return PURPLE_CMD_RET_FAILED
;
2948 if (!purple_strequal(args
[0], "owner") &&
2949 !purple_strequal(args
[0], "admin") &&
2950 !purple_strequal(args
[0], "member") &&
2951 !purple_strequal(args
[0], "outcast") &&
2952 !purple_strequal(args
[0], "none")) {
2953 *error
= g_strdup_printf(_("Unknown affiliation: \"%s\""), args
[0]);
2954 return PURPLE_CMD_RET_FAILED
;
2959 char **nicks
= g_strsplit(args
[1], " ", -1);
2961 for (i
= 0; nicks
[i
]; ++i
)
2962 if (!jabber_chat_affiliate_user(chat
, nicks
[i
], args
[0])) {
2963 *error
= g_strdup_printf(_("Unable to affiliate user %s as \"%s\""), nicks
[i
], args
[0]);
2965 return PURPLE_CMD_RET_FAILED
;
2970 jabber_chat_affiliation_list(chat
, args
[0]);
2973 return PURPLE_CMD_RET_OK
;
2976 static PurpleCmdRet
jabber_cmd_chat_role(PurpleConversation
*conv
,
2977 const char *cmd
, char **args
, char **error
, void *data
)
2979 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
2981 if (!chat
|| !args
|| !args
[0])
2982 return PURPLE_CMD_RET_FAILED
;
2984 if (!purple_strequal(args
[0], "moderator") &&
2985 !purple_strequal(args
[0], "participant") &&
2986 !purple_strequal(args
[0], "visitor") &&
2987 !purple_strequal(args
[0], "none")) {
2988 *error
= g_strdup_printf(_("Unknown role: \"%s\""), args
[0]);
2989 return PURPLE_CMD_RET_FAILED
;
2994 char **nicks
= g_strsplit(args
[1], " ", -1);
2996 for (i
= 0; nicks
[i
]; i
++)
2997 if (!jabber_chat_role_user(chat
, nicks
[i
], args
[0], NULL
)) {
2998 *error
= g_strdup_printf(_("Unable to set role \"%s\" for user: %s"),
3001 return PURPLE_CMD_RET_FAILED
;
3006 jabber_chat_role_list(chat
, args
[0]);
3008 return PURPLE_CMD_RET_OK
;
3011 static PurpleCmdRet
jabber_cmd_chat_invite(PurpleConversation
*conv
,
3012 const char *cmd
, char **args
, char **error
, void *data
)
3014 if(!args
|| !args
[0])
3015 return PURPLE_CMD_RET_FAILED
;
3017 jabber_chat_invite(purple_conversation_get_connection(conv
),
3018 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv
)), args
[1] ? args
[1] : "",
3021 return PURPLE_CMD_RET_OK
;
3024 static PurpleCmdRet
jabber_cmd_chat_join(PurpleConversation
*conv
,
3025 const char *cmd
, char **args
, char **error
, void *data
)
3027 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
3028 GHashTable
*components
;
3029 JabberID
*jid
= NULL
;
3030 const char *room
= NULL
, *server
= NULL
, *handle
= NULL
;
3032 if (!chat
|| !args
|| !args
[0])
3033 return PURPLE_CMD_RET_FAILED
;
3035 components
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
3037 if (strchr(args
[0], '@'))
3038 jid
= jabber_id_new(args
[0]);
3041 server
= jid
->domain
;
3042 handle
= jid
->resource
? jid
->resource
: chat
->handle
;
3044 /* If jabber_id_new failed, the user may have just passed in
3045 * a room name. For backward compatibility, handle that here.
3047 if (strchr(args
[0], '@')) {
3048 *error
= g_strdup(_("Invalid XMPP ID"));
3049 return PURPLE_CMD_RET_FAILED
;
3053 server
= chat
->server
;
3054 handle
= chat
->handle
;
3057 g_hash_table_insert(components
, "room", (gpointer
)room
);
3058 g_hash_table_insert(components
, "server", (gpointer
)server
);
3059 g_hash_table_insert(components
, "handle", (gpointer
)handle
);
3062 g_hash_table_insert(components
, "password", args
[1]);
3064 jabber_chat_join(purple_conversation_get_connection(conv
), components
);
3066 g_hash_table_destroy(components
);
3067 jabber_id_free(jid
);
3068 return PURPLE_CMD_RET_OK
;
3071 static PurpleCmdRet
jabber_cmd_chat_kick(PurpleConversation
*conv
,
3072 const char *cmd
, char **args
, char **error
, void *data
)
3074 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
3076 if(!chat
|| !args
|| !args
[0])
3077 return PURPLE_CMD_RET_FAILED
;
3079 if(!jabber_chat_role_user(chat
, args
[0], "none", args
[1])) {
3080 *error
= g_strdup_printf(_("Unable to kick user %s"), args
[0]);
3081 return PURPLE_CMD_RET_FAILED
;
3084 return PURPLE_CMD_RET_OK
;
3087 static PurpleCmdRet
jabber_cmd_chat_msg(PurpleConversation
*conv
,
3088 const char *cmd
, char **args
, char **error
, void *data
)
3090 JabberChat
*chat
= jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv
));
3094 return PURPLE_CMD_RET_FAILED
;
3096 who
= g_strdup_printf("%s@%s/%s", chat
->room
, chat
->server
, args
[0]);
3098 jabber_message_send_im(purple_conversation_get_connection(conv
),
3099 purple_message_new_outgoing(who
, args
[1], 0));
3102 return PURPLE_CMD_RET_OK
;
3105 static PurpleCmdRet
jabber_cmd_ping(PurpleConversation
*conv
,
3106 const char *cmd
, char **args
, char **error
, void *data
)
3108 PurpleAccount
*account
;
3109 PurpleConnection
*pc
;
3111 if(!args
|| !args
[0])
3112 return PURPLE_CMD_RET_FAILED
;
3114 account
= purple_conversation_get_account(conv
);
3115 pc
= purple_account_get_connection(account
);
3117 if(!jabber_ping_jid(purple_connection_get_protocol_data(pc
), args
[0])) {
3118 *error
= g_strdup_printf(_("Unable to ping user %s"), args
[0]);
3119 return PURPLE_CMD_RET_FAILED
;
3122 return PURPLE_CMD_RET_OK
;
3125 static gboolean
_jabber_send_buzz(JabberStream
*js
, const char *username
, char **error
) {
3128 JabberBuddyResource
*jbr
;
3129 PurpleConnection
*gc
= js
->gc
;
3130 PurpleBuddy
*buddy
=
3131 purple_blist_find_buddy(purple_connection_get_account(gc
), username
);
3132 const gchar
*alias
=
3133 buddy
? purple_buddy_get_contact_alias(buddy
) : username
;
3138 jb
= jabber_buddy_find(js
, username
, FALSE
);
3140 *error
= g_strdup_printf(_("Unable to buzz, because there is nothing "
3141 "known about %s."), alias
);
3145 jbr
= jabber_buddy_find_resource(jb
, NULL
);
3147 *error
= g_strdup_printf(_("Unable to buzz, because %s might be offline."),
3152 if (jabber_resource_has_capability(jbr
, NS_ATTENTION
)) {
3153 PurpleXmlNode
*buzz
, *msg
= purple_xmlnode_new("message");
3156 to
= g_strdup_printf("%s/%s", username
, jbr
->name
);
3157 purple_xmlnode_set_attrib(msg
, "to", to
);
3160 /* avoid offline storage */
3161 purple_xmlnode_set_attrib(msg
, "type", "headline");
3163 buzz
= purple_xmlnode_new_child(msg
, "attention");
3164 purple_xmlnode_set_namespace(buzz
, NS_ATTENTION
);
3166 jabber_send(js
, msg
);
3167 purple_xmlnode_free(msg
);
3171 *error
= g_strdup_printf(_("Unable to buzz, because %s does "
3172 "not support it or does not wish to receive buzzes now."), alias
);
3177 static PurpleCmdRet
jabber_cmd_buzz(PurpleConversation
*conv
,
3178 const char *cmd
, char **args
, char **error
, void *data
)
3180 PurpleAccount
*account
= purple_conversation_get_account(conv
);
3181 JabberStream
*js
= purple_connection_get_protocol_data(purple_account_get_connection(account
));
3186 PurpleAttentionType
*attn
=
3187 purple_get_attention_type_from_code(account
, 0);
3189 if (!args
|| !args
[0]) {
3190 /* use the buddy from conversation, if it's a one-to-one conversation */
3191 if (PURPLE_IS_IM_CONVERSATION(conv
)) {
3192 who
= purple_conversation_get_name(conv
);
3194 return PURPLE_CMD_RET_FAILED
;
3200 buddy
= purple_blist_find_buddy(account
, who
);
3202 alias
= purple_buddy_get_contact_alias(buddy
);
3207 g_strdup_printf(purple_attention_type_get_outgoing_desc(attn
), alias
);
3208 purple_conversation_write_system_message(conv
, description
,
3209 PURPLE_MESSAGE_NOTIFY
);
3210 g_free(description
);
3211 return _jabber_send_buzz(js
, who
, error
) ? PURPLE_CMD_RET_OK
: PURPLE_CMD_RET_FAILED
;
3214 GList
*jabber_attention_types(PurpleProtocolAttention
*attn
, PurpleAccount
*account
)
3216 static GList
*types
= NULL
;
3219 types
= g_list_append(types
, purple_attention_type_new("Buzz", _("Buzz"),
3220 _("%s has buzzed you!"), _("Buzzing %s...")));
3226 gboolean
jabber_send_attention(PurpleProtocolAttention
*attn
, PurpleConnection
*gc
, const char *username
, guint code
)
3228 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3229 gchar
*error
= NULL
;
3231 if (!_jabber_send_buzz(js
, username
, &error
)) {
3232 PurpleAccount
*account
= purple_connection_get_account(gc
);
3233 PurpleConversation
*conv
=
3234 purple_conversations_find_with_account(username
, account
);
3235 purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error
? error
: "(NULL)");
3238 purple_conversation_write_system_message(conv
,
3239 error
, PURPLE_MESSAGE_ERROR
);
3250 gboolean
jabber_offline_message(const PurpleBuddy
*buddy
)
3257 jabber_audio_enabled(JabberStream
*js
, const char *namespace)
3259 PurpleMediaManager
*manager
= purple_media_manager_get();
3260 PurpleMediaCaps caps
= purple_media_manager_get_ui_caps(manager
);
3262 return (caps
& (PURPLE_MEDIA_CAPS_AUDIO
| PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION
));
3266 jabber_video_enabled(JabberStream
*js
, const char *namespace)
3268 PurpleMediaManager
*manager
= purple_media_manager_get();
3269 PurpleMediaCaps caps
= purple_media_manager_get_ui_caps(manager
);
3271 return (caps
& (PURPLE_MEDIA_CAPS_VIDEO
| PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION
));
3275 PurpleAccount
*account
;
3277 PurpleMediaSessionType type
;
3279 } JabberMediaRequest
;
3282 jabber_media_cancel_cb(JabberMediaRequest
*request
,
3283 PurpleRequestFields
*fields
)
3285 g_free(request
->who
);
3290 jabber_media_ok_cb(JabberMediaRequest
*request
, PurpleRequestFields
*fields
)
3292 PurpleRequestField
*field
=
3293 purple_request_fields_get_field(fields
, "resource");
3294 const gchar
*selected
= purple_request_field_choice_get_value(field
);
3295 gchar
*who
= g_strdup_printf("%s/%s", request
->who
, selected
);
3296 jabber_initiate_media(request
->account
, who
, request
->type
);
3299 g_free(request
->who
);
3305 jabber_initiate_media(PurpleAccount
*account
, const char *who
,
3306 PurpleMediaSessionType type
)
3309 PurpleConnection
*gc
= purple_account_get_connection(account
);
3310 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3312 JabberBuddyResource
*jbr
= NULL
;
3313 char *resource
= NULL
;
3316 purple_debug_error("jabber",
3317 "jabber_initiate_media: NULL stream\n");
3321 jb
= jabber_buddy_find(js
, who
, FALSE
);
3323 if(!jb
|| !jb
->resources
||
3324 (((resource
= jabber_get_resource(who
)) != NULL
)
3325 && (jbr
= jabber_buddy_find_resource(jb
, resource
)) == NULL
)) {
3326 /* no resources online, we're trying to initiate with someone
3327 * whose presence we're not subscribed to, or
3328 * someone who is offline. Let's inform the user */
3332 msg
= g_strdup_printf(_("Unable to initiate media with %s: invalid JID"), who
);
3333 } else if(jb
->subscription
& JABBER_SUB_TO
&& !jb
->resources
) {
3334 msg
= g_strdup_printf(_("Unable to initiate media with %s: user is not online"), who
);
3335 } else if(resource
) {
3336 msg
= g_strdup_printf(_("Unable to initiate media with %s: resource is not online"), who
);
3338 msg
= g_strdup_printf(_("Unable to initiate media with %s: not subscribed to user presence"), who
);
3341 purple_notify_error(account
, _("Media Initiation Failed"),
3342 _("Media Initiation Failed"), msg
,
3343 purple_request_cpar_from_connection(gc
));
3347 } else if(jbr
!= NULL
) {
3348 /* they've specified a resource, no need to ask or
3349 * default or anything, just do it */
3353 if (type
& PURPLE_MEDIA_AUDIO
&&
3354 !jabber_resource_has_capability(jbr
,
3355 JINGLE_APP_RTP_SUPPORT_AUDIO
) &&
3356 jabber_resource_has_capability(jbr
, NS_GOOGLE_VOICE
))
3357 return jabber_google_session_initiate(js
, who
, type
);
3359 return jingle_rtp_initiate_media(js
, who
, type
);
3360 } else if(!jb
->resources
->next
) {
3361 /* only 1 resource online (probably our most common case)
3362 * so no need to ask who to initiate with */
3365 jbr
= jb
->resources
->data
;
3366 name
= g_strdup_printf("%s/%s", who
, jbr
->name
);
3367 result
= jabber_initiate_media(account
, name
, type
);
3371 /* we've got multiple resources,
3372 * we need to pick one to initiate with */
3375 PurpleRequestFields
*fields
;
3376 PurpleRequestField
*field
= purple_request_field_choice_new(
3377 "resource", _("Resource"), 0);
3378 PurpleRequestFieldGroup
*group
;
3379 JabberMediaRequest
*request
;
3381 purple_request_field_choice_set_data_destructor(field
, g_free
);
3383 for(l
= jb
->resources
; l
; l
= l
->next
)
3385 JabberBuddyResource
*ljbr
= l
->data
;
3386 PurpleMediaCaps caps
;
3388 name
= g_strdup_printf("%s/%s", who
, ljbr
->name
);
3389 caps
= jabber_get_media_caps(account
, name
);
3392 if ((type
& PURPLE_MEDIA_AUDIO
) &&
3393 (type
& PURPLE_MEDIA_VIDEO
)) {
3394 if (caps
& PURPLE_MEDIA_CAPS_AUDIO_VIDEO
) {
3396 purple_request_field_choice_add(field
,
3397 jbr
->name
, g_strdup(jbr
->name
));
3399 } else if (type
& (PURPLE_MEDIA_AUDIO
) &&
3400 (caps
& PURPLE_MEDIA_CAPS_AUDIO
)) {
3402 purple_request_field_choice_add(field
,
3403 jbr
->name
, g_strdup(jbr
->name
));
3404 }else if (type
& (PURPLE_MEDIA_VIDEO
) &&
3405 (caps
& PURPLE_MEDIA_CAPS_VIDEO
)) {
3407 purple_request_field_choice_add(field
,
3408 jbr
->name
, g_strdup(jbr
->name
));
3413 purple_debug_error("jabber",
3414 "No resources available\n");
3418 if (g_list_length(purple_request_field_choice_get_elements(
3422 purple_request_field_destroy(field
);
3423 name
= g_strdup_printf("%s/%s", who
, jbr
->name
);
3424 result
= jabber_initiate_media(account
, name
, type
);
3429 msg
= g_strdup_printf(_("Please select the resource of %s with which you would like to start a media session."), who
);
3430 fields
= purple_request_fields_new();
3431 group
= purple_request_field_group_new(NULL
);
3432 request
= g_new0(JabberMediaRequest
, 1);
3433 request
->account
= account
;
3434 request
->who
= g_strdup(who
);
3435 request
->type
= type
;
3437 purple_request_field_group_add_field(group
, field
);
3438 purple_request_fields_add_group(fields
, group
);
3439 purple_request_fields(account
, _("Select a Resource"), msg
,
3440 NULL
, fields
, _("Initiate Media"),
3441 G_CALLBACK(jabber_media_ok_cb
), _("Cancel"),
3442 G_CALLBACK(jabber_media_cancel_cb
),
3443 purple_request_cpar_from_account(account
),
3453 PurpleMediaCaps
jabber_get_media_caps(PurpleAccount
*account
, const char *who
)
3456 PurpleConnection
*gc
= purple_account_get_connection(account
);
3457 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3459 JabberBuddyResource
*jbr
;
3460 PurpleMediaCaps total
= PURPLE_MEDIA_CAPS_NONE
;
3462 GList
*specific
= NULL
, *l
;
3465 purple_debug_info("jabber",
3466 "jabber_can_do_media: NULL stream\n");
3470 jb
= jabber_buddy_find(js
, who
, FALSE
);
3472 if (!jb
|| !jb
->resources
) {
3473 /* no resources online, we're trying to get caps for someone
3474 * whose presence we're not subscribed to, or
3475 * someone who is offline. */
3478 } else if ((resource
= jabber_get_resource(who
)) != NULL
) {
3479 /* they've specified a resource, no need to ask or
3480 * default or anything, just do it */
3481 jbr
= jabber_buddy_find_resource(jb
, resource
);
3485 purple_debug_error("jabber", "jabber_get_media_caps:"
3486 " Can't find resource %s\n", who
);
3490 l
= specific
= g_list_prepend(specific
, jbr
);
3493 /* we've got multiple resources, combine their caps */
3497 for (; l
; l
= l
->next
) {
3498 PurpleMediaCaps caps
= PURPLE_MEDIA_CAPS_NONE
;
3501 if (jabber_resource_has_capability(jbr
,
3502 JINGLE_APP_RTP_SUPPORT_AUDIO
))
3503 caps
|= PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION
|
3504 PURPLE_MEDIA_CAPS_AUDIO
;
3505 if (jabber_resource_has_capability(jbr
,
3506 JINGLE_APP_RTP_SUPPORT_VIDEO
))
3507 caps
|= PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION
|
3508 PURPLE_MEDIA_CAPS_VIDEO
;
3509 if (caps
& PURPLE_MEDIA_CAPS_AUDIO
&& caps
&
3510 PURPLE_MEDIA_CAPS_VIDEO
)
3511 caps
|= PURPLE_MEDIA_CAPS_AUDIO_VIDEO
;
3512 if (caps
!= PURPLE_MEDIA_CAPS_NONE
) {
3513 if (!jabber_resource_has_capability(jbr
,
3514 JINGLE_TRANSPORT_ICEUDP
) &&
3515 !jabber_resource_has_capability(jbr
,
3516 JINGLE_TRANSPORT_RAWUDP
)) {
3517 purple_debug_info("jingle-rtp", "Buddy doesn't "
3518 "support the same transport types\n");
3519 caps
= PURPLE_MEDIA_CAPS_NONE
;
3521 caps
|= PURPLE_MEDIA_CAPS_MODIFY_SESSION
|
3522 PURPLE_MEDIA_CAPS_CHANGE_DIRECTION
;
3524 if (jabber_resource_has_capability(jbr
, NS_GOOGLE_VOICE
)) {
3525 caps
|= PURPLE_MEDIA_CAPS_AUDIO
;
3526 if (jabber_resource_has_capability(jbr
, NS_GOOGLE_VIDEO
))
3527 caps
|= PURPLE_MEDIA_CAPS_AUDIO_VIDEO
;
3534 g_list_free(specific
);
3539 return PURPLE_MEDIA_CAPS_NONE
;
3543 gboolean
jabber_can_receive_file(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
)
3545 JabberStream
*js
= purple_connection_get_protocol_data(gc
);
3548 JabberBuddy
*jb
= jabber_buddy_find(js
, who
, FALSE
);
3550 gboolean has_resources_without_caps
= FALSE
;
3552 /* if we didn't find a JabberBuddy, we don't have presence for this
3553 buddy, let's assume they can receive files, disco should tell us
3554 when actually trying */
3558 /* find out if there is any resources without caps */
3559 for (iter
= jb
->resources
; iter
; iter
= g_list_next(iter
)) {
3560 JabberBuddyResource
*jbr
= (JabberBuddyResource
*) iter
->data
;
3562 if (!jabber_resource_know_capabilities(jbr
)) {
3563 has_resources_without_caps
= TRUE
;
3567 if (has_resources_without_caps
) {
3568 /* there is at least one resource which we don't have caps for,
3569 let's assume they can receive files... */
3572 /* we have caps for all the resources, see if at least one has
3574 for (iter
= jb
->resources
; iter
; iter
= g_list_next(iter
)) {
3575 JabberBuddyResource
*jbr
= (JabberBuddyResource
*) iter
->data
;
3577 if (jabber_resource_has_capability(jbr
, NS_SI_FILE_TRANSFER
)
3578 && (jabber_resource_has_capability(jbr
,
3580 || jabber_resource_has_capability(jbr
, NS_IBB
))) {
3592 jabber_cmd_mood(PurpleConversation
*conv
,
3593 const char *cmd
, char **args
, char **error
, void *data
)
3595 PurpleAccount
*account
= purple_conversation_get_account(conv
);
3596 JabberStream
*js
= purple_connection_get_protocol_data(purple_account_get_connection(account
));
3601 if (!args
|| !args
[0]) {
3602 /* No arguments; unset mood */
3603 ret
= jabber_mood_set(js
, NULL
, NULL
);
3605 /* At least one argument. Relying on the list of arguments
3606 * being NULL-terminated.
3608 ret
= jabber_mood_set(js
, args
[0], args
[1]);
3610 /* Let's try again */
3611 char *tmp
= g_strjoin(" ", args
[0], args
[1], NULL
);
3612 ret
= jabber_mood_set(js
, "undefined", tmp
);
3618 return PURPLE_CMD_RET_OK
;
3620 purple_conversation_write_system_message(conv
,
3621 _("Failed to specify mood"),
3622 PURPLE_MESSAGE_ERROR
);
3623 return PURPLE_CMD_RET_FAILED
;
3626 /* account does not support PEP, can't set a mood */
3627 purple_conversation_write_system_message(conv
,
3628 _("Account does not support PEP, can't set mood"),
3629 PURPLE_MESSAGE_ERROR
);
3630 return PURPLE_CMD_RET_FAILED
;
3635 jabber_register_commands(PurpleProtocol
*protocol
)
3637 GSList
*commands
= NULL
;
3639 const gchar
*proto_id
= purple_protocol_get_id(protocol
);
3641 id
= purple_cmd_register("config", "", PURPLE_CMD_P_PROTOCOL
,
3642 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3643 jabber_cmd_chat_config
, _("config: Configure a chat room."),
3645 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3647 id
= purple_cmd_register("configure", "", PURPLE_CMD_P_PROTOCOL
,
3648 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3649 jabber_cmd_chat_config
, _("configure: Configure a chat room."),
3651 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3653 id
= purple_cmd_register("nick", "s", PURPLE_CMD_P_PROTOCOL
,
3654 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3655 jabber_cmd_chat_nick
, _("nick <new nickname>: "
3656 "Change your nickname."), NULL
);
3657 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3659 id
= purple_cmd_register("part", "s", PURPLE_CMD_P_PROTOCOL
,
3660 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3661 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_part
,
3662 _("part [message]: Leave the room."), NULL
);
3663 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3665 id
= purple_cmd_register("register", "", PURPLE_CMD_P_PROTOCOL
,
3666 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3667 jabber_cmd_chat_register
,
3668 _("register: Register with a chat room."), NULL
);
3669 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3671 /* XXX: there needs to be a core /topic cmd, methinks */
3672 id
= purple_cmd_register("topic", "s", PURPLE_CMD_P_PROTOCOL
,
3673 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3674 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_topic
,
3675 _("topic [new topic]: View or change the topic."), NULL
);
3676 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3678 id
= purple_cmd_register("ban", "ws", PURPLE_CMD_P_PROTOCOL
,
3679 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3680 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_ban
,
3681 _("ban <user> [reason]: Ban a user from the room."),
3683 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3685 id
= purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PROTOCOL
,
3686 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3687 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
,
3688 jabber_cmd_chat_affiliate
, _("affiliate "
3689 "<owner|admin|member|outcast|none> [nick1] [nick2] ...: "
3690 "Get the users with an affiliation or set users' affiliation "
3691 "with the room."), NULL
);
3692 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3694 id
= purple_cmd_register("role", "ws", PURPLE_CMD_P_PROTOCOL
,
3695 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3696 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_role
,
3697 _("role <moderator|participant|visitor|none> [nick1] "
3698 "[nick2] ...: Get the users with a role or set users' role "
3699 "with the room."), NULL
);
3700 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3702 id
= purple_cmd_register("invite", "ws", PURPLE_CMD_P_PROTOCOL
,
3703 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3704 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_invite
,
3705 _("invite <user> [message]: Invite a user to the room."),
3707 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3709 id
= purple_cmd_register("join", "ws", PURPLE_CMD_P_PROTOCOL
,
3710 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3711 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_join
,
3712 _("join: <room[@server]> [password]: Join a chat."),
3714 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3716 id
= purple_cmd_register("kick", "ws", PURPLE_CMD_P_PROTOCOL
,
3717 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3718 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_chat_kick
,
3719 _("kick <user> [reason]: Kick a user from the room."),
3721 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3723 id
= purple_cmd_register("msg", "ws", PURPLE_CMD_P_PROTOCOL
,
3724 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
,
3725 jabber_cmd_chat_msg
, _("msg <user> <message>: "
3726 "Send a private message to another user."), NULL
);
3727 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3729 id
= purple_cmd_register("ping", "w", PURPLE_CMD_P_PROTOCOL
,
3730 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_IM
|
3731 PURPLE_CMD_FLAG_PROTOCOL_ONLY
, proto_id
, jabber_cmd_ping
,
3732 _("ping <jid>: Ping a user/component/server."), NULL
);
3733 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3735 id
= purple_cmd_register("buzz", "w", PURPLE_CMD_P_PROTOCOL
,
3736 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
|
3737 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
, proto_id
, jabber_cmd_buzz
,
3738 _("buzz: Buzz a user to get their attention"), NULL
);
3739 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3741 id
= purple_cmd_register("mood", "ws", PURPLE_CMD_P_PROTOCOL
,
3742 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_IM
|
3743 PURPLE_CMD_FLAG_PROTOCOL_ONLY
| PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
,
3744 proto_id
, jabber_cmd_mood
,
3745 _("mood <mood> [text]: Set current user mood"), NULL
);
3746 commands
= g_slist_prepend(commands
, GUINT_TO_POINTER(id
));
3748 g_hash_table_insert(jabber_cmds
, protocol
, commands
);
3751 static void cmds_free_func(gpointer value
)
3753 GSList
*commands
= value
;
3755 purple_cmd_unregister(GPOINTER_TO_UINT(commands
->data
));
3756 commands
= g_slist_delete_link(commands
, commands
);
3760 static void jabber_unregister_commands(PurpleProtocol
*protocol
)
3762 g_hash_table_remove(jabber_cmds
, protocol
);
3765 static PurpleAccount
*find_acct(const char *protocol
, const char *acct_id
)
3767 PurpleAccount
*acct
= NULL
;
3769 /* If we have a specific acct, use it */
3771 acct
= purple_accounts_find(acct_id
, protocol
);
3772 if (acct
&& !purple_account_is_connected(acct
))
3774 } else { /* Otherwise find an active account for the protocol */
3775 GList
*l
= purple_accounts_get_all();
3777 if (purple_strequal(protocol
, purple_account_get_protocol_id(l
->data
))
3778 && purple_account_is_connected(l
->data
)) {
3790 xmpp_uri_handler(const char *proto
, const char *user
, GHashTable
*params
,
3793 PurpleProtocol
*protocol
= (PurpleProtocol
*)user_data
;
3794 const gchar
*acct_id
= NULL
;
3795 PurpleAccount
*acct
;
3797 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol
), FALSE
);
3799 if (g_ascii_strcasecmp(proto
, "xmpp"))
3802 if (params
!= NULL
) {
3803 acct_id
= g_hash_table_lookup(params
, "account");
3806 acct
= find_acct(protocol
->id
, acct_id
);
3811 /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */
3812 /* params is NULL if the URI has no '?' (or anything after it) */
3813 if (!params
|| g_hash_table_lookup_extended(params
, "message", NULL
, NULL
)) {
3814 if (user
&& *user
) {
3815 PurpleIMConversation
*im
=
3816 purple_im_conversation_new(acct
, user
);
3817 const gchar
*body
= NULL
;
3819 purple_conversation_present(PURPLE_CONVERSATION(im
));
3821 if (params
!= NULL
) {
3822 body
= g_hash_table_lookup(params
, "body");
3826 purple_conversation_send_confirm(PURPLE_CONVERSATION(im
), body
);
3829 } else if (g_hash_table_lookup_extended(params
, "roster", NULL
, NULL
)) {
3830 char *name
= g_hash_table_lookup(params
, "name");
3831 if (user
&& *user
) {
3832 purple_blist_request_add_buddy(acct
, user
, NULL
, name
);
3835 } else if (g_hash_table_lookup_extended(params
, "join", NULL
, NULL
)) {
3836 PurpleConnection
*gc
= purple_account_get_connection(acct
);
3837 if (user
&& *user
) {
3838 GHashTable
*params
= jabber_chat_info_defaults(gc
, user
);
3839 jabber_chat_join(gc
, params
);
3848 jabber_do_init(void)
3850 GHashTable
*ui_info
= purple_core_get_ui_info();
3851 const gchar
*ui_type
;
3852 const gchar
*type
= "pc"; /* default client type, if unknown or
3854 const gchar
*ui_name
= NULL
;
3855 #ifdef HAVE_CYRUS_SASL
3856 /* We really really only want to do this once per process */
3857 static gboolean sasl_initialized
= FALSE
;
3859 UINT old_error_mode
;
3865 /* XXX - If any other plugin wants SASL this won't be good ... */
3866 #ifdef HAVE_CYRUS_SASL
3867 if (!sasl_initialized
) {
3868 sasl_initialized
= TRUE
;
3870 sasldir
= g_strdup(wpurple_lib_dir("sasl2"));
3871 sasl_set_path(SASL_PATH_TYPE_PLUGIN
, sasldir
);
3873 /* Suppress error popups for failing to load sasl plugins */
3874 old_error_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
3876 if ((ret
= sasl_client_init(NULL
)) != SASL_OK
) {
3877 purple_debug_error("xmpp", "Error (%d) initializing SASL.\n", ret
);
3880 /* Restore the original error mode */
3881 SetErrorMode(old_error_mode
);
3886 jabber_cmds
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
, NULL
, cmds_free_func
);
3888 ui_type
= ui_info
? g_hash_table_lookup(ui_info
, "client_type") : NULL
;
3890 if (purple_strequal(ui_type
, "pc") ||
3891 purple_strequal(ui_type
, "console") ||
3892 purple_strequal(ui_type
, "phone") ||
3893 purple_strequal(ui_type
, "handheld") ||
3894 purple_strequal(ui_type
, "web") ||
3895 purple_strequal(ui_type
, "bot")) {
3901 ui_name
= g_hash_table_lookup(ui_info
, "name");
3902 if (ui_name
== NULL
)
3905 jabber_add_identity("client", type
, NULL
, ui_name
);
3907 /* initialize jabber_features list */
3908 jabber_add_feature(NS_LAST_ACTIVITY
, NULL
);
3909 jabber_add_feature(NS_OOB_IQ_DATA
, NULL
);
3910 jabber_add_feature(NS_ENTITY_TIME
, NULL
);
3911 jabber_add_feature("jabber:iq:version", NULL
);
3912 jabber_add_feature("jabber:x:conference", NULL
);
3913 jabber_add_feature(NS_BYTESTREAMS
, NULL
);
3914 jabber_add_feature("http://jabber.org/protocol/caps", NULL
);
3915 jabber_add_feature("http://jabber.org/protocol/chatstates", NULL
);
3916 jabber_add_feature(NS_DISCO_INFO
, NULL
);
3917 jabber_add_feature(NS_DISCO_ITEMS
, NULL
);
3918 jabber_add_feature(NS_IBB
, NULL
);
3919 jabber_add_feature("http://jabber.org/protocol/muc", NULL
);
3920 jabber_add_feature("http://jabber.org/protocol/muc#user", NULL
);
3921 jabber_add_feature("http://jabber.org/protocol/si", NULL
);
3922 jabber_add_feature(NS_SI_FILE_TRANSFER
, NULL
);
3923 jabber_add_feature(NS_XHTML_IM
, NULL
);
3924 jabber_add_feature(NS_PING
, NULL
);
3926 /* Buzz/Attention */
3927 jabber_add_feature(NS_ATTENTION
, jabber_buzz_isenabled
);
3929 /* Bits Of Binary */
3930 jabber_add_feature(NS_BOB
, NULL
);
3932 /* Jingle features! */
3933 jabber_add_feature(JINGLE
, NULL
);
3936 jabber_add_feature(NS_GOOGLE_PROTOCOL_SESSION
, jabber_audio_enabled
);
3937 jabber_add_feature(NS_GOOGLE_VOICE
, jabber_audio_enabled
);
3938 jabber_add_feature(NS_GOOGLE_VIDEO
, jabber_video_enabled
);
3939 jabber_add_feature(NS_GOOGLE_CAMERA
, jabber_video_enabled
);
3940 jabber_add_feature(JINGLE_APP_RTP
, NULL
);
3941 jabber_add_feature(JINGLE_APP_RTP_SUPPORT_AUDIO
, jabber_audio_enabled
);
3942 jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO
, jabber_video_enabled
);
3943 jabber_add_feature(JINGLE_TRANSPORT_RAWUDP
, NULL
);
3944 jabber_add_feature(JINGLE_TRANSPORT_ICEUDP
, NULL
);
3946 g_signal_connect(G_OBJECT(purple_media_manager_get()), "ui-caps-changed",
3947 G_CALLBACK(jabber_caps_broadcast_change
), NULL
);
3950 /* reverse order of unload_plugin */
3952 jabber_presence_init();
3954 /* PEP things should be init via jabber_pep_init, not here */
3959 /* TODO: Implement adding and retrieving own features via IPC API */
3968 jabber_do_uninit(void)
3970 /* reverse order of jabber_do_init */
3971 jabber_bosh_uninit();
3972 jabber_data_uninit();
3974 jabber_ibb_uninit();
3975 /* PEP things should be uninit via jabber_pep_uninit, not here */
3976 jabber_pep_uninit();
3977 jabber_caps_uninit();
3978 jabber_presence_uninit();
3982 g_signal_handlers_disconnect_by_func(G_OBJECT(purple_media_manager_get()),
3983 G_CALLBACK(jabber_caps_broadcast_change
), NULL
);
3986 jabber_auth_uninit();
3987 jabber_features_destroy();
3988 jabber_identities_destroy();
3990 g_hash_table_destroy(jabber_cmds
);
3994 static void jabber_init_protocol(PurpleProtocol
*protocol
)
3998 if (plugin_ref
== 1)
4001 jabber_register_commands(protocol
);
4003 purple_signal_register(protocol
, "jabber-register-namespace-watcher",
4004 purple_marshal_VOID__POINTER_POINTER
,
4006 G_TYPE_STRING
, /* node */
4007 G_TYPE_STRING
); /* namespace */
4009 purple_signal_register(protocol
, "jabber-unregister-namespace-watcher",
4010 purple_marshal_VOID__POINTER_POINTER
,
4012 G_TYPE_STRING
, /* node */
4013 G_TYPE_STRING
); /* namespace */
4015 purple_signal_connect(protocol
, "jabber-register-namespace-watcher",
4016 protocol
, PURPLE_CALLBACK(jabber_iq_signal_register
), NULL
);
4017 purple_signal_connect(protocol
, "jabber-unregister-namespace-watcher",
4018 protocol
, PURPLE_CALLBACK(jabber_iq_signal_unregister
), NULL
);
4021 purple_signal_register(protocol
, "jabber-receiving-xmlnode",
4022 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
4023 PURPLE_TYPE_CONNECTION
,
4024 G_TYPE_POINTER
); /* pointer to a PurpleXmlNode* */
4026 purple_signal_register(protocol
, "jabber-sending-xmlnode",
4027 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
4028 PURPLE_TYPE_CONNECTION
,
4029 G_TYPE_POINTER
); /* pointer to a PurpleXmlNode* */
4032 * Do not remove this or the plugin will fail. Completely. You have been
4035 purple_signal_connect_priority(protocol
, "jabber-sending-xmlnode",
4036 protocol
, PURPLE_CALLBACK(jabber_send_signal_cb
),
4037 NULL
, PURPLE_SIGNAL_PRIORITY_HIGHEST
);
4039 purple_signal_register(protocol
, "jabber-sending-text",
4040 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
4041 PURPLE_TYPE_CONNECTION
,
4042 G_TYPE_POINTER
); /* pointer to a string */
4044 purple_signal_register(protocol
, "jabber-receiving-message",
4045 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER
,
4047 PURPLE_TYPE_CONNECTION
,
4048 G_TYPE_STRING
, /* type */
4049 G_TYPE_STRING
, /* id */
4050 G_TYPE_STRING
, /* from */
4051 G_TYPE_STRING
, /* to */
4052 PURPLE_TYPE_XMLNODE
);
4054 purple_signal_register(protocol
, "jabber-receiving-iq",
4055 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER
,
4057 PURPLE_TYPE_CONNECTION
,
4058 G_TYPE_STRING
, /* type */
4059 G_TYPE_STRING
, /* id */
4060 G_TYPE_STRING
, /* from */
4061 PURPLE_TYPE_XMLNODE
);
4063 purple_signal_register(protocol
, "jabber-watched-iq",
4064 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER
,
4066 PURPLE_TYPE_CONNECTION
,
4067 G_TYPE_STRING
, /* type */
4068 G_TYPE_STRING
, /* id */
4069 G_TYPE_STRING
, /* from */
4070 PURPLE_TYPE_XMLNODE
); /* child */
4072 purple_signal_register(protocol
, "jabber-receiving-presence",
4073 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER
,
4075 PURPLE_TYPE_CONNECTION
,
4076 G_TYPE_STRING
, /* type */
4077 G_TYPE_STRING
, /* from */
4078 PURPLE_TYPE_XMLNODE
);
4081 static void jabber_uninit_protocol(PurpleProtocol
*protocol
)
4083 g_return_if_fail(plugin_ref
> 0);
4085 purple_signals_unregister_by_instance(protocol
);
4086 jabber_unregister_commands(protocol
);
4089 if (plugin_ref
== 0)
4094 jabber_protocol_init(JabberProtocol
*self
)
4096 PurpleProtocol
*protocol
= PURPLE_PROTOCOL(self
);
4098 protocol
->id
= "prpl-jabber";
4099 protocol
->name
= "XMPP";
4100 protocol
->options
= OPT_PROTO_CHAT_TOPIC
| OPT_PROTO_UNIQUE_CHATNAME
|
4101 OPT_PROTO_MAIL_CHECK
|
4102 #ifdef HAVE_CYRUS_SASL
4103 OPT_PROTO_PASSWORD_OPTIONAL
|
4105 OPT_PROTO_SLASH_COMMANDS_NATIVE
;
4107 protocol
->icon_spec
= purple_buddy_icon_spec_new("png",
4109 PURPLE_ICON_SCALE_SEND
|
4110 PURPLE_ICON_SCALE_DISPLAY
);
4114 jabber_protocol_class_init(JabberProtocolClass
*klass
)
4116 PurpleProtocolClass
*protocol_class
= PURPLE_PROTOCOL_CLASS(klass
);
4118 protocol_class
->login
= jabber_login
;
4119 protocol_class
->close
= jabber_close
;
4120 protocol_class
->status_types
= jabber_status_types
;
4121 protocol_class
->list_icon
= jabber_list_icon
;
4125 jabber_protocol_class_finalize(G_GNUC_UNUSED JabberProtocolClass
*klass
)
4130 jabber_protocol_client_iface_init(PurpleProtocolClientInterface
*client_iface
)
4132 client_iface
->get_actions
= jabber_get_actions
;
4133 client_iface
->list_emblem
= jabber_list_emblem
;
4134 client_iface
->status_text
= jabber_status_text
;
4135 client_iface
->tooltip_text
= jabber_tooltip_text
;
4136 client_iface
->blist_node_menu
= jabber_blist_node_menu
;
4137 client_iface
->convo_closed
= jabber_convo_closed
;
4138 client_iface
->normalize
= jabber_normalize
;
4139 client_iface
->find_blist_chat
= jabber_find_blist_chat
;
4140 client_iface
->offline_message
= jabber_offline_message
;
4141 client_iface
->get_moods
= jabber_get_moods
;
4145 jabber_protocol_server_iface_init(PurpleProtocolServerInterface
*server_iface
)
4147 server_iface
->register_user
= jabber_register_account
;
4148 server_iface
->unregister_user
= jabber_unregister_account
;
4149 server_iface
->set_info
= jabber_set_info
;
4150 server_iface
->get_info
= jabber_buddy_get_info
;
4151 server_iface
->set_status
= jabber_set_status
;
4152 server_iface
->set_idle
= jabber_idle_set
;
4153 server_iface
->add_buddy
= jabber_roster_add_buddy
;
4154 server_iface
->remove_buddy
= jabber_roster_remove_buddy
;
4155 server_iface
->keepalive
= jabber_keepalive
;
4156 server_iface
->get_keepalive_interval
= jabber_get_keepalive_interval
;
4157 server_iface
->alias_buddy
= jabber_roster_alias_change
;
4158 server_iface
->group_buddy
= jabber_roster_group_change
;
4159 server_iface
->rename_group
= jabber_roster_group_rename
;
4160 server_iface
->set_buddy_icon
= jabber_set_buddy_icon
;
4161 server_iface
->send_raw
= jabber_protocol_send_raw
;
4165 jabber_protocol_im_iface_init(PurpleProtocolIMInterface
*im_iface
)
4167 im_iface
->send
= jabber_message_send_im
;
4168 im_iface
->send_typing
= jabber_send_typing
;
4172 jabber_protocol_chat_iface_init(PurpleProtocolChatInterface
*chat_iface
)
4174 chat_iface
->info
= jabber_chat_info
;
4175 chat_iface
->info_defaults
= jabber_chat_info_defaults
;
4176 chat_iface
->join
= jabber_chat_join
;
4177 chat_iface
->get_name
= jabber_get_chat_name
;
4178 chat_iface
->invite
= jabber_chat_invite
;
4179 chat_iface
->leave
= jabber_chat_leave
;
4180 chat_iface
->send
= jabber_message_send_chat
;
4181 chat_iface
->get_user_real_name
= jabber_chat_user_real_name
;
4182 chat_iface
->set_topic
= jabber_chat_set_topic
;
4186 jabber_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface
*privacy_iface
)
4188 privacy_iface
->add_deny
= jabber_add_deny
;
4189 privacy_iface
->rem_deny
= jabber_rem_deny
;
4193 jabber_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface
*roomlist_iface
)
4195 roomlist_iface
->get_list
= jabber_roomlist_get_list
;
4196 roomlist_iface
->cancel
= jabber_roomlist_cancel
;
4197 roomlist_iface
->room_serialize
= jabber_roomlist_room_serialize
;
4201 jabber_protocol_attention_iface_init(PurpleProtocolAttentionInterface
*iface
)
4203 iface
->send
= jabber_send_attention
;
4204 iface
->get_types
= jabber_attention_types
;
4208 jabber_protocol_media_iface_init(PurpleProtocolMediaInterface
*media_iface
)
4210 media_iface
->initiate_session
= jabber_initiate_media
;
4211 media_iface
->get_caps
= jabber_get_media_caps
;
4215 jabber_protocol_xfer_iface_init(PurpleProtocolXferInterface
*xfer_iface
)
4217 xfer_iface
->can_receive
= jabber_can_receive_file
;
4218 xfer_iface
->send_file
= jabber_si_xfer_send
;
4219 xfer_iface
->new_xfer
= jabber_si_new_xfer
;
4222 G_DEFINE_DYNAMIC_TYPE_EXTENDED(
4223 JabberProtocol
, jabber_protocol
, PURPLE_TYPE_PROTOCOL
,
4224 G_TYPE_FLAG_ABSTRACT
,
4226 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT
,
4227 jabber_protocol_client_iface_init
)
4229 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER
,
4230 jabber_protocol_server_iface_init
)
4232 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM
,
4233 jabber_protocol_im_iface_init
)
4235 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT
,
4236 jabber_protocol_chat_iface_init
)
4238 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY
,
4239 jabber_protocol_privacy_iface_init
)
4241 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST
,
4242 jabber_protocol_roomlist_iface_init
)
4244 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ATTENTION
,
4245 jabber_protocol_attention_iface_init
)
4247 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_MEDIA
,
4248 jabber_protocol_media_iface_init
)
4250 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER
,
4251 jabber_protocol_xfer_iface_init
));
4253 static PurplePluginInfo
*
4254 plugin_query(GError
**error
)
4256 return purple_plugin_info_new(
4258 "name", "XMPP Protocols",
4259 "version", DISPLAY_VERSION
,
4260 "category", N_("Protocol"),
4261 "summary", N_("XMPP and GTalk Protocols Plugin"),
4262 "description", N_("XMPP and GTalk Protocols Plugin"),
4263 "website", PURPLE_WEBSITE
,
4264 "abi-version", PURPLE_ABI_VERSION
,
4265 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
4266 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
4272 plugin_load(PurplePlugin
*plugin
, GError
**error
)
4274 jingle_session_register(plugin
);
4276 jingle_transport_register(plugin
);
4277 jingle_iceudp_register(plugin
);
4278 jingle_rawudp_register(plugin
);
4279 jingle_google_p2p_register(plugin
);
4281 jingle_content_register(plugin
);
4283 jingle_rtp_register(plugin
);
4286 jabber_protocol_register_type(G_TYPE_MODULE(plugin
));
4288 gtalk_protocol_register(plugin
);
4289 xmpp_protocol_register(plugin
);
4291 jabber_si_xfer_register(G_TYPE_MODULE(plugin
));
4293 xmpp_protocol
= purple_protocols_add(XMPP_TYPE_PROTOCOL
, error
);
4297 gtalk_protocol
= purple_protocols_add(GTALK_TYPE_PROTOCOL
, error
);
4298 if (!gtalk_protocol
)
4301 purple_signal_connect(purple_get_core(), "uri-handler", xmpp_protocol
,
4302 PURPLE_CALLBACK(xmpp_uri_handler
), xmpp_protocol
);
4303 purple_signal_connect(purple_get_core(), "uri-handler", gtalk_protocol
,
4304 PURPLE_CALLBACK(xmpp_uri_handler
), gtalk_protocol
);
4306 jabber_init_protocol(xmpp_protocol
);
4307 jabber_init_protocol(gtalk_protocol
);
4313 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
4315 purple_signal_disconnect(purple_get_core(), "uri-handler",
4316 xmpp_protocol
, PURPLE_CALLBACK(xmpp_uri_handler
));
4317 purple_signal_disconnect(purple_get_core(), "uri-handler",
4318 gtalk_protocol
, PURPLE_CALLBACK(xmpp_uri_handler
));
4320 jabber_uninit_protocol(gtalk_protocol
);
4321 jabber_uninit_protocol(xmpp_protocol
);
4323 if (!purple_protocols_remove(gtalk_protocol
, error
))
4326 if (!purple_protocols_remove(xmpp_protocol
, error
))
4332 PURPLE_PLUGIN_INIT(jabber
, plugin_query
, plugin_load
, plugin_unload
);