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.
25 <depend>iksemel</depend>
30 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
36 #include "asterisk/channel.h"
37 #include "asterisk/jabber.h"
38 #include "asterisk/file.h"
39 #include "asterisk/config.h"
40 #include "asterisk/callerid.h"
41 #include "asterisk/lock.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/options.h"
44 #include "asterisk/cli.h"
45 #include "asterisk/app.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/md5.h"
48 #include "asterisk/acl.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/module.h"
51 #include "asterisk/astobj.h"
52 #include "asterisk/astdb.h"
53 #include "asterisk/manager.h"
55 #define JABBER_CONFIG "jabber.conf"
57 /*-- Forward declarations */
58 static int aji_highest_bit(int number
);
59 static void aji_buddy_destroy(struct aji_buddy
*obj
);
60 static void aji_client_destroy(struct aji_client
*obj
);
61 static int aji_send_exec(struct ast_channel
*chan
, void *data
);
62 static int aji_status_exec(struct ast_channel
*chan
, void *data
);
63 static void aji_log_hook(void *data
, const char *xmpp
, size_t size
, int is_incoming
);
64 static int aji_act_hook(void *data
, int type
, iks
*node
);
65 static void aji_handle_iq(struct aji_client
*client
, iks
*node
);
66 static void aji_handle_message(struct aji_client
*client
, ikspak
*pak
);
67 static void aji_handle_presence(struct aji_client
*client
, ikspak
*pak
);
68 static void aji_handle_subscribe(struct aji_client
*client
, ikspak
*pak
);
69 static void *aji_recv_loop(void *data
);
70 static int aji_component_initialize(struct aji_client
*client
);
71 static int aji_client_initialize(struct aji_client
*client
);
72 static int aji_client_connect(void *data
, ikspak
*pak
);
73 static void aji_set_presence(struct aji_client
*client
, char *to
, char *from
, int level
, char *desc
);
74 static int aji_do_debug(int fd
, int argc
, char *argv
[]);
75 static int aji_do_reload(int fd
, int argc
, char *argv
[]);
76 static int aji_no_debug(int fd
, int argc
, char *argv
[]);
77 static int aji_test(int fd
, int argc
, char *argv
[]);
78 static int aji_show_clients(int fd
, int argc
, char *argv
[]);
79 static int aji_create_client(char *label
, struct ast_variable
*var
, int debug
);
80 static int aji_create_buddy(char *label
, struct aji_client
*client
);
81 static int aji_reload(void);
82 static int aji_load_config(void);
83 static void aji_pruneregister(struct aji_client
*client
);
84 static int aji_filter_roster(void *data
, ikspak
*pak
);
85 static int aji_get_roster(struct aji_client
*client
);
86 static int aji_client_info_handler(void *data
, ikspak
*pak
);
87 static int aji_dinfo_handler(void *data
, ikspak
*pak
);
88 static int aji_ditems_handler(void *data
, ikspak
*pak
);
89 static int aji_register_query_handler(void *data
, ikspak
*pak
);
90 static int aji_register_approve_handler(void *data
, ikspak
*pak
);
91 static int aji_reconnect(struct aji_client
*client
);
92 static iks
*jabber_make_auth(iksid
* id
, const char *pass
, const char *sid
);
93 /* No transports in this version */
95 static int aji_create_transport(char *label, struct aji_client *client);
96 static int aji_register_transport(void *data, ikspak *pak);
97 static int aji_register_transport2(void *data, ikspak *pak);
100 static char debug_usage
[] =
101 "Usage: jabber debug\n"
102 " Enables dumping of Jabber packets for debugging purposes.\n";
104 static char no_debug_usage
[] =
105 "Usage: jabber debug off\n"
106 " Disables dumping of Jabber packets for debugging purposes.\n";
108 static char reload_usage
[] =
109 "Usage: jabber reload\n"
110 " Enables reloading of Jabber module.\n";
112 static char test_usage
[] =
113 "Usage: jabber test [client]\n"
114 " Sends test message for debugging purposes. A specific client\n"
115 " as configured in jabber.conf can be optionally specified.\n";
117 static struct ast_cli_entry aji_cli
[] = {
118 { { "jabber", "debug", NULL
},
119 aji_do_debug
, "Enable Jabber debugging",
122 { { "jabber", "reload", NULL
},
123 aji_do_reload
, "Enable Jabber debugging",
126 { { "jabber", "show", "connected", NULL
},
127 aji_show_clients
, "Show state of clients and components",
130 { { "jabber", "debug", "off", NULL
},
131 aji_no_debug
, "Disable Jabber debug",
134 { { "jabber", "test", NULL
},
135 aji_test
, "Shows roster, but is generally used for mog's debugging.",
139 static char *app_ajisend
= "JabberSend";
141 static char *ajisend_synopsis
= "JabberSend(jabber,screenname,message)";
143 static char *ajisend_descrip
=
144 "JabberSend(Jabber,ScreenName,Message)\n"
145 " Jabber - Client or transport Asterisk uses to connect to Jabber\n"
146 " ScreenName - User Name to message.\n"
147 " Message - Message to be sent to the buddy\n";
149 static char *app_ajistatus
= "JabberStatus";
151 static char *ajistatus_synopsis
= "JabberStatus(Jabber,ScreenName,Variable)";
153 static char *ajistatus_descrip
=
154 "JabberStatus(Jabber,ScreenName,Variable)\n"
155 " Jabber - Client or transport Asterisk uses to connect to Jabber\n"
156 " ScreenName - User Name to retrieve status from.\n"
157 " Variable - Variable to store presence in will be 1-6.\n"
158 " In order, Online, Chatty, Away, XAway, DND, Offline\n"
159 " If not in roster variable will = 7\n";
161 struct aji_client_container clients
;
163 struct aji_capabilities
*capabilities
;
165 /*! \brief Global flags, initialized to default values */
166 static struct ast_flags globalflags
= { AJI_AUTOPRUNE
| AJI_AUTOREGISTER
};
169 * \brief Deletes the aji_client data structure.
170 * \param obj is the structure we will delete.
173 static void aji_client_destroy(struct aji_client
*obj
)
175 struct aji_message
*tmp
;
176 ASTOBJ_CONTAINER_DESTROYALL(&obj
->buddies
, aji_buddy_destroy
);
177 ASTOBJ_CONTAINER_DESTROY(&obj
->buddies
);
178 iks_filter_delete(obj
->f
);
179 iks_parser_delete(obj
->p
);
180 iks_stack_delete(obj
->stack
);
181 AST_LIST_LOCK(&obj
->messages
);
182 while ((tmp
= AST_LIST_REMOVE_HEAD(&obj
->messages
, list
))) {
188 AST_LIST_HEAD_DESTROY(&obj
->messages
);
193 * \brief Deletes the aji_buddy data structure.
194 * \param obj is the structure we will delete.
197 static void aji_buddy_destroy(struct aji_buddy
*obj
)
199 struct aji_resource
*tmp
;
201 while ((tmp
= obj
->resources
)) {
202 obj
->resources
= obj
->resources
->next
;
203 free(tmp
->description
);
210 static struct aji_version
*aji_find_version(char *node
, char *version
, ikspak
*pak
)
212 struct aji_capabilities
*list
= NULL
;
213 struct aji_version
*res
= NULL
;
218 node
= pak
->from
->full
;
220 version
= "none supplied.";
222 if(!strcasecmp(list
->node
, node
)) {
223 res
= list
->versions
;
225 if(!strcasecmp(res
->version
, version
))
230 res
= (struct aji_version
*)malloc(sizeof(struct aji_version
));
232 ast_log(LOG_ERROR
, "Out of memory!\n");
237 ast_copy_string(res
->version
, version
, sizeof(res
->version
));
238 res
->next
= list
->versions
;
239 list
->versions
= res
;
246 list
= (struct aji_capabilities
*)malloc(sizeof(struct aji_capabilities
));
248 ast_log(LOG_ERROR
, "Out of memory!\n");
251 res
= (struct aji_version
*)malloc(sizeof(struct aji_version
));
253 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
));
260 res
->next
= list
->versions
;
261 list
->versions
= res
;
262 list
->next
= capabilities
;
268 static struct aji_resource
*aji_find_resource(struct aji_buddy
*buddy
, char *name
)
270 struct aji_resource
*res
= NULL
;
273 res
= buddy
->resources
;
275 if (!strcasecmp(res
->resource
, name
)) {
283 static int gtalk_yuck(iks
*node
)
285 if (iks_find_with_attrib(node
, "c", "node", "http://www.google.com/xmpp/client/caps"))
291 * \brief Detects the highest bit in a number.
292 * \param Number you want to have evaluated.
293 * \return the highest power of 2 that can go into the number.
295 static int aji_highest_bit(int number
)
297 int x
= sizeof(number
) * 8 - 1;
301 if (number
& (1 << x
))
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 channel, and username,watched user, status var
334 static int aji_status_exec(struct ast_channel
*chan
, void *data
)
336 struct aji_client
*client
= NULL
;
337 struct aji_buddy
*buddy
= NULL
;
338 struct aji_resource
*r
= NULL
;
339 char *s
= NULL
, *sender
= NULL
, *jid
= NULL
, *screenname
= NULL
, *resource
= NULL
, *variable
= NULL
;
343 s
= ast_strdupa((char *) data
);
345 sender
= strsep(&s
, "|");
346 if (sender
&& (sender
[0] != '\0')) {
347 jid
= strsep(&s
, "|");
348 if (jid
&& (jid
[0] != '\0')) {
351 ast_log(LOG_ERROR
, "Bad arguments\n");
357 ast_log(LOG_ERROR
, "Out of memory\n");
361 if(!strchr(jid
, '/')) {
364 screenname
= strsep(&jid
, "/");
367 client
= ast_aji_get_client(sender
);
369 ast_log(LOG_WARNING
, "Could not find Connection.\n");
372 if(!&client
->buddies
) {
373 ast_log(LOG_WARNING
, "No buddies for connection.\n");
376 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, (resource
)?screenname
:jid
);
378 ast_log(LOG_WARNING
, "Could not find Buddy in list.\n");
381 r
= aji_find_resource(buddy
, resource
);
382 if(!r
&& buddy
->resources
) {
383 r
= buddy
->resources
;
386 ast_log(LOG_NOTICE
, "Resource %s of buddy %s not found \n", resource
, screenname
);
389 sprintf(status
, "%d", stat
);
390 pbx_builtin_setvar_helper(chan
, variable
, status
);
395 * \brief Dial plan function to send a message.
396 * \param channel, and data, data is sender, reciever, message.
399 static int aji_send_exec(struct ast_channel
*chan
, void *data
)
401 struct aji_client
*client
= NULL
;
403 char *s
= NULL
, *sender
= NULL
, *recipient
= NULL
, *message
= NULL
;
406 ast_log(LOG_ERROR
, "Out of memory\n");
409 s
= ast_strdupa((char *) data
);
411 sender
= strsep(&s
, "|");
412 if (sender
&& (sender
[0] != '\0')) {
413 recipient
= strsep(&s
, "|");
414 if (recipient
&& (recipient
[0] != '\0')) {
417 ast_log(LOG_ERROR
, "Bad arguments \n");
422 if (!(client
= ast_aji_get_client(sender
))) {
423 ast_log(LOG_WARNING
, "Could not find Sender.\n");
426 if (strchr(recipient
, '@') && message
)
427 ast_aji_send(client
, recipient
, message
);
432 * \brief the debug loop.
433 * \param aji_client structure, xml data as string, size of string, direction of packet, 1 for inbound 0 for outbound.
435 static void aji_log_hook(void *data
, const char *xmpp
, size_t size
, int is_incoming
)
437 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
438 manager_event(EVENT_FLAG_USER
, "JabberEvent", "Account: %s\r\nPacket: %s", client
->name
, xmpp
);
442 ast_verbose("\nJABBER: %s INCOMING: %s\n", client
->name
, xmpp
);
444 if( strlen(xmpp
) == 1) {
445 if(option_debug
> 2 && xmpp
[0] == ' ')
446 ast_verbose("\nJABBER: Keep alive packet\n");
448 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client
->name
, xmpp
);
452 ASTOBJ_UNREF(client
, aji_client_destroy
);
456 * \brief The action hook parses the inbound packets, constantly running.
457 * \param aji client structure, type of packet, the actual packet.
458 * \return IKS_OK or IKS_HOOK .
460 static int aji_act_hook(void *data
, int type
, iks
*node
)
462 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
467 ast_log(LOG_ERROR
, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
468 ASTOBJ_UNREF(client
, aji_client_destroy
);
472 pak
= iks_packet(node
);
474 if (!client
->component
) { /*client */
477 if (client
->usetls
&& !iks_is_secure(client
->p
)) {
479 iks_start_tls(client
->p
);
481 ast_log(LOG_ERROR
, "gnuTLS not installed.\n");
484 if (!client
->usesasl
) {
485 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
);
486 auth
= jabber_make_auth(client
->jid
, client
->password
, iks_find_attrib(node
, "id"));
488 iks_insert_attrib(auth
, "id", client
->mid
);
489 iks_insert_attrib(auth
, "to", client
->jid
->server
);
490 ast_aji_increment_mid(client
->mid
);
491 iks_send(client
->p
, auth
);
494 ast_log(LOG_ERROR
, "Out of memory.\n");
498 case IKS_NODE_NORMAL
:
501 if (!strcmp("stream:features", iks_name(node
))) {
502 features
= iks_stream_features(node
);
503 if (client
->usesasl
) {
504 if (client
->usetls
&& !iks_is_secure(client
->p
))
506 if (client
->authorized
) {
507 if (features
& IKS_STREAM_BIND
) {
508 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
);
509 auth
= iks_make_resource_bind(client
->jid
);
511 iks_insert_attrib(auth
, "id", client
->mid
);
512 ast_aji_increment_mid(client
->mid
);
513 iks_send(client
->p
, auth
);
516 ast_log(LOG_ERROR
, "Out of memory.\n");
520 if (features
& IKS_STREAM_SESSION
) {
521 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
);
522 auth
= iks_make_session();
524 iks_insert_attrib(auth
, "id", "auth");
525 ast_aji_increment_mid(client
->mid
);
526 iks_send(client
->p
, auth
);
529 ast_log(LOG_ERROR
, "Out of memory.\n");
533 features
= aji_highest_bit(features
);
534 if (features
== IKS_STREAM_SASL_MD5
)
535 iks_start_sasl(client
->p
, IKS_SASL_DIGEST_MD5
, client
->jid
->user
, client
->password
);
537 if (features
== IKS_STREAM_SASL_PLAIN
) {
541 iks_insert_attrib(x
, "xmlns", IKS_NS_XMPP_SASL
);
542 int len
= strlen(client
->jid
->user
) + strlen(client
->password
) + 3;
543 /* XXX Check return values XXX */
544 char *s
= ast_malloc(80 + len
);
545 char *base64
= ast_malloc(80 + len
* 2);
546 iks_insert_attrib(x
, "mechanism", "PLAIN");
547 sprintf(s
, "%c%s%c%s", 0, client
->jid
->user
, 0, client
->password
);
548 ast_base64encode(base64
, (const unsigned char *) s
, len
, len
* 2);
549 iks_insert_cdata(x
, base64
, 0);
550 iks_send(client
->p
, x
);
557 ast_log(LOG_ERROR
, "Out of memory.\n");
563 } else if (!strcmp("failure", iks_name(node
))) {
564 ast_log(LOG_ERROR
, "JABBER: encryption failure. possible bad password.\n");
565 } else if (!strcmp("success", iks_name(node
))) {
566 client
->authorized
= 1;
567 iks_send_header(client
->p
, client
->jid
->server
);
572 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
573 ASTOBJ_UNREF(client
, aji_client_destroy
);
577 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
578 ASTOBJ_UNREF(client
, aji_client_destroy
);
582 } else if (client
->state
!= AJI_CONNECTED
&& client
->component
) {
585 if (client
->state
== AJI_DISCONNECTED
) {
586 char secret
[160], shasum
[320], *handshake
;
588 sprintf(secret
, "%s%s", pak
->id
, client
->password
);
589 ast_sha1_hash(shasum
, secret
);
591 asprintf(&handshake
, "<handshake>%s</handshake>", shasum
);
593 iks_send_raw(client
->p
, handshake
);
597 client
->state
= AJI_CONNECTING
;
598 if(iks_recv(client
->p
,1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
599 client
->state
= AJI_CONNECTED
;
601 ast_log(LOG_WARNING
,"Jabber didn't seem to handshake, failed to authenicate.\n");
606 case IKS_NODE_NORMAL
:
610 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
611 ASTOBJ_UNREF(client
, aji_client_destroy
);
615 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
616 ASTOBJ_UNREF(client
, aji_client_destroy
);
624 ast_log(LOG_DEBUG
, "JABBER: I Don't know what to do with you NONE\n");
626 case IKS_PAK_MESSAGE
:
627 aji_handle_message(client
, pak
);
629 ast_log(LOG_DEBUG
, "JABBER: I Don't know what to do with you MESSAGE\n");
631 case IKS_PAK_PRESENCE
:
632 aji_handle_presence(client
, pak
);
634 ast_log(LOG_DEBUG
, "JABBER: I Do know how to handle presence!!\n");
637 aji_handle_subscribe(client
, pak
);
639 ast_log(LOG_DEBUG
, "JABBER: I Dont know S10N subscribe!!\n");
643 ast_log(LOG_DEBUG
, "JABBER: I Dont have an IQ!!!\n");
644 aji_handle_iq(client
, node
);
648 ast_log(LOG_DEBUG
, "JABBER: I Dont know %i\n", pak
->type
);
652 iks_filter_packet(client
->f
, pak
);
657 ASTOBJ_UNREF(client
, aji_client_destroy
);
661 static int aji_register_approve_handler(void *data
, ikspak
*pak
)
663 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
664 iks
*iq
= NULL
, *presence
= NULL
, *x
= NULL
;
667 presence
= iks_new("presence");
669 if (client
&& iq
&& presence
&& x
) {
670 if (!iks_find(pak
->query
, "remove")) {
671 iks_insert_attrib(iq
, "from", client
->jid
->full
);
672 iks_insert_attrib(iq
, "to", pak
->from
->full
);
673 iks_insert_attrib(iq
, "id", pak
->id
);
674 iks_insert_attrib(iq
, "type", "result");
675 iks_send(client
->p
, iq
);
677 iks_insert_attrib(presence
, "from", client
->jid
->full
);
678 iks_insert_attrib(presence
, "to", pak
->from
->partial
);
679 iks_insert_attrib(presence
, "id", client
->mid
);
680 ast_aji_increment_mid(client
->mid
);
681 iks_insert_attrib(presence
, "type", "subscribe");
682 iks_insert_attrib(x
, "xmlns", "vcard-temp:x:update");
683 iks_insert_node(presence
, x
);
684 iks_send(client
->p
, presence
);
687 ast_log(LOG_ERROR
, "Out of memory.\n");
693 iks_delete(presence
);
696 ASTOBJ_UNREF(client
, aji_client_destroy
);
697 return IKS_FILTER_EAT
;
700 static int aji_register_query_handler(void *data
, ikspak
*pak
)
702 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
703 struct aji_buddy
*buddy
= NULL
;
706 client
= (struct aji_client
*) data
;
708 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
710 ast_verbose("Someone.... %s tried to register but they aren't allowed\n", pak
->from
->partial
);
711 iks
*iq
= NULL
, *query
= NULL
, *error
= NULL
, *notacceptable
= NULL
;
713 query
= iks_new("query");
714 error
= iks_new("error");
715 notacceptable
= iks_new("not-acceptable");
716 if(iq
&& query
&& error
&& notacceptable
) {
717 iks_insert_attrib(iq
, "type", "error");
718 iks_insert_attrib(iq
, "from", client
->user
);
719 iks_insert_attrib(iq
, "to", pak
->from
->full
);
720 iks_insert_attrib(iq
, "id", pak
->id
);
721 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
722 iks_insert_attrib(error
, "code" , "406");
723 iks_insert_attrib(error
, "type", "modify");
724 iks_insert_attrib(notacceptable
, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
725 iks_insert_node(iq
, query
);
726 iks_insert_node(iq
, error
);
727 iks_insert_node(error
, notacceptable
);
728 iks_send(client
->p
, iq
);
730 ast_log(LOG_ERROR
, "Out of memory.\n");
739 iks_delete(notacceptable
);
740 } else if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
741 iks
*iq
= NULL
, *query
= NULL
, *instructions
= NULL
;
742 char *explain
= "Welcome to Asterisk - the Open Source PBX.\n";
744 query
= iks_new("query");
745 instructions
= iks_new("instructions");
746 if (iq
&& query
&& instructions
&& client
) {
747 iks_insert_attrib(iq
, "from", client
->user
);
748 iks_insert_attrib(iq
, "to", pak
->from
->full
);
749 iks_insert_attrib(iq
, "id", pak
->id
);
750 iks_insert_attrib(iq
, "type", "result");
751 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
752 iks_insert_cdata(instructions
, explain
, 0);
753 iks_insert_node(iq
, query
);
754 iks_insert_node(query
, instructions
);
755 iks_send(client
->p
, iq
);
757 ast_log(LOG_ERROR
, "Out of memory.\n");
764 iks_delete(instructions
);
766 ASTOBJ_UNREF(client
, aji_client_destroy
);
767 return IKS_FILTER_EAT
;
770 static int aji_ditems_handler(void *data
, ikspak
*pak
)
772 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
775 if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
776 iks
*iq
= NULL
, *query
= NULL
, *item
= NULL
;
778 query
= iks_new("query");
779 item
= iks_new("item");
781 if (iq
&& query
&& item
) {
782 iks_insert_attrib(iq
, "from", client
->user
);
783 iks_insert_attrib(iq
, "to", pak
->from
->full
);
784 iks_insert_attrib(iq
, "id", pak
->id
);
785 iks_insert_attrib(iq
, "type", "result");
786 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
787 iks_insert_attrib(item
, "node", "http://jabber.org/protocol/commands");
788 iks_insert_attrib(item
, "name", "Million Dollar Asterisk Commands");
789 iks_insert_attrib(item
, "jid", client
->user
);
791 iks_insert_node(iq
, query
);
792 iks_insert_node(query
, item
);
793 iks_send(client
->p
, iq
);
795 ast_log(LOG_ERROR
, "Out of memory.\n");
804 } else if (!strcasecmp(node
, "http://jabber.org/protocol/commands")) {
805 iks
*iq
, *query
, *confirm
;
807 query
= iks_new("query");
808 confirm
= iks_new("item");
809 if (iq
&& query
&& confirm
&& client
) {
810 iks_insert_attrib(iq
, "from", client
->user
);
811 iks_insert_attrib(iq
, "to", pak
->from
->full
);
812 iks_insert_attrib(iq
, "id", pak
->id
);
813 iks_insert_attrib(iq
, "type", "result");
814 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
815 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
816 iks_insert_attrib(confirm
, "node", "confirmaccount");
817 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
818 iks_insert_attrib(confirm
, "jid", "blog.astjab.org");
820 iks_insert_node(iq
, query
);
821 iks_insert_node(query
, confirm
);
822 iks_send(client
->p
, iq
);
824 ast_log(LOG_ERROR
, "Out of memory.\n");
833 } else if (!strcasecmp(node
, "confirmaccount")) {
834 iks
*iq
= NULL
, *query
= NULL
, *feature
= NULL
;
837 query
= iks_new("query");
838 feature
= iks_new("feature");
840 if (iq
&& query
&& feature
&& client
) {
841 iks_insert_attrib(iq
, "from", client
->user
);
842 iks_insert_attrib(iq
, "to", pak
->from
->full
);
843 iks_insert_attrib(iq
, "id", pak
->id
);
844 iks_insert_attrib(iq
, "type", "result");
845 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
846 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
847 iks_insert_node(iq
, query
);
848 iks_insert_node(query
, feature
);
849 iks_send(client
->p
, iq
);
851 ast_log(LOG_ERROR
, "Out of memory.\n");
861 ASTOBJ_UNREF(client
, aji_client_destroy
);
862 return IKS_FILTER_EAT
;
866 static int aji_client_info_handler(void *data
, ikspak
*pak
)
868 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
869 struct aji_resource
*resource
= NULL
;
870 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
872 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
873 if (pak
->subtype
== IKS_TYPE_RESULT
) {
875 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
876 ASTOBJ_UNREF(client
, aji_client_destroy
);
877 return IKS_FILTER_EAT
;
879 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
880 resource
->cap
->jingle
= 1;
882 resource
->cap
->jingle
= 0;
883 } else if (pak
->subtype
== IKS_TYPE_GET
) {
884 iks
*iq
, *disco
, *ident
, *google
, *query
;
886 query
= iks_new("query");
887 ident
= iks_new("identity");
888 disco
= iks_new("feature");
889 google
= iks_new("feature");
890 if (iq
&& ident
&& disco
&& google
) {
891 iks_insert_attrib(iq
, "from", client
->jid
->full
);
892 iks_insert_attrib(iq
, "to", pak
->from
->full
);
893 iks_insert_attrib(iq
, "type", "result");
894 iks_insert_attrib(iq
, "id", pak
->id
);
895 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
896 iks_insert_attrib(ident
, "category", "client");
897 iks_insert_attrib(ident
, "type", "pc");
898 iks_insert_attrib(ident
, "name", "asterisk");
899 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco#info");
900 iks_insert_attrib(google
, "var", "http://www.google.com/xmpp/protocol/voice/v1");
901 iks_insert_node(iq
, query
);
902 iks_insert_node(query
, ident
);
903 iks_insert_node(query
, google
);
904 iks_insert_node(query
, disco
);
905 iks_send(client
->p
, iq
);
907 ast_log(LOG_ERROR
, "Out of Memory.\n");
918 } else if (pak
->subtype
== IKS_TYPE_ERROR
) {
919 ast_log(LOG_NOTICE
, "User %s does not support discovery.\n", pak
->from
->full
);
921 ASTOBJ_UNREF(client
, aji_client_destroy
);
922 return IKS_FILTER_EAT
;
925 static int aji_dinfo_handler(void *data
, ikspak
*pak
)
927 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
929 struct aji_resource
*resource
= NULL
;
930 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
932 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
933 if (pak
->subtype
== IKS_TYPE_ERROR
) {
934 ast_log(LOG_WARNING
, "Recieved error from a client, turn on jabber debug!\n");
935 return IKS_FILTER_EAT
;
937 if (pak
->subtype
== IKS_TYPE_RESULT
) {
939 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
940 ASTOBJ_UNREF(client
, aji_client_destroy
);
941 return IKS_FILTER_EAT
;
943 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
944 resource
->cap
->jingle
= 1;
946 resource
->cap
->jingle
= 0;
947 } else if (pak
->subtype
== IKS_TYPE_GET
&& !(node
= iks_find_attrib(pak
->query
, "node"))) {
948 iks
*iq
, *query
, *identity
, *disco
, *reg
, *commands
, *gateway
, *version
, *vcard
, *search
;
951 query
= iks_new("query");
952 identity
= iks_new("identity");
953 disco
= iks_new("feature");
954 reg
= iks_new("feature");
955 commands
= iks_new("feature");
956 gateway
= iks_new("feature");
957 version
= iks_new("feature");
958 vcard
= iks_new("feature");
959 search
= iks_new("feature");
961 if (iq
&& query
&& identity
&& disco
&& reg
&& commands
&& gateway
&& version
&& vcard
&& search
&& client
) {
962 iks_insert_attrib(iq
, "from", client
->user
);
963 iks_insert_attrib(iq
, "to", pak
->from
->full
);
964 iks_insert_attrib(iq
, "id", pak
->id
);
965 iks_insert_attrib(iq
, "type", "result");
966 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
967 iks_insert_attrib(identity
, "category", "gateway");
968 iks_insert_attrib(identity
, "type", "pstn");
969 iks_insert_attrib(identity
, "name", "Asterisk The Open Source PBX");
970 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco");
971 iks_insert_attrib(reg
, "var", "jabber:iq:register");
972 iks_insert_attrib(commands
, "var", "http://jabber.org/protocol/commands");
973 iks_insert_attrib(gateway
, "var", "jabber:iq:gateway");
974 iks_insert_attrib(version
, "var", "jabber:iq:version");
975 iks_insert_attrib(vcard
, "var", "vcard-temp");
976 iks_insert_attrib(search
, "var", "jabber:iq:search");
978 iks_insert_node(iq
, query
);
979 iks_insert_node(query
, identity
);
980 iks_insert_node(query
, disco
);
981 iks_insert_node(query
, reg
);
982 iks_insert_node(query
, commands
);
983 iks_insert_node(query
, gateway
);
984 iks_insert_node(query
, version
);
985 iks_insert_node(query
, vcard
);
986 iks_insert_node(query
, search
);
987 iks_send(client
->p
, iq
);
989 ast_log(LOG_ERROR
, "Out of memory.\n");
997 iks_delete(identity
);
1003 iks_delete(commands
);
1005 iks_delete(gateway
);
1007 iks_delete(version
);
1013 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "http://jabber.org/protocol/commands")) {
1014 iks
*iq
, *query
, *confirm
;
1016 query
= iks_new("query");
1017 confirm
= iks_new("item");
1019 if (iq
&& query
&& confirm
&& client
) {
1020 iks_insert_attrib(iq
, "from", client
->user
);
1021 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1022 iks_insert_attrib(iq
, "id", pak
->id
);
1023 iks_insert_attrib(iq
, "type", "result");
1024 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
1025 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
1026 iks_insert_attrib(confirm
, "node", "confirmaccount");
1027 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
1028 iks_insert_attrib(confirm
, "jid", client
->user
);
1029 iks_insert_node(iq
, query
);
1030 iks_insert_node(query
, confirm
);
1031 iks_send(client
->p
, iq
);
1033 ast_log(LOG_ERROR
, "Out of memory.\n");
1040 iks_delete(confirm
);
1042 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "confirmaccount")) {
1043 iks
*iq
, *query
, *feature
;
1046 query
= iks_new("query");
1047 feature
= iks_new("feature");
1049 if (iq
&& query
&& feature
&& client
) {
1050 iks_insert_attrib(iq
, "from", client
->user
);
1051 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1052 iks_insert_attrib(iq
, "id", pak
->id
);
1053 iks_insert_attrib(iq
, "type", "result");
1054 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1055 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
1056 iks_insert_node(iq
, query
);
1057 iks_insert_node(query
, feature
);
1058 iks_send(client
->p
, iq
);
1060 ast_log(LOG_ERROR
, "Out of memory.\n");
1067 iks_delete(feature
);
1070 ASTOBJ_UNREF(client
, aji_client_destroy
);
1071 return IKS_FILTER_EAT
;
1075 * \brief Handles <iq> tags.
1076 * \param client structure and the iq node.
1079 static void aji_handle_iq(struct aji_client
*client
, iks
*node
)
1081 /*Nothing to see here */
1085 * \brief Handles presence packets.
1086 * \param client structure and the node.
1089 static void aji_handle_message(struct aji_client
*client
, ikspak
*pak
)
1091 struct aji_message
*insert
, *tmp
;
1094 if (!(insert
= ast_calloc(1, sizeof(struct aji_message
))))
1096 time(&insert
->arrived
);
1097 if (iks_find_cdata(pak
->x
, "body"))
1098 insert
->message
= ast_strdup(iks_find_cdata(pak
->x
, "body"));
1100 ast_copy_string(insert
->id
, pak
->id
, sizeof(insert
->message
));
1102 insert
->from
= ast_strdup(pak
->from
->full
);
1103 AST_LIST_LOCK(&client
->messages
);
1104 AST_LIST_TRAVERSE_SAFE_BEGIN(&client
->messages
, tmp
, list
) {
1106 AST_LIST_REMOVE_CURRENT(&client
->messages
, list
);
1111 } else if (difftime(time(NULL
), tmp
->arrived
) >= client
->message_timeout
) {
1113 AST_LIST_REMOVE_CURRENT(&client
->messages
, list
);
1120 AST_LIST_TRAVERSE_SAFE_END
;
1121 AST_LIST_INSERT_HEAD(&client
->messages
, insert
, list
);
1122 AST_LIST_UNLOCK(&client
->messages
);
1125 static void aji_handle_presence(struct aji_client
*client
, ikspak
*pak
)
1127 int status
, priority
;
1128 struct aji_buddy
*buddy
;
1129 struct aji_resource
*tmp
= NULL
, *last
= NULL
, *found
= NULL
;
1130 char *ver
, *node
, *descrip
, *type
;
1132 if(client
->state
!= AJI_CONNECTED
)
1133 aji_create_buddy(pak
->from
->partial
, client
);
1135 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1137 ast_log(LOG_NOTICE
, "Got presence packet from %s, someone not in our roster!!!!\n", pak
->from
->partial
);
1140 type
= iks_find_attrib(pak
->x
, "type");
1141 if(client
->component
&& type
&&!strcasecmp("probe", type
)) {
1142 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), 1, client
->statusmessage
);
1143 ast_verbose("what i was looking for \n");
1145 ASTOBJ_WRLOCK(buddy
);
1146 status
= (pak
->show
) ? pak
->show
: 6;
1147 priority
= atoi((iks_find_cdata(pak
->x
, "priority")) ? iks_find_cdata(pak
->x
, "priority") : "0");
1148 tmp
= buddy
->resources
;
1149 descrip
= ast_strdup(iks_find_cdata(pak
->x
,"status"));
1152 if (!strcasecmp(tmp
->resource
, pak
->from
->resource
)) {
1153 tmp
->status
= status
;
1154 if (tmp
->description
) free(tmp
->description
);
1155 tmp
->description
= descrip
;
1157 if (status
== 6) { /* Sign off Destroy resource */
1158 if (last
&& found
->next
) {
1159 last
->next
= found
->next
;
1162 buddy
->resources
= found
->next
;
1164 buddy
->resources
= NULL
;
1165 } else if (!found
->next
) {
1169 buddy
->resources
= NULL
;
1175 if (tmp
->priority
!= priority
) {
1176 found
->priority
= priority
;
1177 if (!last
&& !found
->next
)
1180 last
->next
= found
->next
;
1182 buddy
->resources
= found
->next
;
1184 tmp
= buddy
->resources
;
1185 if (!buddy
->resources
)
1186 buddy
->resources
= found
;
1188 if (found
->priority
> tmp
->priority
) {
1193 buddy
->resources
= found
;
1210 if (!found
&& status
!= 6) {
1211 found
= (struct aji_resource
*) malloc(sizeof(struct aji_resource
));
1212 memset(found
, 0, sizeof(struct aji_resource
));
1215 ast_log(LOG_ERROR
, "Out of memory!\n");
1218 ast_copy_string(found
->resource
, pak
->from
->resource
, sizeof(found
->resource
));
1219 found
->status
= status
;
1220 found
->description
= descrip
;
1221 found
->priority
= priority
;
1224 tmp
= buddy
->resources
;
1226 if (found
->priority
> tmp
->priority
) {
1231 buddy
->resources
= found
;
1242 buddy
->resources
= found
;
1244 ASTOBJ_UNLOCK(buddy
);
1245 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
1247 node
= iks_find_attrib(iks_find(pak
->x
, "c"), "node");
1248 ver
= iks_find_attrib(iks_find(pak
->x
, "c"), "ver");
1250 if(status
!=6 && !found
->cap
) {
1251 found
->cap
= aji_find_version(node
, ver
, pak
);
1252 if(gtalk_yuck(pak
->x
)) /* gtalk should do discover */
1253 found
->cap
->jingle
= 1;
1254 if(found
->cap
->jingle
&& option_debug
> 4)
1255 ast_log(LOG_DEBUG
,"Special case for google till they support discover.\n");
1259 query
= iks_new("query");
1261 iks_insert_attrib(iq
, "type", "get");
1262 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1263 iks_insert_attrib(iq
,"from",iks_find_attrib(pak
->x
,"to"));
1264 iks_insert_attrib(iq
, "id", client
->mid
);
1265 ast_aji_increment_mid(client
->mid
);
1266 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1267 iks_insert_node(iq
, query
);
1268 iks_send(client
->p
, iq
);
1271 ast_log(LOG_ERROR
, "Out of memory.\n");
1278 if (option_verbose
> 4) {
1279 switch (pak
->subtype
) {
1280 case IKS_TYPE_AVAILABLE
:
1281 ast_verbose(VERBOSE_PREFIX_3
"JABBER: I am available ^_* %i\n", pak
->subtype
);
1283 case IKS_TYPE_UNAVAILABLE
:
1284 ast_verbose(VERBOSE_PREFIX_3
"JABBER: I am unavailable ^_* %i\n", pak
->subtype
);
1287 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Ohh sexy and the wrong type: %i\n", pak
->subtype
);
1289 switch (pak
->show
) {
1290 case IKS_SHOW_UNAVAILABLE
:
1291 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1293 case IKS_SHOW_AVAILABLE
:
1294 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type is available\n");
1297 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1300 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type is away\n");
1303 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1306 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1309 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Kinky! how did that happen %i\n", pak
->show
);
1315 * \brief handles subscription requests.
1316 * \param aji_client struct and xml packet.
1319 static void aji_handle_subscribe(struct aji_client
*client
, ikspak
*pak
)
1321 if(pak
->subtype
== IKS_TYPE_SUBSCRIBE
) {
1322 iks
*presence
= NULL
, *status
= NULL
;
1323 presence
= iks_new("presence");
1324 status
= iks_new("status");
1325 if(presence
&& status
) {
1326 iks_insert_attrib(presence
, "type", "subscribed");
1327 iks_insert_attrib(presence
, "to", pak
->from
->full
);
1328 iks_insert_attrib(presence
, "from", iks_find_attrib(pak
->x
, "to"));
1330 iks_insert_attrib(presence
, "id", pak
->id
);
1331 iks_insert_cdata(status
, "Asterisk has approved subscription", 0);
1332 iks_insert_node(presence
, status
);
1333 iks_send(client
->p
, presence
);
1335 ast_log(LOG_ERROR
, "Unable to allocate nodes\n");
1337 iks_delete(presence
);
1340 if(client
->component
)
1341 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), 1, client
->statusmessage
);
1343 if (option_verbose
> 4) {
1344 switch (pak
->subtype
) {
1345 case IKS_TYPE_SUBSCRIBE
:
1346 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1348 case IKS_TYPE_SUBSCRIBED
:
1349 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1351 case IKS_TYPE_UNSUBSCRIBE
:
1352 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1354 case IKS_TYPE_UNSUBSCRIBED
:
1355 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1357 default: /*IKS_TYPE_ERROR: */
1358 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1365 * \brief sends messages.
1366 * \param aji_client struct , reciever, message.
1369 int ast_aji_send(struct aji_client
*client
, const char *address
, const char *message
)
1372 iks
*message_packet
= NULL
;
1373 if (client
->state
== AJI_CONNECTED
) {
1374 message_packet
= iks_make_msg(IKS_TYPE_CHAT
, address
, message
);
1375 if (message_packet
) {
1376 iks_insert_attrib(message_packet
, "from", client
->jid
->full
);
1377 res
= iks_send(client
->p
, message_packet
);
1379 ast_log(LOG_ERROR
, "Out of memory.\n");
1382 iks_delete(message_packet
);
1384 ast_log(LOG_WARNING
, "JABBER: Not connected can't send\n");
1389 * \brief create a chatroom.
1390 * \param aji_client struct , room, server, topic for the room.
1393 int ast_aji_create_chat(struct aji_client
*client
, char *room
, char *server
, char *topic
)
1399 iks_insert_attrib(iq
, "type", "get");
1400 iks_insert_attrib(iq
, "to", server
);
1401 iks_insert_attrib(iq
, "id", client
->mid
);
1402 ast_aji_increment_mid(client
->mid
);
1403 iks_send(client
->p
, iq
);
1405 ast_log(LOG_ERROR
, "Out of memory.\n");
1410 * \brief join a chatroom.
1411 * \param aji_client struct , room.
1414 int ast_aji_join_chat(struct aji_client
*client
, char *room
)
1417 iks
*presence
= NULL
, *priority
= NULL
;
1418 presence
= iks_new("presence");
1419 priority
= iks_new("priority");
1420 if (presence
&& priority
&& client
) {
1421 iks_insert_cdata(priority
, "0", 1);
1422 iks_insert_attrib(presence
, "to", room
);
1423 iks_insert_node(presence
, priority
);
1424 res
= iks_send(client
->p
, presence
);
1425 iks_insert_cdata(priority
, "5", 1);
1426 iks_insert_attrib(presence
, "to", room
);
1427 res
= iks_send(client
->p
, presence
);
1429 ast_log(LOG_ERROR
, "Out of memory.\n");
1431 iks_delete(presence
);
1433 iks_delete(priority
);
1438 * \brief invite to a chatroom.
1439 * \param aji_client struct ,user, room, message.
1442 int ast_aji_invite_chat(struct aji_client
*client
, char *user
, char *room
, char *message
)
1445 iks
*invite
, *body
, *namespace;
1447 invite
= iks_new("message");
1448 body
= iks_new("body");
1449 namespace = iks_new("x");
1450 if (client
&& invite
&& body
&& namespace) {
1451 iks_insert_attrib(invite
, "to", user
);
1452 iks_insert_attrib(invite
, "id", client
->mid
);
1453 ast_aji_increment_mid(client
->mid
);
1454 iks_insert_cdata(body
, message
, 0);
1455 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
1456 iks_insert_attrib(namespace, "jid", room
);
1457 iks_insert_node(invite
, body
);
1458 iks_insert_node(invite
, namespace);
1459 res
= iks_send(client
->p
, invite
);
1461 ast_log(LOG_ERROR
, "Out of memory.\n");
1465 iks_delete(namespace);
1473 * \brief receive message loop.
1474 * \param aji_client struct.
1477 static void *aji_recv_loop(void *data
)
1479 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1482 if (res
!= IKS_OK
) {
1483 while(res
!= IKS_OK
) {
1484 if(option_verbose
> 3)
1485 ast_verbose("JABBER: reconnecting.\n");
1486 res
= aji_reconnect(client
);
1491 res
= iks_recv(client
->p
, 1);
1493 if (res
== IKS_HOOK
)
1494 ast_log(LOG_WARNING
, "JABBER: Got hook event.\n");
1495 else if (res
== IKS_NET_TLSFAIL
)
1496 ast_log(LOG_WARNING
, "JABBER: Failure in TLS.\n");
1497 else if (client
->timeout
== 0 && client
->state
== AJI_CONNECTED
) {
1498 res
= iks_send_raw(client
->p
, " ");
1500 client
->timeout
= 50;
1502 ast_log(LOG_WARNING
, "JABBER: Network Timeout\n");
1503 } else if (res
== IKS_NET_RWERR
)
1504 ast_log(LOG_WARNING
, "JABBER: socket read error\n");
1506 ASTOBJ_UNREF(client
, aji_client_destroy
);
1511 * \brief increments the mid field for messages and other events.
1512 * \param message id.
1515 void ast_aji_increment_mid(char *mid
)
1519 for (i
= strlen(mid
) - 1; i
>= 0; i
--) {
1520 if (mid
[i
] != 'z') {
1521 mid
[i
] = mid
[i
] + 1;
1530 * \brief attempts to register to a transport.
1531 * \param aji_client struct, and xml packet.
1532 * \return IKS_FILTER_EAT.
1534 /*allows for registering to transport , was too sketch and is out for now. */
1535 /*static int aji_register_transport(void *data, ikspak *pak)
1537 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1539 struct aji_buddy *buddy = NULL;
1540 iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
1542 if (client && send) {
1543 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
1544 ASTOBJ_RDLOCK(iterator);
1545 if (iterator->btype == AJI_TRANS) {
1548 ASTOBJ_UNLOCK(iterator);
1550 iks_filter_remove_hook(client->f, aji_register_transport);
1551 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);
1552 iks_insert_attrib(send, "to", buddy->host);
1553 iks_insert_attrib(send, "id", client->mid);
1554 ast_aji_increment_mid(client->mid);
1555 iks_insert_attrib(send, "from", client->user);
1556 res = iks_send(client->p, send);
1558 ast_log(LOG_ERROR, "Out of memory.\n");
1562 ASTOBJ_UNREF(client, aji_client_destroy);
1563 return IKS_FILTER_EAT;
1568 * \brief attempts to register to a transport step 2.
1569 * \param aji_client struct, and xml packet.
1570 * \return IKS_FILTER_EAT.
1572 /* more of the same blob of code, too wonky for now*/
1573 /* static int aji_register_transport2(void *data, ikspak *pak)
1575 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1577 struct aji_buddy *buddy = NULL;
1579 iks *regiq = iks_new("iq");
1580 iks *regquery = iks_new("query");
1581 iks *reguser = iks_new("username");
1582 iks *regpass = iks_new("password");
1584 if (client && regquery && reguser && regpass && regiq) {
1585 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
1586 ASTOBJ_RDLOCK(iterator);
1587 if (iterator->btype == AJI_TRANS)
1588 buddy = iterator; ASTOBJ_UNLOCK(iterator);
1590 iks_filter_remove_hook(client->f, aji_register_transport2);
1591 iks_insert_attrib(regiq, "to", buddy->host);
1592 iks_insert_attrib(regiq, "type", "set");
1593 iks_insert_attrib(regiq, "id", client->mid);
1594 ast_aji_increment_mid(client->mid);
1595 iks_insert_attrib(regiq, "from", client->user);
1596 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
1597 iks_insert_cdata(reguser, buddy->user, 0);
1598 iks_insert_cdata(regpass, buddy->pass, 0);
1599 iks_insert_node(regiq, regquery);
1600 iks_insert_node(regquery, reguser);
1601 iks_insert_node(regquery, regpass);
1602 res = iks_send(client->p, regiq);
1604 ast_log(LOG_ERROR, "Out of memory.\n");
1608 iks_delete(regquery);
1610 iks_delete(reguser);
1612 iks_delete(regpass);
1613 ASTOBJ_UNREF(client, aji_client_destroy);
1614 return IKS_FILTER_EAT;
1618 * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
1619 * \param aji_client struct.
1622 static void aji_pruneregister(struct aji_client
*client
)
1625 iks
*removeiq
= iks_new("iq");
1626 iks
*removequery
= iks_new("query");
1627 iks
*removeitem
= iks_new("item");
1628 iks
*send
= iks_make_iq(IKS_TYPE_GET
, "http://jabber.org/protocol/disco#items");
1630 if (client
&& removeiq
&& removequery
&& removeitem
&& send
) {
1631 iks_insert_node(removeiq
, removequery
);
1632 iks_insert_node(removequery
, removeitem
);
1633 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1634 ASTOBJ_RDLOCK(iterator
);
1635 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
1636 * be called at the same time */
1637 if (ast_test_flag(iterator
, AJI_AUTOPRUNE
)) {
1638 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE
, iterator
->name
,
1639 "GoodBye your status is no longer needed by Asterisk the Open Source PBX"
1640 " so I am no longer subscribing to your presence.\n"));
1641 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED
, iterator
->name
,
1642 "GoodBye you are no longer in the asterisk config file so I am removing"
1643 " your access to my presence.\n"));
1644 iks_insert_attrib(removeiq
, "from", client
->jid
->full
);
1645 iks_insert_attrib(removeiq
, "type", "set");
1646 iks_insert_attrib(removequery
, "xmlns", "jabber:iq:roster");
1647 iks_insert_attrib(removeitem
, "jid", iterator
->name
);
1648 iks_insert_attrib(removeitem
, "subscription", "remove");
1649 res
= iks_send(client
->p
, removeiq
);
1650 } else if (ast_test_flag(iterator
, AJI_AUTOREGISTER
)) {
1651 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_SUBSCRIBE
, iterator
->name
,
1652 "Greetings I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
1653 ast_clear_flag(iterator
, AJI_AUTOREGISTER
);
1655 ASTOBJ_UNLOCK(iterator
);
1658 ast_log(LOG_ERROR
, "Out of memory.\n");
1660 iks_delete(removeiq
);
1662 iks_delete(removequery
);
1664 iks_delete(removeitem
);
1667 ASTOBJ_CONTAINER_PRUNE_MARKED(&client
->buddies
, aji_buddy_destroy
);
1671 * \brief filters the roster packet we get back from server.
1672 * \param aji_client struct, and xml packet.
1673 * \return IKS_FILTER_EAT.
1675 static int aji_filter_roster(void *data
, ikspak
*pak
)
1677 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1680 struct aji_buddy
*buddy
;
1682 client
->state
= AJI_CONNECTED
;
1683 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1684 ASTOBJ_RDLOCK(iterator
);
1685 x
= iks_child(pak
->query
);
1688 if (!iks_strcmp(iks_name(x
), "item")) {
1689 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid"))) {
1691 ast_clear_flag(iterator
, AJI_AUTOPRUNE
| AJI_AUTOREGISTER
);
1697 ast_copy_flags(iterator
, client
, AJI_AUTOREGISTER
);
1700 ASTOBJ_UNLOCK(iterator
);
1703 x
= iks_child(pak
->query
);
1706 if (iks_strcmp(iks_name(x
), "item") == 0) {
1707 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1708 ASTOBJ_RDLOCK(iterator
);
1709 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid")))
1711 ASTOBJ_UNLOCK(iterator
);
1715 buddy
= (struct aji_buddy
*) malloc(sizeof(struct aji_buddy
));
1717 ast_log(LOG_WARNING
, "Out of memory\n");
1720 memset(buddy
, 0, sizeof(struct aji_buddy
));
1722 ASTOBJ_WRLOCK(buddy
);
1723 ast_copy_string(buddy
->name
, iks_find_attrib(x
, "jid"), sizeof(buddy
->name
));
1724 ast_clear_flag(buddy
, AST_FLAGS_ALL
);
1725 if(ast_test_flag(client
, AJI_AUTOPRUNE
)) {
1726 ast_set_flag(buddy
, AJI_AUTOPRUNE
);
1727 buddy
->objflags
|= ASTOBJ_FLAG_MARKED
;
1729 ast_set_flag(buddy
, AJI_AUTOREGISTER
);
1730 ASTOBJ_UNLOCK(buddy
);
1732 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
1733 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
1741 aji_pruneregister(client
);
1743 ASTOBJ_UNREF(client
, aji_client_destroy
);
1744 return IKS_FILTER_EAT
;
1747 static int aji_reconnect(struct aji_client
*client
)
1752 client
->state
= AJI_DISCONNECTED
;
1755 iks_parser_reset(client
->p
);
1756 if (client
->authorized
)
1757 client
->authorized
= 0;
1759 if(client
->component
)
1760 res
= aji_component_initialize(client
);
1762 res
= aji_client_initialize(client
);
1767 static int aji_get_roster(struct aji_client
*client
)
1770 roster
= iks_make_iq(IKS_TYPE_GET
, IKS_NS_ROSTER
);
1772 iks_insert_attrib(roster
, "id", "roster");
1773 aji_set_presence(client
, NULL
, client
->jid
->full
, 1, client
->statusmessage
);
1774 iks_send(client
->p
, roster
);
1782 * \brief connects as a client to jabber server.
1783 * \param aji_client struct, and xml packet.
1786 static int aji_client_connect(void *data
, ikspak
*pak
)
1788 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1792 if (client
->state
== AJI_DISCONNECTED
) {
1793 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
);
1794 client
->state
= AJI_CONNECTING
;
1795 client
->jid
= (iks_find_cdata(pak
->query
, "jid")) ? iks_id_new(client
->stack
, iks_find_cdata(pak
->query
, "jid")) : client
->jid
;
1796 iks_filter_remove_hook(client
->f
, aji_client_connect
);
1797 if(!client
->component
) /*client*/
1798 aji_get_roster(client
);
1801 ast_log(LOG_ERROR
, "Out of memory.\n");
1803 ASTOBJ_UNREF(client
, aji_client_destroy
);
1808 * \brief prepares client for connect.
1809 * \param aji_client struct.
1812 static int aji_client_initialize(struct aji_client
*client
)
1816 connected
= iks_connect_via(client
->p
, S_OR(client
->serverhost
, client
->jid
->server
), client
->port
, client
->jid
->server
);
1818 if (connected
== IKS_NET_NOCONN
) {
1819 ast_log(LOG_ERROR
, "JABBER ERROR: No Connection\n");
1821 } else if (connected
== IKS_NET_NODNS
) {
1822 ast_log(LOG_ERROR
, "JABBER ERROR: No DNS %s for client to %s\n", client
->name
, S_OR(client
->serverhost
, client
->jid
->server
));
1825 iks_recv(client
->p
, 30);
1830 * \brief prepares component for connect.
1831 * \param aji_client struct.
1834 static int aji_component_initialize(struct aji_client
*client
)
1837 connected
= iks_connect_via(client
->p
, client
->jid
->server
, client
->port
, client
->user
);
1838 if (connected
== IKS_NET_NOCONN
) {
1839 ast_log(LOG_ERROR
, "JABBER ERROR: No Connection\n");
1841 } else if (connected
== IKS_NET_NODNS
) {
1842 ast_log(LOG_ERROR
, "JABBER ERROR: No DNS\n");
1844 } else if (!connected
)
1845 iks_recv(client
->p
, 30);
1850 * \brief disconnect from jabber server.
1851 * \param aji_client struct.
1854 int ast_aji_disconnect(struct aji_client
*client
)
1857 if (option_verbose
> 3)
1858 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Disconnecting\n");
1859 iks_disconnect(client
->p
);
1860 iks_parser_delete(client
->p
);
1861 ASTOBJ_UNREF(client
, aji_client_destroy
);
1868 * \brief set presence of client.
1869 * \param aji_client struct, user to send it to, and from, level, description.
1872 static void aji_set_presence(struct aji_client
*client
, char *to
, char *from
, int level
, char *desc
)
1875 iks
*presence
= iks_make_pres(level
, desc
);
1876 iks
*cnode
= iks_new("c");
1877 iks
*priority
= iks_new("priority");
1879 iks_insert_cdata(priority
, "0", 1);
1880 if (presence
&& cnode
&& client
) {
1882 iks_insert_attrib(presence
, "to", to
);
1884 iks_insert_attrib(presence
, "from", from
);
1885 iks_insert_attrib(cnode
, "node", "http://www.asterisk.org/xmpp/client/caps");
1886 iks_insert_attrib(cnode
, "ver", "asterisk-xmpp");
1887 iks_insert_attrib(cnode
, "ext", "voice-v1");
1888 iks_insert_attrib(cnode
, "xmlns", "http://jabber.org/protocol/caps");
1889 iks_insert_node(presence
, cnode
);
1890 res
= iks_send(client
->p
, presence
);
1892 ast_log(LOG_ERROR
, "Out of memory.\n");
1896 iks_delete(presence
);
1900 * \brief turnon console debugging.
1901 * \param fd, number of args, args.
1902 * \return RESULT_SUCCESS.
1904 static int aji_do_debug(int fd
, int argc
, char *argv
[])
1906 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
1907 ASTOBJ_RDLOCK(iterator
);
1908 iterator
->debug
= 1;
1909 ASTOBJ_UNLOCK(iterator
);
1911 ast_cli(fd
, "Jabber Debugging Enabled.\n");
1912 return RESULT_SUCCESS
;
1916 * \brief reload jabber module.
1917 * \param fd, number of args, args.
1918 * \return RESULT_SUCCESS.
1920 static int aji_do_reload(int fd
, int argc
, char *argv
[])
1923 ast_cli(fd
, "Jabber Reloaded.\n");
1924 return RESULT_SUCCESS
;
1928 * \brief turnoff console debugging.
1929 * \param fd, number of args, args.
1930 * \return RESULT_SUCCESS.
1932 static int aji_no_debug(int fd
, int argc
, char *argv
[])
1934 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
1935 ASTOBJ_RDLOCK(iterator
);
1936 iterator
->debug
= 0;
1937 ASTOBJ_UNLOCK(iterator
);
1939 ast_cli(fd
, "Jabber Debugging Disabled.\n");
1940 return RESULT_SUCCESS
;
1944 * \brief show client status.
1945 * \param fd, number of args, args.
1946 * \return RESULT_SUCCESS.
1948 static int aji_show_clients(int fd
, int argc
, char *argv
[])
1952 ast_cli(fd
, "Jabber Users and their status:\n");
1953 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
1954 ASTOBJ_RDLOCK(iterator
);
1956 switch (iterator
->state
) {
1957 case AJI_DISCONNECTED
:
1958 status
= "Disconnected";
1960 case AJI_CONNECTING
:
1961 status
= "Connecting";
1964 status
= "Connected";
1969 ast_cli(fd
, " User: %s - %s\n", iterator
->user
, status
);
1970 ASTOBJ_UNLOCK(iterator
);
1972 ast_cli(fd
, "----\n");
1973 ast_cli(fd
, " Number of users: %d\n", count
);
1974 return RESULT_SUCCESS
;
1978 * \brief send test message for debugging.
1979 * \param fd, number of args, args.
1980 * \return RESULT_SUCCESS.
1982 static int aji_test(int fd
, int argc
, char *argv
[])
1984 struct aji_client
*client
;
1985 struct aji_resource
*resource
;
1986 const char *name
= "asterisk";
1987 struct aji_message
*tmp
;
1990 return RESULT_SHOWUSAGE
;
1994 if (!(client
= ASTOBJ_CONTAINER_FIND(&clients
, name
))) {
1995 ast_cli(fd
, "Unable to find client '%s'!\n", name
);
1996 return RESULT_FAILURE
;
1999 /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
2000 ast_aji_send(client
, "mogorman@astjab.org", "blahblah");
2001 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2002 ASTOBJ_RDLOCK(iterator
);
2003 ast_verbose("User: %s\n", iterator
->name
);
2004 for (resource
= iterator
->resources
; resource
; resource
= resource
->next
) {
2005 ast_verbose("Resource: %s\n", resource
->resource
);
2007 ast_verbose(" client: %s\n", resource
->cap
->parent
->node
);
2008 ast_verbose(" version: %s\n", resource
->cap
->version
);
2009 ast_verbose(" Jingle Capable: %d\n", resource
->cap
->jingle
);
2011 ast_verbose(" Priority: %d\n", resource
->priority
);
2012 ast_verbose(" Status: %d\n", resource
->status
);
2013 ast_verbose(" Message: %s\n", S_OR(resource
->description
,""));
2015 ASTOBJ_UNLOCK(iterator
);
2017 ast_verbose("\nOooh a working message stack!\n");
2018 AST_LIST_LOCK(&client
->messages
);
2019 AST_LIST_TRAVERSE(&client
->messages
, tmp
, list
) {
2020 ast_verbose(" Message from: %s with id %s @ %s %s\n",tmp
->from
, S_OR(tmp
->id
,""), ctime(&tmp
->arrived
), S_OR(tmp
->message
, ""));
2022 AST_LIST_UNLOCK(&client
->messages
);
2023 ASTOBJ_UNREF(client
, aji_client_destroy
);
2025 return RESULT_SUCCESS
;
2029 * \brief creates aji_client structure.
2030 * \param label, ast_variable, debug, pruneregister, component/client, aji_client to dump into.
2033 static int aji_create_client(char *label
, struct ast_variable
*var
, int debug
)
2036 struct aji_client
*client
= NULL
;
2039 client
= ASTOBJ_CONTAINER_FIND(&clients
,label
);
2042 client
= (struct aji_client
*) malloc(sizeof(struct aji_client
));
2044 ast_log(LOG_ERROR
, "Out of memory!\n");
2047 memset(client
, 0, sizeof(struct aji_client
));
2048 ASTOBJ_INIT(client
);
2049 ASTOBJ_WRLOCK(client
);
2050 ASTOBJ_CONTAINER_INIT(&client
->buddies
);
2052 ASTOBJ_WRLOCK(client
);
2053 ASTOBJ_UNMARK(client
);
2055 ASTOBJ_CONTAINER_MARKALL(&client
->buddies
);
2056 ast_copy_string(client
->name
, label
, sizeof(client
->name
));
2057 ast_copy_string(client
->mid
, "aaaaa", sizeof(client
->mid
));
2059 client
->debug
= debug
;
2060 ast_copy_flags(client
, &globalflags
, AST_FLAGS_ALL
);
2061 client
->port
= 5222;
2063 client
->usesasl
= 1;
2064 client
->forcessl
= 0;
2065 client
->keepalive
= 1;
2066 client
->timeout
= 50;
2067 client
->message_timeout
= 100;
2068 AST_LIST_HEAD_INIT(&client
->messages
);
2069 client
->component
= 0;
2070 ast_copy_string(client
->statusmessage
, "Online and Available", sizeof(client
->statusmessage
));
2073 client
->authorized
= 0;
2074 client
->state
= AJI_DISCONNECTED
;
2077 if (!strcasecmp(var
->name
, "username"))
2078 ast_copy_string(client
->user
, var
->value
, sizeof(client
->user
));
2079 else if (!strcasecmp(var
->name
, "serverhost"))
2080 ast_copy_string(client
->serverhost
, var
->value
, sizeof(client
->serverhost
));
2081 else if (!strcasecmp(var
->name
, "secret"))
2082 ast_copy_string(client
->password
, var
->value
, sizeof(client
->password
));
2083 else if (!strcasecmp(var
->name
, "statusmessage"))
2084 ast_copy_string(client
->statusmessage
, var
->value
, sizeof(client
->statusmessage
));
2085 else if (!strcasecmp(var
->name
, "port"))
2086 client
->port
= atoi(var
->value
);
2087 else if (!strcasecmp(var
->name
, "timeout"))
2088 client
->message_timeout
= atoi(var
->value
);
2089 else if (!strcasecmp(var
->name
, "debug"))
2090 client
->debug
= (ast_false(var
->value
)) ? 0 : 1;
2091 else if (!strcasecmp(var
->name
, "type")) {
2092 if (!strcasecmp(var
->value
, "component"))
2093 client
->component
= 1;
2094 } else if (!strcasecmp(var
->name
, "usetls")) {
2095 client
->usetls
= (ast_false(var
->value
)) ? 0 : 1;
2096 } else if (!strcasecmp(var
->name
, "usesasl")) {
2097 client
->usesasl
= (ast_false(var
->value
)) ? 0 : 1;
2098 } else if (!strcasecmp(var
->name
, "forceoldssl"))
2099 client
->forcessl
= (ast_false(var
->value
)) ? 0 : 1;
2100 else if (!strcasecmp(var
->name
, "keepalive"))
2101 client
->keepalive
= (ast_false(var
->value
)) ? 0 : 1;
2102 else if (!strcasecmp(var
->name
, "autoprune"))
2103 ast_set2_flag(client
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2104 else if (!strcasecmp(var
->name
, "autoregister"))
2105 ast_set2_flag(client
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2106 else if (!strcasecmp(var
->name
, "buddy"))
2107 aji_create_buddy(var
->value
, client
);
2108 /* no transport support in this version */
2109 /* else if (!strcasecmp(var->name, "transport"))
2110 aji_create_transport(var->value, client);
2115 ASTOBJ_UNLOCK(client
);
2116 ASTOBJ_UNREF(client
, aji_client_destroy
);
2119 client
->p
= iks_stream_new(((client
->component
) ? "jabber:component:accept" : "jabber:client"), client
, aji_act_hook
);
2121 ast_log(LOG_ERROR
, "Failed to create stream for client '%s'!\n", client
->name
);
2124 client
->stack
= iks_stack_new(8192, 8192);
2125 if (!client
->stack
) {
2126 ast_log(LOG_ERROR
, "Failed to allocate stack for client '%s'\n", client
->name
);
2129 client
->f
= iks_filter_new();
2131 ast_log(LOG_ERROR
, "Failed to create filter for client '%s'\n", client
->name
);
2134 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2136 asprintf(&resource
, "%s/asterisk", client
->user
);
2138 client
->jid
= iks_id_new(client
->stack
, resource
);
2142 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2143 if (client
->component
) {
2144 iks_filter_add_rule(client
->f
, aji_dinfo_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2145 iks_filter_add_rule(client
->f
, aji_ditems_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE
);
2146 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
);
2147 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
);
2149 iks_filter_add_rule(client
->f
, aji_client_info_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2151 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2153 asprintf(&resource
, "%s/asterisk", client
->user
);
2155 client
->jid
= iks_id_new(client
->stack
, resource
);
2159 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2160 iks_set_log_hook(client
->p
, aji_log_hook
);
2161 ASTOBJ_UNLOCK(client
);
2162 ASTOBJ_CONTAINER_LINK(&clients
,client
);
2167 * \brief creates transport.
2168 * \param label, buddy to dump it into.
2171 /* no connecting to transports today */
2173 static int aji_create_transport(char *label, struct aji_client *client)
2175 char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
2176 struct aji_buddy *buddy = NULL;
2178 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
2180 buddy = malloc(sizeof(struct aji_buddy));
2182 ast_log(LOG_WARNING, "Out of memory\n");
2185 memset(buddy, 0, sizeof(struct aji_buddy));
2188 ASTOBJ_WRLOCK(buddy);
2190 if ((buddyname = strchr(label, ','))) {
2193 if (buddyname && buddyname[0] != '\0') {
2194 if ((user = strchr(buddyname, ','))) {
2197 if (user && user[0] != '\0') {
2198 if ((pass = strchr(user, ','))) {
2201 ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
2202 ast_copy_string(buddy->user, user, sizeof(buddy->user));
2203 ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
2204 ast_copy_string(buddy->server, server, sizeof(buddy->server));
2211 ASTOBJ_UNLOCK(buddy);
2212 ASTOBJ_UNMARK(buddy);
2213 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
2219 * \brief creates buddy.
2220 * \param label, buddy to dump it into.
2223 static int aji_create_buddy(char *label
, struct aji_client
*client
)
2225 struct aji_buddy
*buddy
= NULL
;
2227 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
,label
);
2230 buddy
= malloc(sizeof(struct aji_buddy
));
2232 ast_log(LOG_WARNING
, "Out of memory\n");
2235 memset(buddy
, 0, sizeof(struct aji_buddy
));
2238 ASTOBJ_WRLOCK(buddy
);
2239 ast_copy_string(buddy
->name
, label
, sizeof(buddy
->name
));
2240 ASTOBJ_UNLOCK(buddy
);
2242 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
2244 ASTOBJ_UNMARK(buddy
);
2245 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
2251 * \brief load config file.
2255 static int aji_load_config(void)
2259 struct ast_config
*cfg
= NULL
;
2260 struct ast_variable
*var
= NULL
;
2262 cfg
= ast_config_load(JABBER_CONFIG
);
2264 ast_log(LOG_WARNING
, "No such configuration file %s\n", JABBER_CONFIG
);
2268 cat
= ast_category_browse(cfg
, NULL
);
2269 for (var
= ast_variable_browse(cfg
, "general"); var
; var
= var
->next
) {
2270 if (!strcasecmp(var
->name
, "debug"))
2271 debug
= (ast_false(ast_variable_retrieve(cfg
, "general", "debug"))) ? 0 : 1;
2272 else if (!strcasecmp(var
->name
, "autoprune"))
2273 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2274 else if (!strcasecmp(var
->name
, "autoregister"))
2275 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2279 if (strcasecmp(cat
, "general")) {
2280 var
= ast_variable_browse(cfg
, cat
);
2281 aji_create_client(cat
, var
, debug
);
2283 cat
= ast_category_browse(cfg
, cat
);
2289 * \brief grab a aji_client structure by label name.
2293 struct aji_client
*ast_aji_get_client(const char *name
)
2295 struct aji_client
*client
= NULL
;
2297 client
= ASTOBJ_CONTAINER_FIND(&clients
, name
);
2298 if (!client
&& !strchr(name
, '@'))
2299 client
= ASTOBJ_CONTAINER_FIND_FULL(&clients
, name
, user
,,, strcasecmp
);
2303 struct aji_client_container
*ast_aji_get_clients(void)
2308 static char mandescr_jabber_send
[] =
2309 "Description: Sends a message to a Jabber Client.\n"
2311 " Jabber: Client or transport Asterisk uses to connect to JABBER.\n"
2312 " ScreenName: User Name to message.\n"
2313 " Message: Message to be sent to the buddy\n";
2315 /*! \brief Send a Jabber Message via call from the Manager */
2316 static int manager_jabber_send(struct mansession
*s
, const struct message
*m
)
2318 struct aji_client
*client
= NULL
;
2319 const char *id
= astman_get_header(m
,"ActionID");
2320 const char *jabber
= astman_get_header(m
,"Jabber");
2321 const char *screenname
= astman_get_header(m
,"ScreenName");
2322 const char *message
= astman_get_header(m
,"Message");
2324 if (ast_strlen_zero(jabber
)) {
2325 astman_send_error(s
, m
, "No transport specified");
2328 if (ast_strlen_zero(screenname
)) {
2329 astman_send_error(s
, m
, "No ScreenName specified");
2332 if (ast_strlen_zero(message
)) {
2333 astman_send_error(s
, m
, "No Message specified");
2337 astman_send_ack(s
, m
, "Attempting to send Jabber Message");
2338 client
= ast_aji_get_client(jabber
);
2340 astman_send_error(s
, m
, "Could not find Sender");
2343 if (strchr(screenname
, '@') && message
){
2344 ast_aji_send(client
, screenname
, message
);
2345 if (!ast_strlen_zero(id
))
2346 astman_append(s
, "ActionID: %s\r\n",id
);
2347 astman_append(s
, "Response: Success\r\n");
2350 if (!ast_strlen_zero(id
))
2351 astman_append(s
, "ActionID: %s\r\n",id
);
2352 astman_append(s
, "Response: Failure\r\n");
2357 static int aji_reload()
2359 ASTOBJ_CONTAINER_MARKALL(&clients
);
2360 if (!aji_load_config()) {
2361 ast_log(LOG_ERROR
, "JABBER: Failed to load config.\n");
2364 ASTOBJ_CONTAINER_PRUNE_MARKED(&clients
, aji_client_destroy
);
2365 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2366 ASTOBJ_RDLOCK(iterator
);
2367 if(iterator
->state
== AJI_DISCONNECTED
) {
2368 if (!iterator
->thread
)
2369 ast_pthread_create_background(&iterator
->thread
, NULL
, aji_recv_loop
, iterator
);
2370 } else if (iterator
->state
== AJI_CONNECTING
)
2371 aji_get_roster(iterator
);
2372 ASTOBJ_UNLOCK(iterator
);
2378 static int unload_module(void)
2380 ast_cli_unregister_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
2381 ast_unregister_application(app_ajisend
);
2382 ast_unregister_application(app_ajistatus
);
2383 ast_manager_unregister("JabberSend");
2384 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2385 ASTOBJ_RDLOCK(iterator
);
2386 if (option_verbose
> 2)
2387 ast_verbose(VERBOSE_PREFIX_3
"JABBER: %s\n", iterator
->name
);
2388 iterator
->state
= AJI_DISCONNECTED
;
2389 ast_aji_disconnect(iterator
);
2390 pthread_join(iterator
->thread
, NULL
);
2391 ASTOBJ_UNLOCK(iterator
);
2394 ASTOBJ_CONTAINER_DESTROYALL(&clients
, aji_client_destroy
);
2395 ASTOBJ_CONTAINER_DESTROY(&clients
);
2397 ast_log(LOG_NOTICE
, "res_jabber unloaded.\n");
2401 static int load_module(void)
2403 ASTOBJ_CONTAINER_INIT(&clients
);
2405 return AST_MODULE_LOAD_DECLINE
;
2406 ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM
, manager_jabber_send
,
2407 "Sends a message to a Jabber Client", mandescr_jabber_send
);
2408 ast_register_application(app_ajisend
, aji_send_exec
, ajisend_synopsis
, ajisend_descrip
);
2409 ast_register_application(app_ajistatus
, aji_status_exec
, ajistatus_synopsis
, ajistatus_descrip
);
2410 ast_cli_register_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
2412 ast_log(LOG_NOTICE
, "res_jabber.so loaded.\n");
2416 static int reload(void)
2422 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "AJI - Asterisk Jabber Interface",
2423 .load
= load_module
,
2424 .unload
= unload_module
,