2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Matt O'Gorman <mogorman@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
20 * \brief A resource for interfacing asterisk directly as a client
21 * or a component to a jabber compliant server.
23 * \extref Iksemel http://code.google.com/p/iksemel/
25 * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
30 <depend>iksemel</depend>
36 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
41 #include "asterisk/channel.h"
42 #include "asterisk/jabber.h"
43 #include "asterisk/file.h"
44 #include "asterisk/config.h"
45 #include "asterisk/callerid.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/cli.h"
48 #include "asterisk/app.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/md5.h"
51 #include "asterisk/acl.h"
52 #include "asterisk/utils.h"
53 #include "asterisk/module.h"
54 #include "asterisk/astobj.h"
55 #include "asterisk/astdb.h"
56 #include "asterisk/manager.h"
58 #define JABBER_CONFIG "jabber.conf"
68 /*-- Forward declarations */
69 static void aji_buddy_destroy(struct aji_buddy
*obj
);
70 static void aji_client_destroy(struct aji_client
*obj
);
71 static int aji_send_exec(struct ast_channel
*chan
, void *data
);
72 static int aji_status_exec(struct ast_channel
*chan
, void *data
);
73 static int aji_is_secure(struct aji_client
*client
);
74 static int aji_start_tls(struct aji_client
*client
);
75 static int aji_tls_handshake(struct aji_client
*client
);
76 static int aji_io_recv(struct aji_client
*client
, char *buffer
, size_t buf_len
, int timeout
);
77 static int aji_recv(struct aji_client
*client
, int timeout
);
78 static int aji_send_header(struct aji_client
*client
, const char *to
);
79 static int aji_send_raw(struct aji_client
*client
, const char *xmlstr
);
80 static void aji_log_hook(void *data
, const char *xmpp
, size_t size
, int is_incoming
);
81 static int aji_start_sasl(struct aji_client
*client
, enum ikssasltype type
, char *username
, char *pass
);
82 static int aji_act_hook(void *data
, int type
, iks
*node
);
83 static void aji_handle_iq(struct aji_client
*client
, iks
*node
);
84 static void aji_handle_message(struct aji_client
*client
, ikspak
*pak
);
85 static void aji_handle_presence(struct aji_client
*client
, ikspak
*pak
);
86 static void aji_handle_subscribe(struct aji_client
*client
, ikspak
*pak
);
87 static void *aji_recv_loop(void *data
);
88 static int aji_initialize(struct aji_client
*client
);
89 static int aji_client_connect(void *data
, ikspak
*pak
);
90 static void aji_set_presence(struct aji_client
*client
, char *to
, char *from
, int level
, char *desc
);
91 static char *aji_do_debug_deprecated(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
);
92 static char *aji_do_set_debug(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
);
93 static char *aji_do_reload(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
);
94 static char *aji_show_clients(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
);
95 static char *aji_show_buddies(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
);
96 static char *aji_test(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
);
97 static int aji_create_client(char *label
, struct ast_variable
*var
, int debug
);
98 static int aji_create_buddy(char *label
, struct aji_client
*client
);
99 static int aji_reload(int reload
);
100 static int aji_load_config(int reload
);
101 static void aji_pruneregister(struct aji_client
*client
);
102 static int aji_filter_roster(void *data
, ikspak
*pak
);
103 static int aji_get_roster(struct aji_client
*client
);
104 static int aji_client_info_handler(void *data
, ikspak
*pak
);
105 static int aji_dinfo_handler(void *data
, ikspak
*pak
);
106 static int aji_ditems_handler(void *data
, ikspak
*pak
);
107 static int aji_register_query_handler(void *data
, ikspak
*pak
);
108 static int aji_register_approve_handler(void *data
, ikspak
*pak
);
109 static int aji_reconnect(struct aji_client
*client
);
110 static iks
*jabber_make_auth(iksid
* id
, const char *pass
, const char *sid
);
111 /* No transports in this version */
113 static int aji_create_transport(char *label, struct aji_client *client);
114 static int aji_register_transport(void *data, ikspak *pak);
115 static int aji_register_transport2(void *data, ikspak *pak);
118 static struct ast_cli_entry cli_aji_do_debug_deprecated
= AST_CLI_DEFINE(aji_do_debug_deprecated
, "Enable/disable jabber debugging");
119 static struct ast_cli_entry aji_cli
[] = {
120 AST_CLI_DEFINE(aji_do_set_debug
, "Enable/Disable Jabber debug", .deprecate_cmd
= &cli_aji_do_debug_deprecated
),
121 AST_CLI_DEFINE(aji_do_reload
, "Reload Jabber configuration"),
122 AST_CLI_DEFINE(aji_show_clients
, "Show state of clients and components"),
123 AST_CLI_DEFINE(aji_show_buddies
, "Show buddy lists of our clients"),
124 AST_CLI_DEFINE(aji_test
, "Shows roster, but is generally used for mog's debugging."),
127 static char *app_ajisend
= "JabberSend";
129 static char *ajisend_synopsis
= "JabberSend(jabber,screenname,message)";
131 static char *ajisend_descrip
=
132 "JabberSend(Jabber,ScreenName,Message)\n"
133 " Jabber - Client or transport Asterisk uses to connect to Jabber\n"
134 " ScreenName - User Name to message.\n"
135 " Message - Message to be sent to the buddy\n";
137 static char *app_ajistatus
= "JabberStatus";
139 static char *ajistatus_synopsis
= "JabberStatus(Jabber,ScreenName,Variable)";
141 static char *ajistatus_descrip
=
142 "JabberStatus(Jabber,ScreenName,Variable)\n"
143 " Jabber - Client or transport Asterisk uses to connect to Jabber\n"
144 " ScreenName - User Name to retrieve status from.\n"
145 " Variable - Variable to store presence in will be 1-6.\n"
146 " In order, Online, Chatty, Away, XAway, DND, Offline\n"
147 " If not in roster variable will = 7\n";
149 struct aji_client_container clients
;
150 struct aji_capabilities
*capabilities
= NULL
;
152 /*! \brief Global flags, initialized to default values */
153 static struct ast_flags globalflags
= { AJI_AUTOPRUNE
| AJI_AUTOREGISTER
};
156 * \brief Deletes the aji_client data structure.
157 * \param obj aji_client The structure we will delete.
160 static void aji_client_destroy(struct aji_client
*obj
)
162 struct aji_message
*tmp
;
163 ASTOBJ_CONTAINER_DESTROYALL(&obj
->buddies
, aji_buddy_destroy
);
164 ASTOBJ_CONTAINER_DESTROY(&obj
->buddies
);
165 iks_filter_delete(obj
->f
);
166 iks_parser_delete(obj
->p
);
167 iks_stack_delete(obj
->stack
);
168 AST_LIST_LOCK(&obj
->messages
);
169 while ((tmp
= AST_LIST_REMOVE_HEAD(&obj
->messages
, list
))) {
173 ast_free(tmp
->message
);
175 AST_LIST_HEAD_DESTROY(&obj
->messages
);
180 * \brief Deletes the aji_buddy data structure.
181 * \param obj aji_buddy The structure we will delete.
184 static void aji_buddy_destroy(struct aji_buddy
*obj
)
186 struct aji_resource
*tmp
;
188 while ((tmp
= obj
->resources
)) {
189 obj
->resources
= obj
->resources
->next
;
190 ast_free(tmp
->description
);
198 * \brief Find version in XML stream and populate our capabilities list
199 * \param node the node attribute in the caps element we'll look for or add to
201 * \param version the version attribute in the caps element we'll look for or
203 * \param pak struct The XML stanza we're processing
204 * \return a pointer to the added or found aji_version structure
206 static struct aji_version
*aji_find_version(char *node
, char *version
, ikspak
*pak
)
208 struct aji_capabilities
*list
= NULL
;
209 struct aji_version
*res
= NULL
;
214 node
= pak
->from
->full
;
216 version
= "none supplied.";
218 if(!strcasecmp(list
->node
, node
)) {
219 res
= list
->versions
;
221 if(!strcasecmp(res
->version
, version
))
225 /* Specified version not found. Let's add it to
226 this node in our capabilities list */
228 res
= ast_malloc(sizeof(*res
));
230 ast_log(LOG_ERROR
, "Out of memory!\n");
235 ast_copy_string(res
->version
, version
, sizeof(res
->version
));
236 res
->next
= list
->versions
;
237 list
->versions
= res
;
243 /* Specified node not found. Let's add it our capabilities list */
245 list
= ast_malloc(sizeof(*list
));
247 ast_log(LOG_ERROR
, "Out of memory!\n");
250 res
= ast_malloc(sizeof(*res
));
252 ast_log(LOG_ERROR
, "Out of memory!\n");
256 ast_copy_string(list
->node
, node
, sizeof(list
->node
));
257 ast_copy_string(res
->version
, version
, sizeof(res
->version
));
261 list
->versions
= res
;
262 list
->next
= capabilities
;
268 * \brief Find the aji_resource we want
269 * \param buddy aji_buddy A buddy
271 * \return aji_resource object
273 static struct aji_resource
*aji_find_resource(struct aji_buddy
*buddy
, char *name
)
275 struct aji_resource
*res
= NULL
;
278 res
= buddy
->resources
;
280 if (!strcasecmp(res
->resource
, name
)) {
289 * \brief Jabber GTalk function
291 * \return 1 on success, 0 on failure.
293 static int gtalk_yuck(iks
*node
)
295 if (iks_find_with_attrib(node
, "c", "node", "http://www.google.com/xmpp/client/caps"))
301 * \brief Setup the authentication struct
303 * \param pass password
307 static iks
*jabber_make_auth(iksid
* id
, const char *pass
, const char *sid
)
311 iks_insert_attrib(x
, "type", "set");
312 y
= iks_insert(x
, "query");
313 iks_insert_attrib(y
, "xmlns", IKS_NS_AUTH
);
314 iks_insert_cdata(iks_insert(y
, "username"), id
->user
, 0);
315 iks_insert_cdata(iks_insert(y
, "resource"), id
->resource
, 0);
319 snprintf(sidpass
, sizeof(sidpass
), "%s%s", sid
, pass
);
320 ast_sha1_hash(buf
, sidpass
);
321 iks_insert_cdata(iks_insert(y
, "digest"), buf
, 0);
323 iks_insert_cdata(iks_insert(y
, "password"), pass
, 0);
329 * \brief Dial plan function status(). puts the status of watched user
330 into a channel variable.
331 * \param chan ast_channel
333 * \return 0 on success, -1 on error
335 static int aji_status_exec(struct ast_channel
*chan
, void *data
)
337 struct aji_client
*client
= NULL
;
338 struct aji_buddy
*buddy
= NULL
;
339 struct aji_resource
*r
= NULL
;
343 static int deprecation_warning
= 0;
344 AST_DECLARE_APP_ARGS(args
,
347 AST_APP_ARG(variable
);
349 AST_DECLARE_APP_ARGS(jid
,
350 AST_APP_ARG(screenname
);
351 AST_APP_ARG(resource
);
354 if (deprecation_warning
++ % 10 == 0)
355 ast_log(LOG_WARNING
, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
358 ast_log(LOG_ERROR
, "Usage: JabberStatus(<sender>,<screenname>[/<resource>],<varname>\n");
361 s
= ast_strdupa(data
);
362 AST_STANDARD_APP_ARGS(args
, s
);
364 if (args
.argc
!= 3) {
365 ast_log(LOG_ERROR
, "JabberStatus() requires 3 arguments.\n");
369 AST_NONSTANDARD_APP_ARGS(jid
, args
.jid
, '/');
371 if (!(client
= ast_aji_get_client(args
.sender
))) {
372 ast_log(LOG_WARNING
, "Could not find sender connection: '%s'\n", args
.sender
);
375 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, jid
.screenname
);
377 ast_log(LOG_WARNING
, "Could not find buddy in list: '%s'\n", jid
.screenname
);
380 r
= aji_find_resource(buddy
, jid
.resource
);
381 if (!r
&& buddy
->resources
)
382 r
= buddy
->resources
;
384 ast_log(LOG_NOTICE
, "Resource '%s' of buddy '%s' was not found\n", jid
.resource
, jid
.screenname
);
387 snprintf(status
, sizeof(status
), "%d", stat
);
388 pbx_builtin_setvar_helper(chan
, args
.variable
, status
);
392 static int acf_jabberstatus_read(struct ast_channel
*chan
, const char *name
, char *data
, char *buf
, size_t buflen
)
394 struct aji_client
*client
= NULL
;
395 struct aji_buddy
*buddy
= NULL
;
396 struct aji_resource
*r
= NULL
;
398 AST_DECLARE_APP_ARGS(args
,
402 AST_DECLARE_APP_ARGS(jid
,
403 AST_APP_ARG(screenname
);
404 AST_APP_ARG(resource
);
408 ast_log(LOG_ERROR
, "Usage: JABBER_STATUS(<sender>,<screenname>[/<resource>])\n");
411 AST_STANDARD_APP_ARGS(args
, data
);
413 if (args
.argc
!= 2) {
414 ast_log(LOG_ERROR
, "JABBER_STATUS requires 2 arguments.\n");
418 AST_NONSTANDARD_APP_ARGS(jid
, args
.jid
, '/');
420 if (!(client
= ast_aji_get_client(args
.sender
))) {
421 ast_log(LOG_WARNING
, "Could not find sender connection: '%s'\n", args
.sender
);
424 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, jid
.screenname
);
426 ast_log(LOG_WARNING
, "Could not find buddy in list: '%s'\n", jid
.screenname
);
429 r
= aji_find_resource(buddy
, jid
.resource
);
430 if (!r
&& buddy
->resources
)
431 r
= buddy
->resources
;
433 ast_log(LOG_NOTICE
, "Resource %s of buddy %s was not found.\n", jid
.resource
, jid
.screenname
);
436 snprintf(buf
, buflen
, "%d", stat
);
440 static struct ast_custom_function jabberstatus_function
= {
441 .name
= "JABBER_STATUS",
442 .synopsis
= "Retrieve buddy status",
443 .syntax
= "JABBER_STATUS(<sender>,<buddy>[/<resource>])",
444 .read
= acf_jabberstatus_read
,
446 "Retrieves the numeric status associated with the specified buddy. If the\n"
447 "buddy does not exist in the buddylist, returns 7.\n",
451 * \brief Dial plan function to send a message.
452 * \param chan ast_channel
453 * \param data Data is sender|reciever|message.
454 * \return 0 on success,-1 on error.
456 static int aji_send_exec(struct ast_channel
*chan
, void *data
)
458 struct aji_client
*client
= NULL
;
460 AST_DECLARE_APP_ARGS(args
,
462 AST_APP_ARG(recipient
);
463 AST_APP_ARG(message
);
467 ast_log(LOG_ERROR
, "Usage: JabberSend(<sender>,<recipient>,<message>)\n");
470 s
= ast_strdupa(data
);
472 AST_STANDARD_APP_ARGS(args
, s
);
474 ast_log(LOG_ERROR
, "JabberSend requires 3 arguments: '%s'\n", (char *) data
);
478 if (!(client
= ast_aji_get_client(args
.sender
))) {
479 ast_log(LOG_WARNING
, "Could not find sender connection: '%s'\n", args
.sender
);
482 if (strchr(args
.recipient
, '@') && !ast_strlen_zero(args
.message
))
483 ast_aji_send_chat(client
, args
.recipient
, args
.message
);
488 * \brief Tests whether the connection is secured or not
489 * \return 0 if the connection is not secured
491 static int aji_is_secure(struct aji_client
*client
)
494 return client
->stream_flags
& SECURE
;
502 * \brief Starts the TLS procedure
503 * \param client the configured XMPP client we use to connect to a XMPP server
504 * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
505 * if OpenSSL is not installed
507 static int aji_start_tls(struct aji_client
*client
)
511 return IKS_NET_TLSFAIL
;
513 /* This is sent not encrypted */
514 ret
= iks_send_raw(client
->p
, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
517 client
->stream_flags
|= TRY_SECURE
;
523 * \brief TLS handshake, OpenSSL initialization
524 * \param client the configured XMPP client we use to connect to a XMPP server
525 * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
527 static int aji_tls_handshake(struct aji_client
*client
)
533 return IKS_NET_TLSFAIL
;
536 ast_debug(1, "Starting TLS handshake\n");
538 /* Load encryption, hashing algorithms and error strings */
540 SSL_load_error_strings();
542 /* Choose an SSL/TLS protocol version, create SSL_CTX */
543 client
->ssl_method
= SSLv3_method();
544 client
->ssl_context
= SSL_CTX_new(client
->ssl_method
);
545 if (!client
->ssl_context
)
546 return IKS_NET_TLSFAIL
;
548 /* Create new SSL session */
549 client
->ssl_session
= SSL_new(client
->ssl_context
);
550 if (!client
->ssl_session
)
551 return IKS_NET_TLSFAIL
;
553 /* Enforce TLS on our XMPP connection */
554 sock
= iks_fd(client
->p
);
555 ret
= SSL_set_fd(client
->ssl_session
, sock
);
557 return IKS_NET_TLSFAIL
;
559 /* Perform SSL handshake */
560 ret
= SSL_connect(client
->ssl_session
);
562 return IKS_NET_TLSFAIL
;
564 client
->stream_flags
&= (~TRY_SECURE
);
565 client
->stream_flags
|= SECURE
;
567 /* Sent over the established TLS connection */
568 ret
= aji_send_header(client
, client
->jid
->server
);
570 return IKS_NET_TLSFAIL
;
572 ast_debug(1, "TLS started with server\n");
578 * \brief Secured or unsecured IO socket receiving function
579 * \param client the configured XMPP client we use to connect to a XMPP server
580 * \param buffer the reception buffer
581 * \param buf_len the size of the buffer
582 * \param timeout the select timer
583 * \return the number of read bytes on success, 0 on timeout expiration,
586 static int aji_io_recv(struct aji_client
*client
, char *buffer
, size_t buf_len
, int timeout
)
590 struct timeval tv
, *tvptr
= NULL
;
594 if (aji_is_secure(client
)) {
595 sock
= SSL_get_fd(client
->ssl_session
);
599 #endif /* HAVE_OPENSSL */
600 sock
= iks_fd(client
->p
);
602 memset(&tv
, 0, sizeof(struct timeval
));
607 /* NULL value for tvptr makes ast_select wait indefinitely */
608 tvptr
= (timeout
!= -1) ? &tv
: NULL
;
610 /* ast_select emulates linux behaviour in terms of timeout handling */
611 res
= ast_select(sock
+ 1, &fds
, NULL
, NULL
, tvptr
);
614 if (aji_is_secure(client
)) {
615 len
= SSL_read(client
->ssl_session
, buffer
, buf_len
);
617 #endif /* HAVE_OPENSSL */
618 len
= recv(sock
, buffer
, buf_len
, 0);
622 } else if (len
<= 0) {
630 * \brief Tries to receive data from the Jabber server
631 * \param client the configured XMPP client we use to connect to a XMPP server
632 * \param timeout the timeout value
633 * This function receives (encrypted or unencrypted) data from the XMPP server,
634 * and passes it to the parser.
635 * \return IKS_OK on success, IKS_NET_RWERR on IO error, IKS_NET_NOCONN, if no
636 * connection available, IKS_NET_EXPIRED on timeout expiration
638 static int aji_recv (struct aji_client
*client
, int timeout
)
641 char buf
[NET_IO_BUF_SIZE
-1];
642 char newbuf
[NET_IO_BUF_SIZE
-1];
647 memset(buf
, 0, sizeof(buf
));
648 memset(newbuf
, 0, sizeof(newbuf
));
651 len
= aji_io_recv(client
, buf
, NET_IO_BUF_SIZE
- 1, timeout
);
652 if (len
< 0) return IKS_NET_RWERR
;
653 if (len
== 0) return IKS_NET_EXPIRED
;
656 /* our iksemel parser won't work as expected if we feed
657 it with XML packets that contain multiple whitespace
658 characters between tags */
661 /* if we stumble on the ending tag character,
662 we skip any whitespace that follows it*/
664 while (isspace(buf
[pos
+1])) {
668 newbuf
[newbufpos
] = c
;
675 /* Log the message here, because iksemel's logHook is
677 aji_log_hook(client
, buf
, len
, 1);
679 /* let iksemel deal with the string length,
680 and reset our buffer */
681 ret
= iks_parse(client
->p
, newbuf
, 0, 0);
682 memset(newbuf
, 0, sizeof(newbuf
));
685 ast_log(LOG_WARNING
, "XML parsing failed\n");
688 ast_debug(3, "XML parsing successful\n");
694 * \brief Sends XMPP header to the server
695 * \param client the configured XMPP client we use to connect to a XMPP server
696 * \param to the target XMPP server
697 * \return IKS_OK on success, any other value on failure
699 static int aji_send_header(struct aji_client
*client
, const char *to
)
704 len
= 91 + strlen(client
->name_space
) + 6 + strlen(to
) + 16 + 1;
705 msg
= iks_malloc(len
);
708 sprintf(msg
, "<?xml version='1.0'?>"
709 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
710 "%s' to='%s' version='1.0'>", client
->name_space
, to
);
711 err
= aji_send_raw(client
, msg
);
720 * \brief Wraps raw sending
721 * \param client the configured XMPP client we use to connect to a XMPP server
722 * \param x the XMPP packet to send
723 * \return IKS_OK on success, any other value on failure
725 int ast_aji_send(struct aji_client
*client
, iks
*x
)
727 return aji_send_raw(client
, iks_string(iks_stack(x
), x
));
731 * \brief Sends an XML string over an XMPP connection
732 * \param client the configured XMPP client we use to connect to a XMPP server
733 * \param xmlstr the XML string to send
734 * The XML data is sent whether the connection is secured or not. In the
735 * latter case, we just call iks_send_raw().
736 * \return IKS_OK on success, any other value on failure
738 static int aji_send_raw(struct aji_client
*client
, const char *xmlstr
)
742 int len
= strlen(xmlstr
);
744 if (aji_is_secure(client
)) {
745 ret
= SSL_write(client
->ssl_session
, xmlstr
, len
);
747 /* Log the message here, because iksemel's logHook is
749 aji_log_hook(client
, xmlstr
, len
, 0);
754 /* If needed, data will be sent unencrypted, and logHook will
755 be called inside iks_send_raw */
756 ret
= iks_send_raw(client
->p
, xmlstr
);
764 * \brief the debug loop.
766 * \param xmpp xml data as string
767 * \param size size of string
768 * \param is_incoming direction of packet 1 for inbound 0 for outbound.
770 static void aji_log_hook(void *data
, const char *xmpp
, size_t size
, int is_incoming
)
772 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
774 if (!ast_strlen_zero(xmpp
))
775 manager_event(EVENT_FLAG_USER
, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client
->name
, xmpp
);
779 ast_verbose("\nJABBER: %s INCOMING: %s\n", client
->name
, xmpp
);
781 if( strlen(xmpp
) == 1) {
782 if(option_debug
> 2 && xmpp
[0] == ' ')
783 ast_verbose("\nJABBER: Keep alive packet\n");
785 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client
->name
, xmpp
);
789 ASTOBJ_UNREF(client
, aji_client_destroy
);
793 * \brief A wrapper function for iks_start_sasl
794 * \param client the configured XMPP client we use to connect to a XMPP server
795 * \param type the SASL authentication type. Supported types are PLAIN and MD5
797 * \param pass password.
799 * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
801 static int aji_start_sasl(struct aji_client
*client
, enum ikssasltype type
, char *username
, char *pass
)
808 /* trigger SASL DIGEST-MD5 only over an unsecured connection.
809 iks_start_sasl is an iksemel API function and relies on GnuTLS,
810 whereas we use OpenSSL */
811 if ((type
& IKS_STREAM_SASL_MD5
) && !aji_is_secure(client
))
812 return iks_start_sasl(client
->p
, IKS_SASL_DIGEST_MD5
, username
, pass
);
813 if (!(type
& IKS_STREAM_SASL_PLAIN
)) {
814 ast_log(LOG_ERROR
, "Server does not support SASL PLAIN authentication\n");
815 return IKS_NET_NOTSUPP
;
820 ast_log(LOG_ERROR
, "Out of memory.\n");
821 return IKS_NET_NOTSUPP
;
824 iks_insert_attrib(x
, "xmlns", IKS_NS_XMPP_SASL
);
825 len
= strlen(username
) + strlen(pass
) + 3;
827 base64
= alloca((len
+ 2) * 4 / 3);
828 iks_insert_attrib(x
, "mechanism", "PLAIN");
829 snprintf(s
, len
, "%c%s%c%s", 0, username
, 0, pass
);
831 /* exclude the NULL training byte from the base64 encoding operation
832 as some XMPP servers will refuse it.
833 The format for authentication is [authzid]\0authcid\0password
834 not [authzid]\0authcid\0password\0 */
835 ast_base64encode(base64
, (const unsigned char *) s
, len
- 1, (len
+ 2) * 4 / 3);
836 iks_insert_cdata(x
, base64
, 0);
837 ast_aji_send(client
, x
);
844 * \brief The action hook parses the inbound packets, constantly running.
845 * \param data aji client structure
846 * \param type type of packet
847 * \param node the actual packet.
848 * \return IKS_OK or IKS_HOOK .
850 static int aji_act_hook(void *data
, int type
, iks
*node
)
852 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
858 ast_log(LOG_ERROR
, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
859 ASTOBJ_UNREF(client
, aji_client_destroy
);
863 if (client
->state
== AJI_DISCONNECTING
) {
864 ASTOBJ_UNREF(client
, aji_client_destroy
);
868 pak
= iks_packet(node
);
870 if (!client
->component
) { /*client */
873 if (client
->usetls
&& !aji_is_secure(client
)) {
874 if (aji_start_tls(client
) == IKS_NET_TLSFAIL
) {
875 ast_log(LOG_ERROR
, "OpenSSL not installed. You need to install OpenSSL on this system\n");
876 ASTOBJ_UNREF(client
, aji_client_destroy
);
882 if (!client
->usesasl
) {
883 iks_filter_add_rule(client
->f
, aji_client_connect
, client
, IKS_RULE_TYPE
, IKS_PAK_IQ
, IKS_RULE_SUBTYPE
, IKS_TYPE_RESULT
, IKS_RULE_ID
, client
->mid
, IKS_RULE_DONE
);
884 auth
= jabber_make_auth(client
->jid
, client
->password
, iks_find_attrib(node
, "id"));
886 iks_insert_attrib(auth
, "id", client
->mid
);
887 iks_insert_attrib(auth
, "to", client
->jid
->server
);
888 ast_aji_increment_mid(client
->mid
);
889 ast_aji_send(client
, auth
);
892 ast_log(LOG_ERROR
, "Out of memory.\n");
896 case IKS_NODE_NORMAL
:
897 if (client
->stream_flags
& TRY_SECURE
) {
898 if (!strcmp("proceed", iks_name(node
))) {
899 return aji_tls_handshake(client
);
903 if (!strcmp("stream:features", iks_name(node
))) {
904 features
= iks_stream_features(node
);
905 if (client
->usesasl
) {
906 if (client
->usetls
&& !aji_is_secure(client
))
908 if (client
->authorized
) {
909 if (features
& IKS_STREAM_BIND
) {
910 iks_filter_add_rule(client
->f
, aji_client_connect
, client
, IKS_RULE_TYPE
, IKS_PAK_IQ
, IKS_RULE_SUBTYPE
, IKS_TYPE_RESULT
, IKS_RULE_DONE
);
911 auth
= iks_make_resource_bind(client
->jid
);
913 iks_insert_attrib(auth
, "id", client
->mid
);
914 ast_aji_increment_mid(client
->mid
);
915 ast_aji_send(client
, auth
);
918 ast_log(LOG_ERROR
, "Out of memory.\n");
922 if (features
& IKS_STREAM_SESSION
) {
923 iks_filter_add_rule (client
->f
, aji_client_connect
, client
, IKS_RULE_TYPE
, IKS_PAK_IQ
, IKS_RULE_SUBTYPE
, IKS_TYPE_RESULT
, IKS_RULE_ID
, "auth", IKS_RULE_DONE
);
924 auth
= iks_make_session();
926 iks_insert_attrib(auth
, "id", "auth");
927 ast_aji_increment_mid(client
->mid
);
928 ast_aji_send(client
, auth
);
931 ast_log(LOG_ERROR
, "Out of memory.\n");
936 if (!client
->jid
->user
) {
937 ast_log(LOG_ERROR
, "Malformed Jabber ID : %s (domain missing?)\n", client
->jid
->full
);
941 ret
= aji_start_sasl(client
, features
, client
->jid
->user
, client
->password
);
943 ASTOBJ_UNREF(client
, aji_client_destroy
);
949 } else if (!strcmp("failure", iks_name(node
))) {
950 ast_log(LOG_ERROR
, "JABBER: encryption failure. possible bad password.\n");
951 } else if (!strcmp("success", iks_name(node
))) {
952 client
->authorized
= 1;
953 aji_send_header(client
, client
->jid
->server
);
957 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
958 ASTOBJ_UNREF(client
, aji_client_destroy
);
962 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
963 ASTOBJ_UNREF(client
, aji_client_destroy
);
967 } else if (client
->state
!= AJI_CONNECTED
&& client
->component
) {
970 if (client
->state
== AJI_DISCONNECTED
) {
971 char secret
[160], shasum
[320], *handshake
;
973 sprintf(secret
, "%s%s", pak
->id
, client
->password
);
974 ast_sha1_hash(shasum
, secret
);
976 asprintf(&handshake
, "<handshake>%s</handshake>", shasum
);
978 aji_send_raw(client
, handshake
);
982 client
->state
= AJI_CONNECTING
;
983 if(aji_recv(client
, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
984 client
->state
= AJI_CONNECTED
;
986 ast_log(LOG_WARNING
, "Jabber didn't seem to handshake, failed to authenticate.\n");
991 case IKS_NODE_NORMAL
:
995 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
996 ASTOBJ_UNREF(client
, aji_client_destroy
);
1000 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
1001 ASTOBJ_UNREF(client
, aji_client_destroy
);
1006 switch (pak
->type
) {
1008 ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1010 case IKS_PAK_MESSAGE
:
1011 aji_handle_message(client
, pak
);
1012 ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1014 case IKS_PAK_PRESENCE
:
1015 aji_handle_presence(client
, pak
);
1016 ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1019 aji_handle_subscribe(client
, pak
);
1020 ast_debug(1, "JABBER: Handling paktype S10N\n");
1023 ast_debug(1, "JABBER: Handling paktype IQ\n");
1024 aji_handle_iq(client
, node
);
1027 ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak
->type
);
1031 iks_filter_packet(client
->f
, pak
);
1036 ASTOBJ_UNREF(client
, aji_client_destroy
);
1043 * \return IKS_FILTER_EAT.
1045 static int aji_register_approve_handler(void *data
, ikspak
*pak
)
1047 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1048 iks
*iq
= NULL
, *presence
= NULL
, *x
= NULL
;
1051 presence
= iks_new("presence");
1053 if (client
&& iq
&& presence
&& x
) {
1054 if (!iks_find(pak
->query
, "remove")) {
1055 iks_insert_attrib(iq
, "from", client
->jid
->full
);
1056 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1057 iks_insert_attrib(iq
, "id", pak
->id
);
1058 iks_insert_attrib(iq
, "type", "result");
1059 ast_aji_send(client
, iq
);
1061 iks_insert_attrib(presence
, "from", client
->jid
->full
);
1062 iks_insert_attrib(presence
, "to", pak
->from
->partial
);
1063 iks_insert_attrib(presence
, "id", client
->mid
);
1064 ast_aji_increment_mid(client
->mid
);
1065 iks_insert_attrib(presence
, "type", "subscribe");
1066 iks_insert_attrib(x
, "xmlns", "vcard-temp:x:update");
1067 iks_insert_node(presence
, x
);
1068 ast_aji_send(client
, presence
);
1071 ast_log(LOG_ERROR
, "Out of memory.\n");
1076 iks_delete(presence
);
1079 ASTOBJ_UNREF(client
, aji_client_destroy
);
1080 return IKS_FILTER_EAT
;
1083 * \brief register query
1084 * \param data incoming aji_client request
1086 * \return IKS_FILTER_EAT.
1088 static int aji_register_query_handler(void *data
, ikspak
*pak
)
1090 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1091 struct aji_buddy
*buddy
= NULL
;
1094 client
= (struct aji_client
*) data
;
1096 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1098 iks
*iq
= NULL
, *query
= NULL
, *error
= NULL
, *notacceptable
= NULL
;
1100 ast_verbose("Someone.... %s tried to register but they aren't allowed\n", pak
->from
->partial
);
1102 query
= iks_new("query");
1103 error
= iks_new("error");
1104 notacceptable
= iks_new("not-acceptable");
1105 if(iq
&& query
&& error
&& notacceptable
) {
1106 iks_insert_attrib(iq
, "type", "error");
1107 iks_insert_attrib(iq
, "from", client
->user
);
1108 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1109 iks_insert_attrib(iq
, "id", pak
->id
);
1110 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
1111 iks_insert_attrib(error
, "code" , "406");
1112 iks_insert_attrib(error
, "type", "modify");
1113 iks_insert_attrib(notacceptable
, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1114 iks_insert_node(iq
, query
);
1115 iks_insert_node(iq
, error
);
1116 iks_insert_node(error
, notacceptable
);
1117 ast_aji_send(client
, iq
);
1119 ast_log(LOG_ERROR
, "Out of memory.\n");
1125 iks_delete(notacceptable
);
1126 } else if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
1127 iks
*iq
= NULL
, *query
= NULL
, *instructions
= NULL
;
1128 char *explain
= "Welcome to Asterisk - the Open Source PBX.\n";
1130 query
= iks_new("query");
1131 instructions
= iks_new("instructions");
1132 if (iq
&& query
&& instructions
&& client
) {
1133 iks_insert_attrib(iq
, "from", client
->user
);
1134 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1135 iks_insert_attrib(iq
, "id", pak
->id
);
1136 iks_insert_attrib(iq
, "type", "result");
1137 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
1138 iks_insert_cdata(instructions
, explain
, 0);
1139 iks_insert_node(iq
, query
);
1140 iks_insert_node(query
, instructions
);
1141 ast_aji_send(client
, iq
);
1143 ast_log(LOG_ERROR
, "Out of memory.\n");
1148 iks_delete(instructions
);
1150 ASTOBJ_UNREF(client
, aji_client_destroy
);
1151 return IKS_FILTER_EAT
;
1155 * \brief Handles stuff
1158 * \return IKS_FILTER_EAT.
1160 static int aji_ditems_handler(void *data
, ikspak
*pak
)
1162 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1165 if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
1166 iks
*iq
= NULL
, *query
= NULL
, *item
= NULL
;
1168 query
= iks_new("query");
1169 item
= iks_new("item");
1171 if (iq
&& query
&& item
) {
1172 iks_insert_attrib(iq
, "from", client
->user
);
1173 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1174 iks_insert_attrib(iq
, "id", pak
->id
);
1175 iks_insert_attrib(iq
, "type", "result");
1176 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
1177 iks_insert_attrib(item
, "node", "http://jabber.org/protocol/commands");
1178 iks_insert_attrib(item
, "name", "Million Dollar Asterisk Commands");
1179 iks_insert_attrib(item
, "jid", client
->user
);
1181 iks_insert_node(iq
, query
);
1182 iks_insert_node(query
, item
);
1183 ast_aji_send(client
, iq
);
1185 ast_log(LOG_ERROR
, "Out of memory.\n");
1192 } else if (!strcasecmp(node
, "http://jabber.org/protocol/commands")) {
1193 iks
*iq
, *query
, *confirm
;
1195 query
= iks_new("query");
1196 confirm
= iks_new("item");
1197 if (iq
&& query
&& confirm
&& client
) {
1198 iks_insert_attrib(iq
, "from", client
->user
);
1199 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1200 iks_insert_attrib(iq
, "id", pak
->id
);
1201 iks_insert_attrib(iq
, "type", "result");
1202 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
1203 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
1204 iks_insert_attrib(confirm
, "node", "confirmaccount");
1205 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
1206 iks_insert_attrib(confirm
, "jid", "blog.astjab.org");
1208 iks_insert_node(iq
, query
);
1209 iks_insert_node(query
, confirm
);
1210 ast_aji_send(client
, iq
);
1212 ast_log(LOG_ERROR
, "Out of memory.\n");
1217 iks_delete(confirm
);
1219 } else if (!strcasecmp(node
, "confirmaccount")) {
1220 iks
*iq
= NULL
, *query
= NULL
, *feature
= NULL
;
1223 query
= iks_new("query");
1224 feature
= iks_new("feature");
1226 if (iq
&& query
&& feature
&& client
) {
1227 iks_insert_attrib(iq
, "from", client
->user
);
1228 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1229 iks_insert_attrib(iq
, "id", pak
->id
);
1230 iks_insert_attrib(iq
, "type", "result");
1231 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
1232 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
1233 iks_insert_node(iq
, query
);
1234 iks_insert_node(query
, feature
);
1235 ast_aji_send(client
, iq
);
1237 ast_log(LOG_ERROR
, "Out of memory.\n");
1242 iks_delete(feature
);
1245 ASTOBJ_UNREF(client
, aji_client_destroy
);
1246 return IKS_FILTER_EAT
;
1250 * \brief Handle add extra info
1253 * \return IKS_FILTER_EAT
1255 static int aji_client_info_handler(void *data
, ikspak
*pak
)
1257 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1258 struct aji_resource
*resource
= NULL
;
1259 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1261 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
1262 if (pak
->subtype
== IKS_TYPE_RESULT
) {
1264 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
1265 ASTOBJ_UNREF(client
, aji_client_destroy
);
1266 return IKS_FILTER_EAT
;
1268 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
1269 resource
->cap
->jingle
= 1;
1271 resource
->cap
->jingle
= 0;
1272 } else if (pak
->subtype
== IKS_TYPE_GET
) {
1273 iks
*iq
, *disco
, *ident
, *google
, *query
;
1275 query
= iks_new("query");
1276 ident
= iks_new("identity");
1277 disco
= iks_new("feature");
1278 google
= iks_new("feature");
1279 if (iq
&& ident
&& disco
&& google
) {
1280 iks_insert_attrib(iq
, "from", client
->jid
->full
);
1281 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1282 iks_insert_attrib(iq
, "type", "result");
1283 iks_insert_attrib(iq
, "id", pak
->id
);
1284 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1285 iks_insert_attrib(ident
, "category", "client");
1286 iks_insert_attrib(ident
, "type", "pc");
1287 iks_insert_attrib(ident
, "name", "asterisk");
1288 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco#info");
1289 iks_insert_attrib(google
, "var", "http://www.google.com/xmpp/protocol/voice/v1");
1290 iks_insert_node(iq
, query
);
1291 iks_insert_node(query
, ident
);
1292 iks_insert_node(query
, google
);
1293 iks_insert_node(query
, disco
);
1294 ast_aji_send(client
, iq
);
1296 ast_log(LOG_ERROR
, "Out of Memory.\n");
1303 } else if (pak
->subtype
== IKS_TYPE_ERROR
) {
1304 ast_log(LOG_NOTICE
, "User %s does not support discovery.\n", pak
->from
->full
);
1306 ASTOBJ_UNREF(client
, aji_client_destroy
);
1307 return IKS_FILTER_EAT
;
1310 * \brief Handler of the return info packet
1311 * \param data aji_client
1313 * \return IKS_FILTER_EAT
1315 static int aji_dinfo_handler(void *data
, ikspak
*pak
)
1317 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1319 struct aji_resource
*resource
= NULL
;
1320 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1322 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
1323 if (pak
->subtype
== IKS_TYPE_ERROR
) {
1324 ast_log(LOG_WARNING
, "Recieved error from a client, turn on jabber debug!\n");
1325 return IKS_FILTER_EAT
;
1327 if (pak
->subtype
== IKS_TYPE_RESULT
) {
1329 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
1330 ASTOBJ_UNREF(client
, aji_client_destroy
);
1331 return IKS_FILTER_EAT
;
1333 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
1334 resource
->cap
->jingle
= 1;
1336 resource
->cap
->jingle
= 0;
1337 } else if (pak
->subtype
== IKS_TYPE_GET
&& !(node
= iks_find_attrib(pak
->query
, "node"))) {
1338 iks
*iq
, *query
, *identity
, *disco
, *reg
, *commands
, *gateway
, *version
, *vcard
, *search
;
1341 query
= iks_new("query");
1342 identity
= iks_new("identity");
1343 disco
= iks_new("feature");
1344 reg
= iks_new("feature");
1345 commands
= iks_new("feature");
1346 gateway
= iks_new("feature");
1347 version
= iks_new("feature");
1348 vcard
= iks_new("feature");
1349 search
= iks_new("feature");
1351 if (iq
&& query
&& identity
&& disco
&& reg
&& commands
&& gateway
&& version
&& vcard
&& search
&& client
) {
1352 iks_insert_attrib(iq
, "from", client
->user
);
1353 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1354 iks_insert_attrib(iq
, "id", pak
->id
);
1355 iks_insert_attrib(iq
, "type", "result");
1356 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1357 iks_insert_attrib(identity
, "category", "gateway");
1358 iks_insert_attrib(identity
, "type", "pstn");
1359 iks_insert_attrib(identity
, "name", "Asterisk The Open Source PBX");
1360 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco");
1361 iks_insert_attrib(reg
, "var", "jabber:iq:register");
1362 iks_insert_attrib(commands
, "var", "http://jabber.org/protocol/commands");
1363 iks_insert_attrib(gateway
, "var", "jabber:iq:gateway");
1364 iks_insert_attrib(version
, "var", "jabber:iq:version");
1365 iks_insert_attrib(vcard
, "var", "vcard-temp");
1366 iks_insert_attrib(search
, "var", "jabber:iq:search");
1368 iks_insert_node(iq
, query
);
1369 iks_insert_node(query
, identity
);
1370 iks_insert_node(query
, disco
);
1371 iks_insert_node(query
, reg
);
1372 iks_insert_node(query
, commands
);
1373 iks_insert_node(query
, gateway
);
1374 iks_insert_node(query
, version
);
1375 iks_insert_node(query
, vcard
);
1376 iks_insert_node(query
, search
);
1377 ast_aji_send(client
, iq
);
1379 ast_log(LOG_ERROR
, "Out of memory.\n");
1384 iks_delete(identity
);
1387 iks_delete(commands
);
1388 iks_delete(gateway
);
1389 iks_delete(version
);
1393 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "http://jabber.org/protocol/commands")) {
1394 iks
*iq
, *query
, *confirm
;
1396 query
= iks_new("query");
1397 confirm
= iks_new("item");
1399 if (iq
&& query
&& confirm
&& client
) {
1400 iks_insert_attrib(iq
, "from", client
->user
);
1401 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1402 iks_insert_attrib(iq
, "id", pak
->id
);
1403 iks_insert_attrib(iq
, "type", "result");
1404 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
1405 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
1406 iks_insert_attrib(confirm
, "node", "confirmaccount");
1407 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
1408 iks_insert_attrib(confirm
, "jid", client
->user
);
1409 iks_insert_node(iq
, query
);
1410 iks_insert_node(query
, confirm
);
1411 ast_aji_send(client
, iq
);
1413 ast_log(LOG_ERROR
, "Out of memory.\n");
1418 iks_delete(confirm
);
1420 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "confirmaccount")) {
1421 iks
*iq
, *query
, *feature
;
1424 query
= iks_new("query");
1425 feature
= iks_new("feature");
1427 if (iq
&& query
&& feature
&& client
) {
1428 iks_insert_attrib(iq
, "from", client
->user
);
1429 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1430 iks_insert_attrib(iq
, "id", pak
->id
);
1431 iks_insert_attrib(iq
, "type", "result");
1432 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1433 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
1434 iks_insert_node(iq
, query
);
1435 iks_insert_node(query
, feature
);
1436 ast_aji_send(client
, iq
);
1438 ast_log(LOG_ERROR
, "Out of memory.\n");
1443 iks_delete(feature
);
1446 ASTOBJ_UNREF(client
, aji_client_destroy
);
1447 return IKS_FILTER_EAT
;
1451 * \brief Handles \verbatim <iq> \endverbatim tags.
1452 * \param client the configured XMPP client we use to connect to a XMPP server
1456 static void aji_handle_iq(struct aji_client
*client
, iks
*node
)
1458 /*Nothing to see here */
1462 * \brief Handles presence packets.
1463 * \param client the configured XMPP client we use to connect to a XMPP server
1464 * \param pak ikspak the node
1466 static void aji_handle_message(struct aji_client
*client
, ikspak
*pak
)
1468 struct aji_message
*insert
, *tmp
;
1471 if (!(insert
= ast_calloc(1, sizeof(*insert
))))
1473 time(&insert
->arrived
);
1474 if (iks_find_cdata(pak
->x
, "body"))
1475 insert
->message
= ast_strdup(iks_find_cdata(pak
->x
, "body"));
1477 ast_copy_string(insert
->id
, pak
->id
, sizeof(insert
->message
));
1479 insert
->from
= ast_strdup(pak
->from
->full
);
1480 AST_LIST_LOCK(&client
->messages
);
1481 AST_LIST_TRAVERSE_SAFE_BEGIN(&client
->messages
, tmp
, list
) {
1483 AST_LIST_REMOVE_CURRENT(list
);
1485 ast_free(tmp
->from
);
1487 ast_free(tmp
->message
);
1488 } else if (difftime(time(NULL
), tmp
->arrived
) >= client
->message_timeout
) {
1490 AST_LIST_REMOVE_CURRENT(list
);
1492 ast_free(tmp
->from
);
1494 ast_free(tmp
->message
);
1497 AST_LIST_TRAVERSE_SAFE_END
;
1498 AST_LIST_INSERT_HEAD(&client
->messages
, insert
, list
);
1499 AST_LIST_UNLOCK(&client
->messages
);
1502 * \brief Check the presence info
1503 * \param client the configured XMPP client we use to connect to a XMPP server
1506 static void aji_handle_presence(struct aji_client
*client
, ikspak
*pak
)
1508 int status
, priority
;
1509 struct aji_buddy
*buddy
;
1510 struct aji_resource
*tmp
= NULL
, *last
= NULL
, *found
= NULL
;
1511 char *ver
, *node
, *descrip
, *type
;
1513 if(client
->state
!= AJI_CONNECTED
)
1514 aji_create_buddy(pak
->from
->partial
, client
);
1516 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1517 if (!buddy
&& pak
->from
->partial
) {
1518 /* allow our jid to be used to log in with another resource */
1519 if (!strcmp((const char *)pak
->from
->partial
, (const char *)client
->jid
->partial
))
1520 aji_create_buddy(pak
->from
->partial
, client
);
1522 ast_log(LOG_NOTICE
, "Got presence packet from %s, someone not in our roster!!!!\n", pak
->from
->partial
);
1525 type
= iks_find_attrib(pak
->x
, "type");
1526 if(client
->component
&& type
&&!strcasecmp("probe", type
)) {
1527 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), client
->status
, client
->statusmessage
);
1528 ast_verbose("what i was looking for \n");
1530 ASTOBJ_WRLOCK(buddy
);
1531 status
= (pak
->show
) ? pak
->show
: 6;
1532 priority
= atoi((iks_find_cdata(pak
->x
, "priority")) ? iks_find_cdata(pak
->x
, "priority") : "0");
1533 tmp
= buddy
->resources
;
1534 descrip
= ast_strdup(iks_find_cdata(pak
->x
,"status"));
1536 while (tmp
&& pak
->from
->resource
) {
1537 if (!strcasecmp(tmp
->resource
, pak
->from
->resource
)) {
1538 tmp
->status
= status
;
1539 if (tmp
->description
) ast_free(tmp
->description
);
1540 tmp
->description
= descrip
;
1542 if (status
== 6) { /* Sign off Destroy resource */
1543 if (last
&& found
->next
) {
1544 last
->next
= found
->next
;
1547 buddy
->resources
= found
->next
;
1549 buddy
->resources
= NULL
;
1550 } else if (!found
->next
) {
1554 buddy
->resources
= NULL
;
1560 /* resource list is sorted by descending priority */
1561 if (tmp
->priority
!= priority
) {
1562 found
->priority
= priority
;
1563 if (!last
&& !found
->next
)
1564 /* resource was found to be unique,
1567 /* search for resource in our list
1568 and take it out for the moment */
1570 last
->next
= found
->next
;
1572 buddy
->resources
= found
->next
;
1575 tmp
= buddy
->resources
;
1576 if (!buddy
->resources
)
1577 buddy
->resources
= found
;
1578 /* priority processing */
1580 /* insert resource back according to
1581 its priority value */
1582 if (found
->priority
> tmp
->priority
) {
1584 /* insert within list */
1589 buddy
->resources
= found
;
1593 /* insert at the end of the list */
1608 /* resource not found in our list, create it */
1609 if (!found
&& status
!= 6 && pak
->from
->resource
) {
1610 found
= ast_calloc(1, sizeof(*found
));
1613 ast_log(LOG_ERROR
, "Out of memory!\n");
1616 ast_copy_string(found
->resource
, pak
->from
->resource
, sizeof(found
->resource
));
1617 found
->status
= status
;
1618 found
->description
= descrip
;
1619 found
->priority
= priority
;
1622 tmp
= buddy
->resources
;
1624 if (found
->priority
> tmp
->priority
) {
1629 buddy
->resources
= found
;
1640 buddy
->resources
= found
;
1643 ASTOBJ_UNLOCK(buddy
);
1644 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
1646 node
= iks_find_attrib(iks_find(pak
->x
, "c"), "node");
1647 ver
= iks_find_attrib(iks_find(pak
->x
, "c"), "ver");
1649 /* handle gmail client's special caps:c tag */
1650 if (!node
&& !ver
) {
1651 node
= iks_find_attrib(iks_find(pak
->x
, "caps:c"), "node");
1652 ver
= iks_find_attrib(iks_find(pak
->x
, "caps:c"), "ver");
1655 /* retrieve capabilites of the new resource */
1656 if(status
!=6 && found
&& !found
->cap
) {
1657 found
->cap
= aji_find_version(node
, ver
, pak
);
1658 if(gtalk_yuck(pak
->x
)) /* gtalk should do discover */
1659 found
->cap
->jingle
= 1;
1660 if(found
->cap
->jingle
&& option_debug
> 4) {
1661 ast_debug(1,"Special case for google till they support discover.\n");
1666 query
= iks_new("query");
1668 iks_insert_attrib(iq
, "type", "get");
1669 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1670 iks_insert_attrib(iq
,"from", client
->jid
->full
);
1671 iks_insert_attrib(iq
, "id", client
->mid
);
1672 ast_aji_increment_mid(client
->mid
);
1673 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1674 iks_insert_node(iq
, query
);
1675 ast_aji_send(client
, iq
);
1678 ast_log(LOG_ERROR
, "Out of memory.\n");
1684 switch (pak
->subtype
) {
1685 case IKS_TYPE_AVAILABLE
:
1686 ast_verb(5, "JABBER: I am available ^_* %i\n", pak
->subtype
);
1688 case IKS_TYPE_UNAVAILABLE
:
1689 ast_verb(5, "JABBER: I am unavailable ^_* %i\n", pak
->subtype
);
1692 ast_verb(5, "JABBER: Ohh sexy and the wrong type: %i\n", pak
->subtype
);
1694 switch (pak
->show
) {
1695 case IKS_SHOW_UNAVAILABLE
:
1696 ast_verb(5, "JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1698 case IKS_SHOW_AVAILABLE
:
1699 ast_verb(5, "JABBER: type is available\n");
1702 ast_verb(5, "JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1705 ast_verb(5, "JABBER: type is away\n");
1708 ast_verb(5, "JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1711 ast_verb(5, "JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1714 ast_verb(5, "JABBER: Kinky! how did that happen %i\n", pak
->show
);
1719 * \brief handles subscription requests.
1720 * \param client the configured XMPP client we use to connect to a XMPP server
1721 * \param pak ikspak iksemel packet.
1724 static void aji_handle_subscribe(struct aji_client
*client
, ikspak
*pak
)
1726 iks
*presence
= NULL
, *status
= NULL
;
1727 struct aji_buddy
* buddy
= NULL
;
1729 switch (pak
->subtype
) {
1730 case IKS_TYPE_SUBSCRIBE
:
1731 presence
= iks_new("presence");
1732 status
= iks_new("status");
1733 if (presence
&& status
) {
1734 iks_insert_attrib(presence
, "type", "subscribed");
1735 iks_insert_attrib(presence
, "to", pak
->from
->full
);
1736 iks_insert_attrib(presence
, "from", client
->jid
->full
);
1738 iks_insert_attrib(presence
, "id", pak
->id
);
1739 iks_insert_cdata(status
, "Asterisk has approved subscription", 0);
1740 iks_insert_node(presence
, status
);
1741 ast_aji_send(client
, presence
);
1743 ast_log(LOG_ERROR
, "Unable to allocate nodes\n");
1745 iks_delete(presence
);
1748 if (client
->component
)
1749 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), client
->status
, client
->statusmessage
);
1750 case IKS_TYPE_SUBSCRIBED
:
1751 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1752 if (!buddy
&& pak
->from
->partial
) {
1753 aji_create_buddy(pak
->from
->partial
, client
);
1756 if (option_verbose
> 4) {
1757 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1763 * \brief sends messages.
1764 * \param client the configured XMPP client we use to connect to a XMPP server
1769 int ast_aji_send_chat(struct aji_client
*client
, const char *address
, const char *message
)
1772 iks
*message_packet
= NULL
;
1773 if (client
->state
== AJI_CONNECTED
) {
1774 message_packet
= iks_make_msg(IKS_TYPE_CHAT
, address
, message
);
1775 if (message_packet
) {
1776 iks_insert_attrib(message_packet
, "from", client
->jid
->full
);
1777 res
= ast_aji_send(client
, message_packet
);
1779 ast_log(LOG_ERROR
, "Out of memory.\n");
1782 iks_delete(message_packet
);
1784 ast_log(LOG_WARNING
, "JABBER: Not connected can't send\n");
1789 * \brief create a chatroom.
1790 * \param client the configured XMPP client we use to connect to a XMPP server
1791 * \param room name of room
1792 * \param server name of server
1793 * \param topic topic for the room.
1796 int ast_aji_create_chat(struct aji_client
*client
, char *room
, char *server
, char *topic
)
1803 iks_insert_attrib(iq
, "type", "get");
1804 iks_insert_attrib(iq
, "to", server
);
1805 iks_insert_attrib(iq
, "id", client
->mid
);
1806 ast_aji_increment_mid(client
->mid
);
1807 ast_aji_send(client
, iq
);
1809 ast_log(LOG_ERROR
, "Out of memory.\n");
1817 * \brief join a chatroom.
1818 * \param client the configured XMPP client we use to connect to a XMPP server
1819 * \param room room to join
1822 int ast_aji_join_chat(struct aji_client
*client
, char *room
)
1825 iks
*presence
= NULL
, *priority
= NULL
;
1826 presence
= iks_new("presence");
1827 priority
= iks_new("priority");
1828 if (presence
&& priority
&& client
) {
1829 iks_insert_cdata(priority
, "0", 1);
1830 iks_insert_attrib(presence
, "to", room
);
1831 iks_insert_node(presence
, priority
);
1832 res
= ast_aji_send(client
, presence
);
1833 iks_insert_cdata(priority
, "5", 1);
1834 iks_insert_attrib(presence
, "to", room
);
1835 res
= ast_aji_send(client
, presence
);
1837 ast_log(LOG_ERROR
, "Out of memory.\n");
1839 iks_delete(presence
);
1840 iks_delete(priority
);
1846 * \brief invite to a chatroom.
1847 * \param client the configured XMPP client we use to connect to a XMPP server
1853 int ast_aji_invite_chat(struct aji_client
*client
, char *user
, char *room
, char *message
)
1856 iks
*invite
, *body
, *namespace;
1858 invite
= iks_new("message");
1859 body
= iks_new("body");
1860 namespace = iks_new("x");
1861 if (client
&& invite
&& body
&& namespace) {
1862 iks_insert_attrib(invite
, "to", user
);
1863 iks_insert_attrib(invite
, "id", client
->mid
);
1864 ast_aji_increment_mid(client
->mid
);
1865 iks_insert_cdata(body
, message
, 0);
1866 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
1867 iks_insert_attrib(namespace, "jid", room
);
1868 iks_insert_node(invite
, body
);
1869 iks_insert_node(invite
, namespace);
1870 res
= ast_aji_send(client
, invite
);
1872 ast_log(LOG_ERROR
, "Out of memory.\n");
1875 iks_delete(namespace);
1883 * \brief receive message loop.
1887 static void *aji_recv_loop(void *data
)
1889 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1892 while(res
!= IKS_OK
) {
1893 ast_verb(4, "JABBER: Connecting.\n");
1894 res
= aji_reconnect(client
);
1899 if (res
== IKS_NET_RWERR
|| client
->timeout
== 0) {
1900 while(res
!= IKS_OK
) {
1901 ast_verb(4, "JABBER: reconnecting.\n");
1902 res
= aji_reconnect(client
);
1907 res
= aji_recv(client
, 1);
1909 if (client
->state
== AJI_DISCONNECTING
) {
1910 ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
1914 /* Decrease timeout if no data received */
1915 if (res
== IKS_NET_EXPIRED
)
1918 if (res
== IKS_HOOK
)
1919 ast_log(LOG_WARNING
, "JABBER: Got hook event.\n");
1920 else if (res
== IKS_NET_TLSFAIL
)
1921 ast_log(LOG_WARNING
, "JABBER: Failure in TLS.\n");
1922 else if (client
->timeout
== 0 && client
->state
== AJI_CONNECTED
) {
1923 res
= client
->keepalive
? aji_send_raw(client
, " ") : IKS_OK
;
1925 client
->timeout
= 50;
1927 ast_log(LOG_WARNING
, "JABBER: Network Timeout\n");
1928 } else if (res
== IKS_NET_RWERR
)
1929 ast_log(LOG_WARNING
, "JABBER: socket read error\n");
1931 ASTOBJ_UNREF(client
, aji_client_destroy
);
1936 * \brief increments the mid field for messages and other events.
1940 void ast_aji_increment_mid(char *mid
)
1944 for (i
= strlen(mid
) - 1; i
>= 0; i
--) {
1945 if (mid
[i
] != 'z') {
1946 mid
[i
] = mid
[i
] + 1;
1955 * \brief attempts to register to a transport.
1956 * \param aji_client struct, and xml packet.
1957 * \return IKS_FILTER_EAT.
1959 /*allows for registering to transport , was too sketch and is out for now. */
1960 static int aji_register_transport(void *data
, ikspak
*pak
)
1962 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1964 struct aji_buddy
*buddy
= NULL
;
1965 iks
*send
= iks_make_iq(IKS_TYPE_GET
, "jabber:iq:register");
1967 if (client
&& send
) {
1968 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1969 ASTOBJ_RDLOCK(iterator
);
1970 if (iterator
->btype
== AJI_TRANS
) {
1973 ASTOBJ_UNLOCK(iterator
);
1975 iks_filter_remove_hook(client
->f
, aji_register_transport
);
1976 iks_filter_add_rule(client
->f
, aji_register_transport2
, client
, IKS_RULE_TYPE
, IKS_PAK_IQ
, IKS_RULE_SUBTYPE
, IKS_TYPE_RESULT
, IKS_RULE_NS
, IKS_NS_REGISTER
, IKS_RULE_DONE
);
1977 iks_insert_attrib(send
, "to", buddy
->host
);
1978 iks_insert_attrib(send
, "id", client
->mid
);
1979 ast_aji_increment_mid(client
->mid
);
1980 iks_insert_attrib(send
, "from", client
->user
);
1981 res
= ast_aji_send(client
, send
);
1983 ast_log(LOG_ERROR
, "Out of memory.\n");
1987 ASTOBJ_UNREF(client
, aji_client_destroy
);
1988 return IKS_FILTER_EAT
;
1992 * \brief attempts to register to a transport step 2.
1993 * \param aji_client struct, and xml packet.
1994 * \return IKS_FILTER_EAT.
1996 /* more of the same blob of code, too wonky for now*/
1997 static int aji_register_transport2(void *data
, ikspak
*pak
)
1999 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
2001 struct aji_buddy
*buddy
= NULL
;
2003 iks
*regiq
= iks_new("iq");
2004 iks
*regquery
= iks_new("query");
2005 iks
*reguser
= iks_new("username");
2006 iks
*regpass
= iks_new("password");
2008 if (client
&& regquery
&& reguser
&& regpass
&& regiq
) {
2009 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2010 ASTOBJ_RDLOCK(iterator
);
2011 if (iterator
->btype
== AJI_TRANS
)
2012 buddy
= iterator
; ASTOBJ_UNLOCK(iterator
);
2014 iks_filter_remove_hook(client
->f
, aji_register_transport2
);
2015 iks_insert_attrib(regiq
, "to", buddy
->host
);
2016 iks_insert_attrib(regiq
, "type", "set");
2017 iks_insert_attrib(regiq
, "id", client
->mid
);
2018 ast_aji_increment_mid(client
->mid
);
2019 iks_insert_attrib(regiq
, "from", client
->user
);
2020 iks_insert_attrib(regquery
, "xmlns", "jabber:iq:register");
2021 iks_insert_cdata(reguser
, buddy
->user
, 0);
2022 iks_insert_cdata(regpass
, buddy
->pass
, 0);
2023 iks_insert_node(regiq
, regquery
);
2024 iks_insert_node(regquery
, reguser
);
2025 iks_insert_node(regquery
, regpass
);
2026 res
= ast_aji_send(client
, regiq
);
2028 ast_log(LOG_ERROR
, "Out of memory.\n");
2032 iks_delete(regquery
);
2034 iks_delete(reguser
);
2036 iks_delete(regpass
);
2037 ASTOBJ_UNREF(client
, aji_client_destroy
);
2038 return IKS_FILTER_EAT
;
2043 * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2044 * \param client the configured XMPP client we use to connect to a XMPP server
2047 static void aji_pruneregister(struct aji_client
*client
)
2050 iks
*removeiq
= iks_new("iq");
2051 iks
*removequery
= iks_new("query");
2052 iks
*removeitem
= iks_new("item");
2053 iks
*send
= iks_make_iq(IKS_TYPE_GET
, "http://jabber.org/protocol/disco#items");
2055 if (client
&& removeiq
&& removequery
&& removeitem
&& send
) {
2056 iks_insert_node(removeiq
, removequery
);
2057 iks_insert_node(removequery
, removeitem
);
2058 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2059 ASTOBJ_RDLOCK(iterator
);
2060 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2061 * be called at the same time */
2062 if (ast_test_flag(&iterator
->flags
, AJI_AUTOPRUNE
)) {
2063 res
= ast_aji_send(client
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE
, iterator
->name
,
2064 "GoodBye your status is no longer needed by Asterisk the Open Source PBX"
2065 " so I am no longer subscribing to your presence.\n"));
2066 res
= ast_aji_send(client
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED
, iterator
->name
,
2067 "GoodBye you are no longer in the asterisk config file so I am removing"
2068 " your access to my presence.\n"));
2069 iks_insert_attrib(removeiq
, "from", client
->jid
->full
);
2070 iks_insert_attrib(removeiq
, "type", "set");
2071 iks_insert_attrib(removequery
, "xmlns", "jabber:iq:roster");
2072 iks_insert_attrib(removeitem
, "jid", iterator
->name
);
2073 iks_insert_attrib(removeitem
, "subscription", "remove");
2074 res
= ast_aji_send(client
, removeiq
);
2075 } else if (ast_test_flag(&iterator
->flags
, AJI_AUTOREGISTER
)) {
2076 res
= ast_aji_send(client
, iks_make_s10n(IKS_TYPE_SUBSCRIBE
, iterator
->name
,
2077 "Greetings I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2078 ast_clear_flag(&iterator
->flags
, AJI_AUTOREGISTER
);
2080 ASTOBJ_UNLOCK(iterator
);
2083 ast_log(LOG_ERROR
, "Out of memory.\n");
2085 iks_delete(removeiq
);
2086 iks_delete(removequery
);
2087 iks_delete(removeitem
);
2090 ASTOBJ_CONTAINER_PRUNE_MARKED(&client
->buddies
, aji_buddy_destroy
);
2094 * \brief filters the roster packet we get back from server.
2096 * \param pak ikspak iksemel packet.
2097 * \return IKS_FILTER_EAT.
2099 static int aji_filter_roster(void *data
, ikspak
*pak
)
2101 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
2104 struct aji_buddy
*buddy
;
2106 client
->state
= AJI_CONNECTED
;
2107 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2108 ASTOBJ_RDLOCK(iterator
);
2109 x
= iks_child(pak
->query
);
2112 if (!iks_strcmp(iks_name(x
), "item")) {
2113 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid"))) {
2115 ast_clear_flag(&iterator
->flags
, AJI_AUTOPRUNE
| AJI_AUTOREGISTER
);
2121 ast_copy_flags(&iterator
->flags
, &client
->flags
, AJI_AUTOREGISTER
);
2124 ASTOBJ_UNLOCK(iterator
);
2127 x
= iks_child(pak
->query
);
2130 if (iks_strcmp(iks_name(x
), "item") == 0) {
2131 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2132 ASTOBJ_RDLOCK(iterator
);
2133 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid")))
2135 ASTOBJ_UNLOCK(iterator
);
2139 buddy
= ast_calloc(1, sizeof(*buddy
));
2141 ast_log(LOG_WARNING
, "Out of memory\n");
2145 ASTOBJ_WRLOCK(buddy
);
2146 ast_copy_string(buddy
->name
, iks_find_attrib(x
, "jid"), sizeof(buddy
->name
));
2147 ast_clear_flag(&buddy
->flags
, AST_FLAGS_ALL
);
2148 if(ast_test_flag(&client
->flags
, AJI_AUTOPRUNE
)) {
2149 ast_set_flag(&buddy
->flags
, AJI_AUTOPRUNE
);
2152 ast_set_flag(&buddy
->flags
, AJI_AUTOREGISTER
);
2153 ASTOBJ_UNLOCK(buddy
);
2155 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
2156 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
2164 aji_pruneregister(client
);
2166 ASTOBJ_UNREF(client
, aji_client_destroy
);
2167 return IKS_FILTER_EAT
;
2170 * \brief reconnect to jabber server
2171 * \param client the configured XMPP client we use to connect to a XMPP server
2174 static int aji_reconnect(struct aji_client
*client
)
2179 client
->state
= AJI_DISCONNECTED
;
2182 iks_parser_reset(client
->p
);
2183 if (client
->authorized
)
2184 client
->authorized
= 0;
2186 res
= aji_initialize(client
);
2191 * \brief Get the roster of jabber users
2192 * \param client the configured XMPP client we use to connect to a XMPP server
2195 static int aji_get_roster(struct aji_client
*client
)
2198 roster
= iks_make_iq(IKS_TYPE_GET
, IKS_NS_ROSTER
);
2201 iks_insert_attrib(roster
, "id", "roster");
2202 aji_set_presence(client
, NULL
, client
->jid
->full
, client
->status
, client
->statusmessage
);
2203 ast_aji_send(client
, roster
);
2212 * \brief connects as a client to jabber server.
2214 * \param pak ikspak iksemel packet
2217 static int aji_client_connect(void *data
, ikspak
*pak
)
2219 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
2223 if (client
->state
== AJI_DISCONNECTED
) {
2224 iks_filter_add_rule(client
->f
, aji_filter_roster
, client
, IKS_RULE_TYPE
, IKS_PAK_IQ
, IKS_RULE_SUBTYPE
, IKS_TYPE_RESULT
, IKS_RULE_ID
, "roster", IKS_RULE_DONE
);
2225 client
->state
= AJI_CONNECTING
;
2226 client
->jid
= (iks_find_cdata(pak
->query
, "jid")) ? iks_id_new(client
->stack
, iks_find_cdata(pak
->query
, "jid")) : client
->jid
;
2227 iks_filter_remove_hook(client
->f
, aji_client_connect
);
2228 if(!client
->component
) /*client*/
2229 aji_get_roster(client
);
2232 ast_log(LOG_ERROR
, "Out of memory.\n");
2234 ASTOBJ_UNREF(client
, aji_client_destroy
);
2239 * \brief prepares client for connect.
2240 * \param client the configured XMPP client we use to connect to a XMPP server
2243 static int aji_initialize(struct aji_client
*client
)
2245 int connected
= IKS_NET_NOCONN
;
2247 /* reset stream flags */
2248 client
->stream_flags
= 0;
2250 /* If it's a component, connect to user, otherwise, connect to server */
2251 connected
= iks_connect_via(client
->p
, S_OR(client
->serverhost
, client
->jid
->server
), client
->port
, client
->component
? client
->user
: client
->jid
->server
);
2253 if (connected
== IKS_NET_NOCONN
) {
2254 ast_log(LOG_ERROR
, "JABBER ERROR: No Connection\n");
2256 } else if (connected
== IKS_NET_NODNS
) {
2257 ast_log(LOG_ERROR
, "JABBER ERROR: No DNS %s for client to %s\n", client
->name
, S_OR(client
->serverhost
, client
->jid
->server
));
2265 * \brief disconnect from jabber server.
2266 * \param client the configured XMPP client we use to connect to a XMPP server
2269 int ast_aji_disconnect(struct aji_client
*client
)
2272 ast_verb(4, "JABBER: Disconnecting\n");
2274 if (client
->stream_flags
& SECURE
) {
2275 SSL_shutdown(client
->ssl_session
);
2276 SSL_CTX_free(client
->ssl_context
);
2277 SSL_free(client
->ssl_session
);
2280 iks_disconnect(client
->p
);
2281 iks_parser_delete(client
->p
);
2282 ASTOBJ_UNREF(client
, aji_client_destroy
);
2289 * \brief set presence of client.
2290 * \param client the configured XMPP client we use to connect to a XMPP server
2291 * \param to user send it to
2292 * \param from user it came from
2297 static void aji_set_presence(struct aji_client
*client
, char *to
, char *from
, int level
, char *desc
)
2300 iks
*presence
= iks_make_pres(level
, desc
);
2301 iks
*cnode
= iks_new("c");
2302 iks
*priority
= iks_new("priority");
2305 if (presence
&& cnode
&& client
&& priority
) {
2307 iks_insert_attrib(presence
, "to", to
);
2309 iks_insert_attrib(presence
, "from", from
);
2310 snprintf(priorityS
, sizeof(priorityS
), "%d", client
->priority
);
2311 iks_insert_cdata(priority
, priorityS
, strlen(priorityS
));
2312 iks_insert_node(presence
, priority
);
2313 iks_insert_attrib(cnode
, "node", "http://www.asterisk.org/xmpp/client/caps");
2314 iks_insert_attrib(cnode
, "ver", "asterisk-xmpp");
2315 iks_insert_attrib(cnode
, "ext", "voice-v1");
2316 iks_insert_attrib(cnode
, "xmlns", "http://jabber.org/protocol/caps");
2317 iks_insert_node(presence
, cnode
);
2318 res
= ast_aji_send(client
, presence
);
2320 ast_log(LOG_ERROR
, "Out of memory.\n");
2323 iks_delete(presence
);
2324 iks_delete(priority
);
2328 * \brief Turn on/off console debugging.
2329 * \return CLI_SUCCESS.
2331 static char *aji_do_set_debug(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
2335 e
->command
= "jabber set debug {on|off}";
2337 "Usage: jabber set debug {on|off}\n"
2338 " Enables/disables dumping of Jabber packets for debugging purposes.\n";
2344 if (a
->argc
!= e
->args
)
2345 return CLI_SHOWUSAGE
;
2347 if (!strncasecmp(a
->argv
[e
->args
- 1], "on", 2)) {
2348 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2349 ASTOBJ_RDLOCK(iterator
);
2350 iterator
->debug
= 1;
2351 ASTOBJ_UNLOCK(iterator
);
2353 ast_cli(a
->fd
, "Jabber Debugging Enabled.\n");
2355 } else if (!strncasecmp(a
->argv
[e
->args
- 1], "off", 3)) {
2356 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2357 ASTOBJ_RDLOCK(iterator
);
2358 iterator
->debug
= 0;
2359 ASTOBJ_UNLOCK(iterator
);
2361 ast_cli(a
->fd
, "Jabber Debugging Disabled.\n");
2364 return CLI_SHOWUSAGE
; /* defaults to invalid */
2368 * \brief Turn on/off console debugging (deprecated, use aji_do_set_debug).
2369 * \return CLI_SUCCESS.
2371 static char *aji_do_debug_deprecated(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
2376 e
->command
= "jabber debug [off]";
2378 "Usage: jabber debug [off]\n"
2379 " Enables/disables dumping of Jabber packets for debugging purposes.\n";
2386 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2387 ASTOBJ_RDLOCK(iterator
);
2388 iterator
->debug
= 1;
2389 ASTOBJ_UNLOCK(iterator
);
2391 ast_cli(a
->fd
, "Jabber Debugging Enabled.\n");
2393 } else if (a
->argc
== 3) {
2394 if (!strncasecmp(a
->argv
[2], "off", 3)) {
2395 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2396 ASTOBJ_RDLOCK(iterator
);
2397 iterator
->debug
= 0;
2398 ASTOBJ_UNLOCK(iterator
);
2400 ast_cli(a
->fd
, "Jabber Debugging Disabled.\n");
2404 return CLI_SHOWUSAGE
; /* defaults to invalid */
2408 * \brief Reload jabber module.
2409 * \return CLI_SUCCESS.
2411 static char *aji_do_reload(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
2415 e
->command
= "jabber reload";
2417 "Usage: jabber reload\n"
2418 " Reloads the Jabber module.\n";
2425 ast_cli(a
->fd
, "Jabber Reloaded.\n");
2430 * \brief Show client status.
2431 * \return CLI_SUCCESS.
2433 static char *aji_show_clients(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
2440 e
->command
= "jabber show connected";
2442 "Usage: jabber show connected\n"
2443 " Shows state of clients and components\n";
2449 ast_cli(a
->fd
, "Jabber Users and their status:\n");
2450 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2451 ASTOBJ_RDLOCK(iterator
);
2453 switch (iterator
->state
) {
2454 case AJI_DISCONNECTED
:
2455 status
= "Disconnected";
2457 case AJI_CONNECTING
:
2458 status
= "Connecting";
2461 status
= "Connected";
2466 ast_cli(a
->fd
, " User: %s - %s\n", iterator
->user
, status
);
2467 ASTOBJ_UNLOCK(iterator
);
2469 ast_cli(a
->fd
, "----\n");
2470 ast_cli(a
->fd
, " Number of users: %d\n", count
);
2475 * \brief Show buddy lists
2476 * \return CLI_SUCCESS.
2478 static char *aji_show_buddies(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
2480 struct aji_resource
*resource
;
2481 struct aji_client
*client
;
2485 e
->command
= "jabber show buddies";
2487 "Usage: jabber show buddies\n"
2488 " Shows buddy lists of our clients\n";
2494 ast_cli(a
->fd
, "Jabber buddy lists\n");
2495 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2496 ast_cli(a
->fd
,"Client: %s\n", iterator
->user
);
2498 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2499 ASTOBJ_RDLOCK(iterator
);
2500 ast_cli(a
->fd
,"\tBuddy:\t%s\n", iterator
->name
);
2501 if (!iterator
->resources
)
2502 ast_cli(a
->fd
,"\t\tResource: None\n");
2503 for (resource
= iterator
->resources
; resource
; resource
= resource
->next
) {
2504 ast_cli(a
->fd
,"\t\tResource: %s\n", resource
->resource
);
2506 ast_cli(a
->fd
,"\t\t\tnode: %s\n", resource
->cap
->parent
->node
);
2507 ast_cli(a
->fd
,"\t\t\tversion: %s\n", resource
->cap
->version
);
2508 ast_cli(a
->fd
,"\t\t\tJingle capable: %s\n", resource
->cap
->jingle
? "yes" : "no");
2510 ast_cli(a
->fd
,"\t\tStatus: %d\n", resource
->status
);
2511 ast_cli(a
->fd
,"\t\tPriority: %d\n", resource
->priority
);
2513 ASTOBJ_UNLOCK(iterator
);
2521 * \brief Send test message for debugging.
2522 * \return CLI_SUCCESS,CLI_FAILURE.
2524 static char *aji_test(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
2526 struct aji_client
*client
;
2527 struct aji_resource
*resource
;
2528 const char *name
= "asterisk";
2529 struct aji_message
*tmp
;
2533 e
->command
= "jabber test";
2535 "Usage: jabber test [client]\n"
2536 " Sends test message for debugging purposes. A specific client\n"
2537 " as configured in jabber.conf can be optionally specified.\n";
2544 return CLI_SHOWUSAGE
;
2545 else if (a
->argc
== 3)
2548 if (!(client
= ASTOBJ_CONTAINER_FIND(&clients
, name
))) {
2549 ast_cli(a
->fd
, "Unable to find client '%s'!\n", name
);
2553 /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
2554 ast_aji_send_chat(client
, "mogorman@astjab.org", "blahblah");
2555 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2556 ASTOBJ_RDLOCK(iterator
);
2557 ast_verbose("User: %s\n", iterator
->name
);
2558 for (resource
= iterator
->resources
; resource
; resource
= resource
->next
) {
2559 ast_verbose("Resource: %s\n", resource
->resource
);
2561 ast_verbose(" client: %s\n", resource
->cap
->parent
->node
);
2562 ast_verbose(" version: %s\n", resource
->cap
->version
);
2563 ast_verbose(" Jingle Capable: %d\n", resource
->cap
->jingle
);
2565 ast_verbose(" Priority: %d\n", resource
->priority
);
2566 ast_verbose(" Status: %d\n", resource
->status
);
2567 ast_verbose(" Message: %s\n", S_OR(resource
->description
,""));
2569 ASTOBJ_UNLOCK(iterator
);
2571 ast_verbose("\nOooh a working message stack!\n");
2572 AST_LIST_LOCK(&client
->messages
);
2573 AST_LIST_TRAVERSE(&client
->messages
, tmp
, list
) {
2574 ast_verbose(" Message from: %s with id %s @ %s %s\n",tmp
->from
, S_OR(tmp
->id
,""), ctime(&tmp
->arrived
), S_OR(tmp
->message
, ""));
2576 AST_LIST_UNLOCK(&client
->messages
);
2577 ASTOBJ_UNREF(client
, aji_client_destroy
);
2583 * \brief creates aji_client structure.
2585 * \param var ast_variable
2589 static int aji_create_client(char *label
, struct ast_variable
*var
, int debug
)
2592 struct aji_client
*client
= NULL
;
2595 client
= ASTOBJ_CONTAINER_FIND(&clients
,label
);
2598 client
= ast_calloc(1, sizeof(*client
));
2600 ast_log(LOG_ERROR
, "Out of memory!\n");
2603 ASTOBJ_INIT(client
);
2604 ASTOBJ_WRLOCK(client
);
2605 ASTOBJ_CONTAINER_INIT(&client
->buddies
);
2607 ASTOBJ_WRLOCK(client
);
2608 ASTOBJ_UNMARK(client
);
2610 ASTOBJ_CONTAINER_MARKALL(&client
->buddies
);
2611 ast_copy_string(client
->name
, label
, sizeof(client
->name
));
2612 ast_copy_string(client
->mid
, "aaaaa", sizeof(client
->mid
));
2614 /* Set default values for the client object */
2615 client
->debug
= debug
;
2616 ast_copy_flags(&client
->flags
, &globalflags
, AST_FLAGS_ALL
);
2617 client
->port
= 5222;
2619 client
->usesasl
= 1;
2620 client
->forcessl
= 0;
2621 client
->keepalive
= 1;
2622 client
->timeout
= 50;
2623 client
->message_timeout
= 100;
2624 AST_LIST_HEAD_INIT(&client
->messages
);
2625 client
->component
= 0;
2626 ast_copy_string(client
->statusmessage
, "Online and Available", sizeof(client
->statusmessage
));
2627 client
->priority
= 0;
2628 client
->status
= IKS_SHOW_AVAILABLE
;
2631 client
->authorized
= 0;
2632 client
->state
= AJI_DISCONNECTED
;
2635 if (!strcasecmp(var
->name
, "username"))
2636 ast_copy_string(client
->user
, var
->value
, sizeof(client
->user
));
2637 else if (!strcasecmp(var
->name
, "serverhost"))
2638 ast_copy_string(client
->serverhost
, var
->value
, sizeof(client
->serverhost
));
2639 else if (!strcasecmp(var
->name
, "secret"))
2640 ast_copy_string(client
->password
, var
->value
, sizeof(client
->password
));
2641 else if (!strcasecmp(var
->name
, "statusmessage"))
2642 ast_copy_string(client
->statusmessage
, var
->value
, sizeof(client
->statusmessage
));
2643 else if (!strcasecmp(var
->name
, "port"))
2644 client
->port
= atoi(var
->value
);
2645 else if (!strcasecmp(var
->name
, "timeout"))
2646 client
->message_timeout
= atoi(var
->value
);
2647 else if (!strcasecmp(var
->name
, "debug"))
2648 client
->debug
= (ast_false(var
->value
)) ? 0 : 1;
2649 else if (!strcasecmp(var
->name
, "type")) {
2650 if (!strcasecmp(var
->value
, "component"))
2651 client
->component
= 1;
2652 } else if (!strcasecmp(var
->name
, "usetls")) {
2653 client
->usetls
= (ast_false(var
->value
)) ? 0 : 1;
2654 } else if (!strcasecmp(var
->name
, "usesasl")) {
2655 client
->usesasl
= (ast_false(var
->value
)) ? 0 : 1;
2656 } else if (!strcasecmp(var
->name
, "forceoldssl"))
2657 client
->forcessl
= (ast_false(var
->value
)) ? 0 : 1;
2658 else if (!strcasecmp(var
->name
, "keepalive"))
2659 client
->keepalive
= (ast_false(var
->value
)) ? 0 : 1;
2660 else if (!strcasecmp(var
->name
, "autoprune"))
2661 ast_set2_flag(&client
->flags
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2662 else if (!strcasecmp(var
->name
, "autoregister"))
2663 ast_set2_flag(&client
->flags
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2664 else if (!strcasecmp(var
->name
, "buddy"))
2665 aji_create_buddy((char *)var
->value
, client
);
2666 else if (!strcasecmp(var
->name
, "priority"))
2667 client
->priority
= atoi(var
->value
);
2668 else if (!strcasecmp(var
->name
, "status")) {
2669 if (!strcasecmp(var
->value
, "unavailable"))
2670 client
->status
= IKS_SHOW_UNAVAILABLE
;
2672 if (!strcasecmp(var
->value
, "available")
2673 || !strcasecmp(var
->value
, "online"))
2674 client
->status
= IKS_SHOW_AVAILABLE
;
2676 if (!strcasecmp(var
->value
, "chat")
2677 || !strcasecmp(var
->value
, "chatty"))
2678 client
->status
= IKS_SHOW_CHAT
;
2680 if (!strcasecmp(var
->value
, "away"))
2681 client
->status
= IKS_SHOW_AWAY
;
2683 if (!strcasecmp(var
->value
, "xa")
2684 || !strcasecmp(var
->value
, "xaway"))
2685 client
->status
= IKS_SHOW_XA
;
2687 if (!strcasecmp(var
->value
, "dnd"))
2688 client
->status
= IKS_SHOW_DND
;
2690 if (!strcasecmp(var
->value
, "invisible"))
2691 #ifdef IKS_SHOW_INVISIBLE
2692 client
->status
= IKS_SHOW_INVISIBLE
;
2695 ast_log(LOG_WARNING
, "Your iksemel doesn't support invisible status: falling back to DND\n");
2696 client
->status
= IKS_SHOW_DND
;
2700 ast_log(LOG_WARNING
, "Unknown presence status: %s\n", var
->value
);
2702 /* no transport support in this version */
2703 /* else if (!strcasecmp(var->name, "transport"))
2704 aji_create_transport(var->value, client);
2709 ASTOBJ_UNLOCK(client
);
2710 ASTOBJ_UNREF(client
, aji_client_destroy
);
2714 ast_copy_string(client
->name_space
, (client
->component
) ? "jabber:component:accept" : "jabber:client", sizeof(client
->name_space
));
2715 client
->p
= iks_stream_new(client
->name_space
, client
, aji_act_hook
);
2717 ast_log(LOG_ERROR
, "Failed to create stream for client '%s'!\n", client
->name
);
2720 client
->stack
= iks_stack_new(8192, 8192);
2721 if (!client
->stack
) {
2722 ast_log(LOG_ERROR
, "Failed to allocate stack for client '%s'\n", client
->name
);
2725 client
->f
= iks_filter_new();
2727 ast_log(LOG_ERROR
, "Failed to create filter for client '%s'\n", client
->name
);
2730 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2732 asprintf(&resource
, "%s/asterisk", client
->user
);
2734 client
->jid
= iks_id_new(client
->stack
, resource
);
2738 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2739 if (client
->component
) {
2740 iks_filter_add_rule(client
->f
, aji_dinfo_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2741 iks_filter_add_rule(client
->f
, aji_ditems_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE
);
2742 iks_filter_add_rule(client
->f
, aji_register_query_handler
, client
, IKS_RULE_SUBTYPE
, IKS_TYPE_GET
, IKS_RULE_NS
, "jabber:iq:register", IKS_RULE_DONE
);
2743 iks_filter_add_rule(client
->f
, aji_register_approve_handler
, client
, IKS_RULE_SUBTYPE
, IKS_TYPE_SET
, IKS_RULE_NS
, "jabber:iq:register", IKS_RULE_DONE
);
2745 iks_filter_add_rule(client
->f
, aji_client_info_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2747 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2749 asprintf(&resource
, "%s/asterisk", client
->user
);
2751 client
->jid
= iks_id_new(client
->stack
, resource
);
2755 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2756 iks_set_log_hook(client
->p
, aji_log_hook
);
2757 ASTOBJ_UNLOCK(client
);
2758 ASTOBJ_CONTAINER_LINK(&clients
,client
);
2764 * \brief creates transport.
2765 * \param label, buddy to dump it into.
2768 /* no connecting to transports today */
2769 static int aji_create_transport(char *label
, struct aji_client
*client
)
2771 char *server
= NULL
, *buddyname
= NULL
, *user
= NULL
, *pass
= NULL
;
2772 struct aji_buddy
*buddy
= NULL
;
2774 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
,label
);
2776 buddy
= ast_calloc(1, sizeof(*buddy
));
2778 ast_log(LOG_WARNING
, "Out of memory\n");
2783 ASTOBJ_WRLOCK(buddy
);
2785 if ((buddyname
= strchr(label
, ','))) {
2788 if (buddyname
&& buddyname
[0] != '\0') {
2789 if ((user
= strchr(buddyname
, ','))) {
2792 if (user
&& user
[0] != '\0') {
2793 if ((pass
= strchr(user
, ','))) {
2796 ast_copy_string(buddy
->pass
, pass
, sizeof(buddy
->pass
));
2797 ast_copy_string(buddy
->user
, user
, sizeof(buddy
->user
));
2798 ast_copy_string(buddy
->name
, buddyname
, sizeof(buddy
->name
));
2799 ast_copy_string(buddy
->server
, server
, sizeof(buddy
->server
));
2806 ASTOBJ_UNLOCK(buddy
);
2807 ASTOBJ_UNMARK(buddy
);
2808 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
2814 * \brief creates buddy.
2815 * \param label char.
2816 * \param client the configured XMPP client we use to connect to a XMPP server
2817 * \return 1 on success, 0 on failure.
2819 static int aji_create_buddy(char *label
, struct aji_client
*client
)
2821 struct aji_buddy
*buddy
= NULL
;
2823 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
,label
);
2826 buddy
= ast_calloc(1, sizeof(*buddy
));
2828 ast_log(LOG_WARNING
, "Out of memory\n");
2833 ASTOBJ_WRLOCK(buddy
);
2834 ast_copy_string(buddy
->name
, label
, sizeof(buddy
->name
));
2835 ASTOBJ_UNLOCK(buddy
);
2837 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
2839 ASTOBJ_UNMARK(buddy
);
2840 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
2845 /*!< load config file. \return 1. */
2846 static int aji_load_config(int reload
)
2850 struct ast_config
*cfg
= NULL
;
2851 struct ast_variable
*var
= NULL
;
2852 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
2854 if ((cfg
= ast_config_load(JABBER_CONFIG
, config_flags
)) == CONFIG_STATUS_FILEUNCHANGED
)
2857 /* Reset flags to default value */
2858 ast_set_flag(&globalflags
, AJI_AUTOPRUNE
| AJI_AUTOREGISTER
);
2861 ast_log(LOG_WARNING
, "No such configuration file %s\n", JABBER_CONFIG
);
2865 cat
= ast_category_browse(cfg
, NULL
);
2866 for (var
= ast_variable_browse(cfg
, "general"); var
; var
= var
->next
) {
2867 if (!strcasecmp(var
->name
, "debug"))
2868 debug
= (ast_false(ast_variable_retrieve(cfg
, "general", "debug"))) ? 0 : 1;
2869 else if (!strcasecmp(var
->name
, "autoprune"))
2870 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2871 else if (!strcasecmp(var
->name
, "autoregister"))
2872 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2876 if (strcasecmp(cat
, "general")) {
2877 var
= ast_variable_browse(cfg
, cat
);
2878 aji_create_client(cat
, var
, debug
);
2880 cat
= ast_category_browse(cfg
, cat
);
2882 ast_config_destroy(cfg
); /* or leak memory */
2887 * \brief grab a aji_client structure by label name or JID
2888 * (without the resource string)
2889 * \param name label or JID
2890 * \return aji_client.
2892 struct aji_client
*ast_aji_get_client(const char *name
)
2894 struct aji_client
*client
= NULL
;
2897 client
= ASTOBJ_CONTAINER_FIND(&clients
, name
);
2898 if (!client
&& strchr(name
, '@')) {
2899 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2900 aux
= ast_strdupa(iterator
->user
);
2901 if (strchr(aux
, '/')) {
2902 /* strip resource for comparison */
2903 aux
= strsep(&aux
, "/");
2905 if (!strcasecmp(aux
, name
)) {
2914 struct aji_client_container
*ast_aji_get_clients(void)
2919 static char mandescr_jabber_send
[] =
2920 "Description: Sends a message to a Jabber Client.\n"
2922 " Jabber: Client or transport Asterisk uses to connect to JABBER.\n"
2923 " ScreenName: User Name to message.\n"
2924 " Message: Message to be sent to the buddy\n";
2927 * \brief Send a Jabber Message via call from the Manager
2928 * \param s mansession Manager session
2929 * \param m message Message to send
2932 static int manager_jabber_send(struct mansession
*s
, const struct message
*m
)
2934 struct aji_client
*client
= NULL
;
2935 const char *id
= astman_get_header(m
,"ActionID");
2936 const char *jabber
= astman_get_header(m
,"Jabber");
2937 const char *screenname
= astman_get_header(m
,"ScreenName");
2938 const char *message
= astman_get_header(m
,"Message");
2940 if (ast_strlen_zero(jabber
)) {
2941 astman_send_error(s
, m
, "No transport specified");
2944 if (ast_strlen_zero(screenname
)) {
2945 astman_send_error(s
, m
, "No ScreenName specified");
2948 if (ast_strlen_zero(message
)) {
2949 astman_send_error(s
, m
, "No Message specified");
2953 astman_send_ack(s
, m
, "Attempting to send Jabber Message");
2954 client
= ast_aji_get_client(jabber
);
2956 astman_send_error(s
, m
, "Could not find Sender");
2959 if (strchr(screenname
, '@') && message
){
2960 ast_aji_send_chat(client
, screenname
, message
);
2961 astman_append(s
, "Response: Success\r\n");
2962 if (!ast_strlen_zero(id
))
2963 astman_append(s
, "ActionID: %s\r\n",id
);
2966 astman_append(s
, "Response: Error\r\n");
2967 if (!ast_strlen_zero(id
))
2968 astman_append(s
, "ActionID: %s\r\n",id
);
2972 /*! \brief Reload the jabber module */
2973 static int aji_reload(int reload
)
2977 ASTOBJ_CONTAINER_MARKALL(&clients
);
2978 if (!(res
= aji_load_config(reload
))) {
2979 ast_log(LOG_ERROR
, "JABBER: Failed to load config.\n");
2981 } else if (res
== -1)
2984 ASTOBJ_CONTAINER_PRUNE_MARKED(&clients
, aji_client_destroy
);
2985 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2986 ASTOBJ_RDLOCK(iterator
);
2987 if(iterator
->state
== AJI_DISCONNECTED
) {
2988 if (!iterator
->thread
)
2989 ast_pthread_create_background(&iterator
->thread
, NULL
, aji_recv_loop
, iterator
);
2990 } else if (iterator
->state
== AJI_CONNECTING
)
2991 aji_get_roster(iterator
);
2992 ASTOBJ_UNLOCK(iterator
);
2998 /*! \brief Unload the jabber module */
2999 static int unload_module(void)
3002 ast_cli_unregister_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
3003 ast_unregister_application(app_ajisend
);
3004 ast_unregister_application(app_ajistatus
);
3005 ast_manager_unregister("JabberSend");
3006 ast_custom_function_unregister(&jabberstatus_function
);
3008 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
3009 ASTOBJ_RDLOCK(iterator
);
3010 ast_debug(3, "JABBER: Releasing and disconnecting client: %s\n", iterator
->name
);
3011 iterator
->state
= AJI_DISCONNECTING
;
3012 ast_aji_disconnect(iterator
);
3013 pthread_join(iterator
->thread
, NULL
);
3014 ASTOBJ_UNLOCK(iterator
);
3017 ASTOBJ_CONTAINER_DESTROYALL(&clients
, aji_client_destroy
);
3018 ASTOBJ_CONTAINER_DESTROY(&clients
);
3022 /*! \brief Unload the jabber module */
3023 static int load_module(void)
3025 ASTOBJ_CONTAINER_INIT(&clients
);
3027 return AST_MODULE_LOAD_DECLINE
;
3028 ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM
, manager_jabber_send
,
3029 "Sends a message to a Jabber Client", mandescr_jabber_send
);
3030 ast_register_application(app_ajisend
, aji_send_exec
, ajisend_synopsis
, ajisend_descrip
);
3031 ast_register_application(app_ajistatus
, aji_status_exec
, ajistatus_synopsis
, ajistatus_descrip
);
3032 ast_cli_register_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
3033 ast_custom_function_register(&jabberstatus_function
);
3038 /*! \brief Wrapper for aji_reload */
3039 static int reload(void)
3045 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "AJI - Asterisk Jabber Interface",
3046 .load
= load_module
,
3047 .unload
= unload_module
,