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 * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
24 * \todo If you have TLS, you can't unload this module. See bug #9738. This needs to be fixed,
25 * but the bug is in the unmantained Iksemel library
30 <depend>iksemel</depend>
36 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
42 #include "asterisk/channel.h"
43 #include "asterisk/jabber.h"
44 #include "asterisk/file.h"
45 #include "asterisk/config.h"
46 #include "asterisk/callerid.h"
47 #include "asterisk/lock.h"
48 #include "asterisk/logger.h"
49 #include "asterisk/options.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/app.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/md5.h"
54 #include "asterisk/acl.h"
55 #include "asterisk/utils.h"
56 #include "asterisk/module.h"
57 #include "asterisk/astobj.h"
58 #include "asterisk/astdb.h"
59 #include "asterisk/manager.h"
61 #define JABBER_CONFIG "jabber.conf"
71 /*-- Forward declarations */
72 static int aji_highest_bit(int number
);
73 static void aji_buddy_destroy(struct aji_buddy
*obj
);
74 static void aji_client_destroy(struct aji_client
*obj
);
75 static int aji_send_exec(struct ast_channel
*chan
, void *data
);
76 static int aji_status_exec(struct ast_channel
*chan
, void *data
);
77 static void aji_log_hook(void *data
, const char *xmpp
, size_t size
, int is_incoming
);
78 static int aji_act_hook(void *data
, int type
, iks
*node
);
79 static void aji_handle_iq(struct aji_client
*client
, iks
*node
);
80 static void aji_handle_message(struct aji_client
*client
, ikspak
*pak
);
81 static void aji_handle_presence(struct aji_client
*client
, ikspak
*pak
);
82 static void aji_handle_subscribe(struct aji_client
*client
, ikspak
*pak
);
83 static void *aji_recv_loop(void *data
);
84 static int aji_component_initialize(struct aji_client
*client
);
85 static int aji_client_initialize(struct aji_client
*client
);
86 static int aji_client_connect(void *data
, ikspak
*pak
);
87 static void aji_set_presence(struct aji_client
*client
, char *to
, char *from
, int level
, char *desc
);
88 static int aji_do_debug(int fd
, int argc
, char *argv
[]);
89 static int aji_do_reload(int fd
, int argc
, char *argv
[]);
90 static int aji_no_debug(int fd
, int argc
, char *argv
[]);
91 static int aji_test(int fd
, int argc
, char *argv
[]);
92 static int aji_show_clients(int fd
, int argc
, char *argv
[]);
93 static int aji_create_client(char *label
, struct ast_variable
*var
, int debug
);
94 static int aji_create_buddy(char *label
, struct aji_client
*client
);
95 static int aji_reload(void);
96 static int aji_load_config(void);
97 static void aji_pruneregister(struct aji_client
*client
);
98 static int aji_filter_roster(void *data
, ikspak
*pak
);
99 static int aji_get_roster(struct aji_client
*client
);
100 static int aji_client_info_handler(void *data
, ikspak
*pak
);
101 static int aji_dinfo_handler(void *data
, ikspak
*pak
);
102 static int aji_ditems_handler(void *data
, ikspak
*pak
);
103 static int aji_register_query_handler(void *data
, ikspak
*pak
);
104 static int aji_register_approve_handler(void *data
, ikspak
*pak
);
105 static int aji_reconnect(struct aji_client
*client
);
106 static iks
*jabber_make_auth(iksid
* id
, const char *pass
, const char *sid
);
107 /* No transports in this version */
109 static int aji_create_transport(char *label, struct aji_client *client);
110 static int aji_register_transport(void *data, ikspak *pak);
111 static int aji_register_transport2(void *data, ikspak *pak);
114 static char debug_usage
[] =
115 "Usage: jabber debug\n"
116 " Enables dumping of Jabber packets for debugging purposes.\n";
118 static char no_debug_usage
[] =
119 "Usage: jabber debug off\n"
120 " Disables dumping of Jabber packets for debugging purposes.\n";
122 static char reload_usage
[] =
123 "Usage: jabber reload\n"
124 " Enables reloading of Jabber module.\n";
126 static char test_usage
[] =
127 "Usage: jabber test [client]\n"
128 " Sends test message for debugging purposes. A specific client\n"
129 " as configured in jabber.conf can be optionally specified.\n";
131 static struct ast_cli_entry aji_cli
[] = {
132 { { "jabber", "debug", NULL
},
133 aji_do_debug
, "Enable Jabber debugging",
136 { { "jabber", "reload", NULL
},
137 aji_do_reload
, "Reload Jabber configuration",
140 { { "jabber", "show", "connected", NULL
},
141 aji_show_clients
, "Show state of clients and components",
144 { { "jabber", "debug", "off", NULL
},
145 aji_no_debug
, "Disable Jabber debug",
148 { { "jabber", "test", NULL
},
149 aji_test
, "Shows roster, but is generally used for mog's debugging.",
153 static char *app_ajisend
= "JabberSend";
155 static char *ajisend_synopsis
= "JabberSend(jabber,screenname,message)";
157 static char *ajisend_descrip
=
158 "JabberSend(Jabber,ScreenName,Message)\n"
159 " Jabber - Client or transport Asterisk uses to connect to Jabber\n"
160 " ScreenName - User Name to message.\n"
161 " Message - Message to be sent to the buddy\n";
163 static char *app_ajistatus
= "JabberStatus";
165 static char *ajistatus_synopsis
= "JabberStatus(Jabber,ScreenName,Variable)";
167 static char *ajistatus_descrip
=
168 "JabberStatus(Jabber,ScreenName,Variable)\n"
169 " Jabber - Client or transport Asterisk uses to connect to Jabber\n"
170 " ScreenName - User Name to retrieve status from.\n"
171 " Variable - Variable to store presence in will be 1-6.\n"
172 " In order, Online, Chatty, Away, XAway, DND, Offline\n"
173 " If not in roster variable will = 7\n";
175 struct aji_client_container clients
;
176 struct aji_capabilities
*capabilities
= NULL
;
178 /*! \brief Global flags, initialized to default values */
179 static struct ast_flags globalflags
= { AJI_AUTOPRUNE
| AJI_AUTOREGISTER
};
180 static int tls_initialized
= FALSE
;
183 * \brief Deletes the aji_client data structure.
184 * \param obj is the structure we will delete.
187 static void aji_client_destroy(struct aji_client
*obj
)
189 struct aji_message
*tmp
;
190 ASTOBJ_CONTAINER_DESTROYALL(&obj
->buddies
, aji_buddy_destroy
);
191 ASTOBJ_CONTAINER_DESTROY(&obj
->buddies
);
192 iks_filter_delete(obj
->f
);
193 iks_parser_delete(obj
->p
);
194 iks_stack_delete(obj
->stack
);
195 AST_LIST_LOCK(&obj
->messages
);
196 while ((tmp
= AST_LIST_REMOVE_HEAD(&obj
->messages
, list
))) {
202 AST_LIST_HEAD_DESTROY(&obj
->messages
);
207 * \brief Deletes the aji_buddy data structure.
208 * \param obj is the structure we will delete.
211 static void aji_buddy_destroy(struct aji_buddy
*obj
)
213 struct aji_resource
*tmp
;
215 while ((tmp
= obj
->resources
)) {
216 obj
->resources
= obj
->resources
->next
;
217 free(tmp
->description
);
225 * \brief Find version in XML stream and populate our capabilities list
226 * \param node the node attribute in the caps element we'll look for or add to
228 * \param version the version attribute in the caps element we'll look for or
230 * \param pak the XML stanza we're processing
231 * \return a pointer to the added or found aji_version structure
233 static struct aji_version
*aji_find_version(char *node
, char *version
, ikspak
*pak
)
235 struct aji_capabilities
*list
= NULL
;
236 struct aji_version
*res
= NULL
;
241 node
= pak
->from
->full
;
243 version
= "none supplied.";
245 if(!strcasecmp(list
->node
, node
)) {
246 res
= list
->versions
;
248 if(!strcasecmp(res
->version
, version
))
252 /* Specified version not found. Let's add it to
253 this node in our capabilities list */
255 res
= (struct aji_version
*)malloc(sizeof(struct aji_version
));
257 ast_log(LOG_ERROR
, "Out of memory!\n");
262 ast_copy_string(res
->version
, version
, sizeof(res
->version
));
263 res
->next
= list
->versions
;
264 list
->versions
= res
;
270 /* Specified node not found. Let's add it our capabilities list */
272 list
= (struct aji_capabilities
*)malloc(sizeof(struct aji_capabilities
));
274 ast_log(LOG_ERROR
, "Out of memory!\n");
277 res
= (struct aji_version
*)malloc(sizeof(struct aji_version
));
279 ast_log(LOG_ERROR
, "Out of memory!\n");
282 ast_copy_string(list
->node
, node
, sizeof(list
->node
));
283 ast_copy_string(res
->version
, version
, sizeof(res
->version
));
287 list
->versions
= res
;
288 list
->next
= capabilities
;
294 static struct aji_resource
*aji_find_resource(struct aji_buddy
*buddy
, char *name
)
296 struct aji_resource
*res
= NULL
;
299 res
= buddy
->resources
;
301 if (!strcasecmp(res
->resource
, name
)) {
309 static int gtalk_yuck(iks
*node
)
311 if (iks_find_with_attrib(node
, "c", "node", "http://www.google.com/xmpp/client/caps"))
317 * \brief Detects the highest bit in a number.
318 * \param Number you want to have evaluated.
319 * \return the highest power of 2 that can go into the number.
321 static int aji_highest_bit(int number
)
323 int x
= sizeof(number
) * 8 - 1;
327 if (number
& (1 << x
))
333 static iks
*jabber_make_auth(iksid
* id
, const char *pass
, const char *sid
)
337 iks_insert_attrib(x
, "type", "set");
338 y
= iks_insert(x
, "query");
339 iks_insert_attrib(y
, "xmlns", IKS_NS_AUTH
);
340 iks_insert_cdata(iks_insert(y
, "username"), id
->user
, 0);
341 iks_insert_cdata(iks_insert(y
, "resource"), id
->resource
, 0);
345 snprintf(sidpass
, sizeof(sidpass
), "%s%s", sid
, pass
);
346 ast_sha1_hash(buf
, sidpass
);
347 iks_insert_cdata(iks_insert(y
, "digest"), buf
, 0);
349 iks_insert_cdata(iks_insert(y
, "password"), pass
, 0);
355 * \brief Dial plan function status(). puts the status of watched user
356 into a channel variable.
357 * \param channel, and username,watched user, status var
360 static int aji_status_exec(struct ast_channel
*chan
, void *data
)
362 struct aji_client
*client
= NULL
;
363 struct aji_buddy
*buddy
= NULL
;
364 struct aji_resource
*r
= NULL
;
365 char *s
= NULL
, *sender
= NULL
, *jid
= NULL
, *screenname
= NULL
, *resource
= NULL
, *variable
= NULL
;
370 ast_log(LOG_ERROR
, "This application requires arguments.\n");
373 s
= ast_strdupa(data
);
375 sender
= strsep(&s
, "|");
376 if (sender
&& (sender
[0] != '\0')) {
377 jid
= strsep(&s
, "|");
378 if (jid
&& (jid
[0] != '\0')) {
381 ast_log(LOG_ERROR
, "Bad arguments\n");
387 if(!strchr(jid
, '/')) {
390 screenname
= strsep(&jid
, "/");
393 client
= ast_aji_get_client(sender
);
395 ast_log(LOG_WARNING
, "Could not find sender connection: %s\n", sender
);
398 if(!&client
->buddies
) {
399 ast_log(LOG_WARNING
, "No buddies for connection : %s\n", sender
);
402 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, resource
? screenname
: jid
);
404 ast_log(LOG_WARNING
, "Could not find buddy in list : %s\n", resource
? screenname
: jid
);
407 r
= aji_find_resource(buddy
, resource
);
408 if(!r
&& buddy
->resources
)
409 r
= buddy
->resources
;
411 ast_log(LOG_NOTICE
, "Resource %s of buddy %s not found \n", resource
, screenname
);
414 sprintf(status
, "%d", stat
);
415 pbx_builtin_setvar_helper(chan
, variable
, status
);
420 * \brief Dial plan function to send a message.
421 * \param channel, and data, data is sender, reciever, message.
424 static int aji_send_exec(struct ast_channel
*chan
, void *data
)
426 struct aji_client
*client
= NULL
;
428 char *s
= NULL
, *sender
= NULL
, *recipient
= NULL
, *message
= NULL
;
431 ast_log(LOG_ERROR
, "This application requires arguments.\n");
434 s
= ast_strdupa(data
);
436 sender
= strsep(&s
, "|");
437 if (sender
&& (sender
[0] != '\0')) {
438 recipient
= strsep(&s
, "|");
439 if (recipient
&& (recipient
[0] != '\0')) {
442 ast_log(LOG_ERROR
, "Bad arguments: %s\n", (char *) data
);
447 if (!(client
= ast_aji_get_client(sender
))) {
448 ast_log(LOG_WARNING
, "Could not find sender connection: %s\n", sender
);
451 if (strchr(recipient
, '@') && message
)
452 ast_aji_send(client
, recipient
, message
);
457 * \brief the debug loop.
458 * \param aji_client structure, xml data as string, size of string, direction of packet, 1 for inbound 0 for outbound.
460 static void aji_log_hook(void *data
, const char *xmpp
, size_t size
, int is_incoming
)
462 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
463 manager_event(EVENT_FLAG_USER
, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client
->name
, xmpp
);
467 ast_verbose("\nJABBER: %s INCOMING: %s\n", client
->name
, xmpp
);
469 if( strlen(xmpp
) == 1) {
470 if(option_debug
> 2 && xmpp
[0] == ' ')
471 ast_verbose("\nJABBER: Keep alive packet\n");
473 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client
->name
, xmpp
);
477 ASTOBJ_UNREF(client
, aji_client_destroy
);
481 * \brief The action hook parses the inbound packets, constantly running.
482 * \param data aji client structure
483 * \param type type of packet
484 * \param node the actual packet.
485 * \return IKS_OK or IKS_HOOK .
487 static int aji_act_hook(void *data
, int type
, iks
*node
)
489 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
494 ast_log(LOG_ERROR
, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
495 ASTOBJ_UNREF(client
, aji_client_destroy
);
499 if (client
->state
== AJI_DISCONNECTING
) {
500 ASTOBJ_UNREF(client
, aji_client_destroy
);
504 pak
= iks_packet(node
);
506 if (!client
->component
) { /*client */
509 if (client
->usetls
&& !iks_is_secure(client
->p
)) {
511 iks_start_tls(client
->p
);
512 tls_initialized
= TRUE
;
514 ast_log(LOG_ERROR
, "gnuTLS not installed. You need to recompile the Iksemel library with gnuTLS support\n");
517 if (!client
->usesasl
) {
518 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
);
519 auth
= jabber_make_auth(client
->jid
, client
->password
, iks_find_attrib(node
, "id"));
521 iks_insert_attrib(auth
, "id", client
->mid
);
522 iks_insert_attrib(auth
, "to", client
->jid
->server
);
523 ast_aji_increment_mid(client
->mid
);
524 iks_send(client
->p
, auth
);
527 ast_log(LOG_ERROR
, "Out of memory.\n");
531 case IKS_NODE_NORMAL
:
534 if (!strcmp("stream:features", iks_name(node
))) {
535 features
= iks_stream_features(node
);
536 if (client
->usesasl
) {
537 if (client
->usetls
&& !iks_is_secure(client
->p
))
539 if (client
->authorized
) {
540 if (features
& IKS_STREAM_BIND
) {
541 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
);
542 auth
= iks_make_resource_bind(client
->jid
);
544 iks_insert_attrib(auth
, "id", client
->mid
);
545 ast_aji_increment_mid(client
->mid
);
546 iks_send(client
->p
, auth
);
549 ast_log(LOG_ERROR
, "Out of memory.\n");
553 if (features
& IKS_STREAM_SESSION
) {
554 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
);
555 auth
= iks_make_session();
557 iks_insert_attrib(auth
, "id", "auth");
558 ast_aji_increment_mid(client
->mid
);
559 iks_send(client
->p
, auth
);
562 ast_log(LOG_ERROR
, "Out of memory.\n");
566 if (!client
->jid
->user
) {
567 ast_log(LOG_ERROR
, "Malformed Jabber ID : %s (domain missing?)\n", client
->jid
->full
);
570 features
= aji_highest_bit(features
);
571 if (features
== IKS_STREAM_SASL_MD5
)
572 iks_start_sasl(client
->p
, IKS_SASL_DIGEST_MD5
, client
->jid
->user
, client
->password
);
574 if (features
== IKS_STREAM_SASL_PLAIN
) {
578 int len
= strlen(client
->jid
->user
) + strlen(client
->password
) + 3;
579 /* XXX Check return values XXX */
580 char *s
= ast_malloc(80 + len
);
581 char *base64
= ast_malloc(80 + len
* 2);
582 iks_insert_attrib(x
, "xmlns", IKS_NS_XMPP_SASL
);
583 iks_insert_attrib(x
, "mechanism", "PLAIN");
584 sprintf(s
, "%c%s%c%s", 0, client
->jid
->user
, 0, client
->password
);
585 ast_base64encode(base64
, (const unsigned char *) s
, len
, len
* 2);
586 iks_insert_cdata(x
, base64
, 0);
587 iks_send(client
->p
, x
);
594 ast_log(LOG_ERROR
, "Out of memory.\n");
600 } else if (!strcmp("failure", iks_name(node
))) {
601 ast_log(LOG_ERROR
, "JABBER: encryption failure. possible bad password.\n");
602 } else if (!strcmp("success", iks_name(node
))) {
603 client
->authorized
= 1;
604 iks_send_header(client
->p
, client
->jid
->server
);
609 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
610 ASTOBJ_UNREF(client
, aji_client_destroy
);
614 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
615 ASTOBJ_UNREF(client
, aji_client_destroy
);
619 } else if (client
->state
!= AJI_CONNECTED
&& client
->component
) {
622 if (client
->state
== AJI_DISCONNECTED
) {
623 char secret
[160], shasum
[320], *handshake
;
625 sprintf(secret
, "%s%s", pak
->id
, client
->password
);
626 ast_sha1_hash(shasum
, secret
);
628 asprintf(&handshake
, "<handshake>%s</handshake>", shasum
);
630 iks_send_raw(client
->p
, handshake
);
634 client
->state
= AJI_CONNECTING
;
635 if(iks_recv(client
->p
,1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
636 client
->state
= AJI_CONNECTED
;
638 ast_log(LOG_WARNING
,"Jabber didn't seem to handshake, failed to authenicate.\n");
643 case IKS_NODE_NORMAL
:
647 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
648 ASTOBJ_UNREF(client
, aji_client_destroy
);
652 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
653 ASTOBJ_UNREF(client
, aji_client_destroy
);
661 ast_log(LOG_DEBUG
, "JABBER: I Don't know what to do with you NONE\n");
663 case IKS_PAK_MESSAGE
:
664 aji_handle_message(client
, pak
);
666 ast_log(LOG_DEBUG
, "JABBER: I Don't know what to do with you MESSAGE\n");
668 case IKS_PAK_PRESENCE
:
669 aji_handle_presence(client
, pak
);
671 ast_log(LOG_DEBUG
, "JABBER: I Do know how to handle presence!!\n");
674 aji_handle_subscribe(client
, pak
);
676 ast_log(LOG_DEBUG
, "JABBER: I Dont know S10N subscribe!!\n");
680 ast_log(LOG_DEBUG
, "JABBER: I Dont have an IQ!!!\n");
681 aji_handle_iq(client
, node
);
685 ast_log(LOG_DEBUG
, "JABBER: I Dont know %i\n", pak
->type
);
689 iks_filter_packet(client
->f
, pak
);
694 ASTOBJ_UNREF(client
, aji_client_destroy
);
698 static int aji_register_approve_handler(void *data
, ikspak
*pak
)
700 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
701 iks
*iq
= NULL
, *presence
= NULL
, *x
= NULL
;
704 presence
= iks_new("presence");
706 if (client
&& iq
&& presence
&& x
) {
707 if (!iks_find(pak
->query
, "remove")) {
708 iks_insert_attrib(iq
, "from", client
->jid
->full
);
709 iks_insert_attrib(iq
, "to", pak
->from
->full
);
710 iks_insert_attrib(iq
, "id", pak
->id
);
711 iks_insert_attrib(iq
, "type", "result");
712 iks_send(client
->p
, iq
);
714 iks_insert_attrib(presence
, "from", client
->jid
->full
);
715 iks_insert_attrib(presence
, "to", pak
->from
->partial
);
716 iks_insert_attrib(presence
, "id", client
->mid
);
717 ast_aji_increment_mid(client
->mid
);
718 iks_insert_attrib(presence
, "type", "subscribe");
719 iks_insert_attrib(x
, "xmlns", "vcard-temp:x:update");
720 iks_insert_node(presence
, x
);
721 iks_send(client
->p
, presence
);
724 ast_log(LOG_ERROR
, "Out of memory.\n");
730 iks_delete(presence
);
733 ASTOBJ_UNREF(client
, aji_client_destroy
);
734 return IKS_FILTER_EAT
;
737 static int aji_register_query_handler(void *data
, ikspak
*pak
)
739 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
740 struct aji_buddy
*buddy
= NULL
;
743 client
= (struct aji_client
*) data
;
745 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
747 iks
*iq
= NULL
, *query
= NULL
, *error
= NULL
, *notacceptable
= NULL
;
748 ast_verbose("Someone.... %s tried to register but they aren't allowed\n", pak
->from
->partial
);
750 query
= iks_new("query");
751 error
= iks_new("error");
752 notacceptable
= iks_new("not-acceptable");
753 if(iq
&& query
&& error
&& notacceptable
) {
754 iks_insert_attrib(iq
, "type", "error");
755 iks_insert_attrib(iq
, "from", client
->user
);
756 iks_insert_attrib(iq
, "to", pak
->from
->full
);
757 iks_insert_attrib(iq
, "id", pak
->id
);
758 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
759 iks_insert_attrib(error
, "code" , "406");
760 iks_insert_attrib(error
, "type", "modify");
761 iks_insert_attrib(notacceptable
, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
762 iks_insert_node(iq
, query
);
763 iks_insert_node(iq
, error
);
764 iks_insert_node(error
, notacceptable
);
765 iks_send(client
->p
, iq
);
767 ast_log(LOG_ERROR
, "Out of memory.\n");
776 iks_delete(notacceptable
);
777 } else if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
778 iks
*iq
= NULL
, *query
= NULL
, *instructions
= NULL
;
779 char *explain
= "Welcome to Asterisk - the Open Source PBX.\n";
781 query
= iks_new("query");
782 instructions
= iks_new("instructions");
783 if (iq
&& query
&& instructions
&& client
) {
784 iks_insert_attrib(iq
, "from", client
->user
);
785 iks_insert_attrib(iq
, "to", pak
->from
->full
);
786 iks_insert_attrib(iq
, "id", pak
->id
);
787 iks_insert_attrib(iq
, "type", "result");
788 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
789 iks_insert_cdata(instructions
, explain
, 0);
790 iks_insert_node(iq
, query
);
791 iks_insert_node(query
, instructions
);
792 iks_send(client
->p
, iq
);
794 ast_log(LOG_ERROR
, "Out of memory.\n");
801 iks_delete(instructions
);
803 ASTOBJ_UNREF(client
, aji_client_destroy
);
804 return IKS_FILTER_EAT
;
807 static int aji_ditems_handler(void *data
, ikspak
*pak
)
809 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
812 if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
813 iks
*iq
= NULL
, *query
= NULL
, *item
= NULL
;
815 query
= iks_new("query");
816 item
= iks_new("item");
818 if (iq
&& query
&& item
) {
819 iks_insert_attrib(iq
, "from", client
->user
);
820 iks_insert_attrib(iq
, "to", pak
->from
->full
);
821 iks_insert_attrib(iq
, "id", pak
->id
);
822 iks_insert_attrib(iq
, "type", "result");
823 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
824 iks_insert_attrib(item
, "node", "http://jabber.org/protocol/commands");
825 iks_insert_attrib(item
, "name", "Million Dollar Asterisk Commands");
826 iks_insert_attrib(item
, "jid", client
->user
);
828 iks_insert_node(iq
, query
);
829 iks_insert_node(query
, item
);
830 iks_send(client
->p
, iq
);
832 ast_log(LOG_ERROR
, "Out of memory.\n");
841 } else if (!strcasecmp(node
, "http://jabber.org/protocol/commands")) {
842 iks
*iq
, *query
, *confirm
;
844 query
= iks_new("query");
845 confirm
= iks_new("item");
846 if (iq
&& query
&& confirm
&& client
) {
847 iks_insert_attrib(iq
, "from", client
->user
);
848 iks_insert_attrib(iq
, "to", pak
->from
->full
);
849 iks_insert_attrib(iq
, "id", pak
->id
);
850 iks_insert_attrib(iq
, "type", "result");
851 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
852 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
853 iks_insert_attrib(confirm
, "node", "confirmaccount");
854 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
855 iks_insert_attrib(confirm
, "jid", "blog.astjab.org");
857 iks_insert_node(iq
, query
);
858 iks_insert_node(query
, confirm
);
859 iks_send(client
->p
, iq
);
861 ast_log(LOG_ERROR
, "Out of memory.\n");
870 } else if (!strcasecmp(node
, "confirmaccount")) {
871 iks
*iq
= NULL
, *query
= NULL
, *feature
= NULL
;
874 query
= iks_new("query");
875 feature
= iks_new("feature");
877 if (iq
&& query
&& feature
&& client
) {
878 iks_insert_attrib(iq
, "from", client
->user
);
879 iks_insert_attrib(iq
, "to", pak
->from
->full
);
880 iks_insert_attrib(iq
, "id", pak
->id
);
881 iks_insert_attrib(iq
, "type", "result");
882 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
883 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
884 iks_insert_node(iq
, query
);
885 iks_insert_node(query
, feature
);
886 iks_send(client
->p
, iq
);
888 ast_log(LOG_ERROR
, "Out of memory.\n");
898 ASTOBJ_UNREF(client
, aji_client_destroy
);
899 return IKS_FILTER_EAT
;
903 static int aji_client_info_handler(void *data
, ikspak
*pak
)
905 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
906 struct aji_resource
*resource
= NULL
;
907 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
909 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
910 if (pak
->subtype
== IKS_TYPE_RESULT
) {
912 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
913 ASTOBJ_UNREF(client
, aji_client_destroy
);
914 return IKS_FILTER_EAT
;
916 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
917 resource
->cap
->jingle
= 1;
919 resource
->cap
->jingle
= 0;
920 } else if (pak
->subtype
== IKS_TYPE_GET
) {
921 iks
*iq
, *disco
, *ident
, *google
, *query
;
923 query
= iks_new("query");
924 ident
= iks_new("identity");
925 disco
= iks_new("feature");
926 google
= iks_new("feature");
927 if (iq
&& ident
&& disco
&& google
) {
928 iks_insert_attrib(iq
, "from", client
->jid
->full
);
929 iks_insert_attrib(iq
, "to", pak
->from
->full
);
930 iks_insert_attrib(iq
, "type", "result");
931 iks_insert_attrib(iq
, "id", pak
->id
);
932 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
933 iks_insert_attrib(ident
, "category", "client");
934 iks_insert_attrib(ident
, "type", "pc");
935 iks_insert_attrib(ident
, "name", "asterisk");
936 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco#info");
937 iks_insert_attrib(google
, "var", "http://www.google.com/xmpp/protocol/voice/v1");
938 iks_insert_node(iq
, query
);
939 iks_insert_node(query
, ident
);
940 iks_insert_node(query
, google
);
941 iks_insert_node(query
, disco
);
942 iks_send(client
->p
, iq
);
944 ast_log(LOG_ERROR
, "Out of Memory.\n");
955 } else if (pak
->subtype
== IKS_TYPE_ERROR
) {
956 ast_log(LOG_NOTICE
, "User %s does not support discovery.\n", pak
->from
->full
);
958 ASTOBJ_UNREF(client
, aji_client_destroy
);
959 return IKS_FILTER_EAT
;
962 static int aji_dinfo_handler(void *data
, ikspak
*pak
)
964 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
966 struct aji_resource
*resource
= NULL
;
967 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
969 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
970 if (pak
->subtype
== IKS_TYPE_ERROR
) {
971 ast_log(LOG_WARNING
, "Recieved error from a client, turn on jabber debug!\n");
972 return IKS_FILTER_EAT
;
974 if (pak
->subtype
== IKS_TYPE_RESULT
) {
976 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
977 ASTOBJ_UNREF(client
, aji_client_destroy
);
978 return IKS_FILTER_EAT
;
980 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
981 resource
->cap
->jingle
= 1;
983 resource
->cap
->jingle
= 0;
984 } else if (pak
->subtype
== IKS_TYPE_GET
&& !(node
= iks_find_attrib(pak
->query
, "node"))) {
985 iks
*iq
, *query
, *identity
, *disco
, *reg
, *commands
, *gateway
, *version
, *vcard
, *search
;
988 query
= iks_new("query");
989 identity
= iks_new("identity");
990 disco
= iks_new("feature");
991 reg
= iks_new("feature");
992 commands
= iks_new("feature");
993 gateway
= iks_new("feature");
994 version
= iks_new("feature");
995 vcard
= iks_new("feature");
996 search
= iks_new("feature");
998 if (iq
&& query
&& identity
&& disco
&& reg
&& commands
&& gateway
&& version
&& vcard
&& search
&& client
) {
999 iks_insert_attrib(iq
, "from", client
->user
);
1000 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1001 iks_insert_attrib(iq
, "id", pak
->id
);
1002 iks_insert_attrib(iq
, "type", "result");
1003 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1004 iks_insert_attrib(identity
, "category", "gateway");
1005 iks_insert_attrib(identity
, "type", "pstn");
1006 iks_insert_attrib(identity
, "name", "Asterisk The Open Source PBX");
1007 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco");
1008 iks_insert_attrib(reg
, "var", "jabber:iq:register");
1009 iks_insert_attrib(commands
, "var", "http://jabber.org/protocol/commands");
1010 iks_insert_attrib(gateway
, "var", "jabber:iq:gateway");
1011 iks_insert_attrib(version
, "var", "jabber:iq:version");
1012 iks_insert_attrib(vcard
, "var", "vcard-temp");
1013 iks_insert_attrib(search
, "var", "jabber:iq:search");
1015 iks_insert_node(iq
, query
);
1016 iks_insert_node(query
, identity
);
1017 iks_insert_node(query
, disco
);
1018 iks_insert_node(query
, reg
);
1019 iks_insert_node(query
, commands
);
1020 iks_insert_node(query
, gateway
);
1021 iks_insert_node(query
, version
);
1022 iks_insert_node(query
, vcard
);
1023 iks_insert_node(query
, search
);
1024 iks_send(client
->p
, iq
);
1026 ast_log(LOG_ERROR
, "Out of memory.\n");
1034 iks_delete(identity
);
1040 iks_delete(commands
);
1042 iks_delete(gateway
);
1044 iks_delete(version
);
1050 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "http://jabber.org/protocol/commands")) {
1051 iks
*iq
, *query
, *confirm
;
1053 query
= iks_new("query");
1054 confirm
= iks_new("item");
1056 if (iq
&& query
&& confirm
&& client
) {
1057 iks_insert_attrib(iq
, "from", client
->user
);
1058 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1059 iks_insert_attrib(iq
, "id", pak
->id
);
1060 iks_insert_attrib(iq
, "type", "result");
1061 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
1062 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
1063 iks_insert_attrib(confirm
, "node", "confirmaccount");
1064 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
1065 iks_insert_attrib(confirm
, "jid", client
->user
);
1066 iks_insert_node(iq
, query
);
1067 iks_insert_node(query
, confirm
);
1068 iks_send(client
->p
, iq
);
1070 ast_log(LOG_ERROR
, "Out of memory.\n");
1077 iks_delete(confirm
);
1079 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "confirmaccount")) {
1080 iks
*iq
, *query
, *feature
;
1083 query
= iks_new("query");
1084 feature
= iks_new("feature");
1086 if (iq
&& query
&& feature
&& client
) {
1087 iks_insert_attrib(iq
, "from", client
->user
);
1088 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1089 iks_insert_attrib(iq
, "id", pak
->id
);
1090 iks_insert_attrib(iq
, "type", "result");
1091 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1092 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
1093 iks_insert_node(iq
, query
);
1094 iks_insert_node(query
, feature
);
1095 iks_send(client
->p
, iq
);
1097 ast_log(LOG_ERROR
, "Out of memory.\n");
1104 iks_delete(feature
);
1107 ASTOBJ_UNREF(client
, aji_client_destroy
);
1108 return IKS_FILTER_EAT
;
1112 * \brief Handles <iq> tags.
1113 * \param client structure and the iq node.
1116 static void aji_handle_iq(struct aji_client
*client
, iks
*node
)
1118 /*Nothing to see here */
1122 * \brief Handles presence packets.
1123 * \param client structure and the node.
1126 static void aji_handle_message(struct aji_client
*client
, ikspak
*pak
)
1128 struct aji_message
*insert
, *tmp
;
1131 if (!(insert
= ast_calloc(1, sizeof(struct aji_message
))))
1133 time(&insert
->arrived
);
1134 if (iks_find_cdata(pak
->x
, "body"))
1135 insert
->message
= ast_strdup(iks_find_cdata(pak
->x
, "body"));
1137 ast_copy_string(insert
->id
, pak
->id
, sizeof(insert
->message
));
1139 insert
->from
= ast_strdup(pak
->from
->full
);
1140 AST_LIST_LOCK(&client
->messages
);
1141 AST_LIST_TRAVERSE_SAFE_BEGIN(&client
->messages
, tmp
, list
) {
1143 AST_LIST_REMOVE_CURRENT(&client
->messages
, list
);
1148 } else if (difftime(time(NULL
), tmp
->arrived
) >= client
->message_timeout
) {
1150 AST_LIST_REMOVE_CURRENT(&client
->messages
, list
);
1157 AST_LIST_TRAVERSE_SAFE_END
;
1158 AST_LIST_INSERT_HEAD(&client
->messages
, insert
, list
);
1159 AST_LIST_UNLOCK(&client
->messages
);
1162 static void aji_handle_presence(struct aji_client
*client
, ikspak
*pak
)
1164 int status
, priority
;
1165 struct aji_buddy
*buddy
;
1166 struct aji_resource
*tmp
= NULL
, *last
= NULL
, *found
= NULL
;
1167 char *ver
, *node
, *descrip
, *type
;
1169 if(client
->state
!= AJI_CONNECTED
)
1170 aji_create_buddy(pak
->from
->partial
, client
);
1172 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1173 if (!buddy
&& pak
->from
->partial
) {
1174 /* allow our jid to be used to log in with another resource */
1175 if (!strcmp((const char *)pak
->from
->partial
, (const char *)client
->jid
->partial
))
1176 aji_create_buddy(pak
->from
->partial
, client
);
1178 ast_log(LOG_NOTICE
, "Got presence packet from %s, someone not in our roster!!!!\n", pak
->from
->partial
);
1181 type
= iks_find_attrib(pak
->x
, "type");
1182 if(client
->component
&& type
&&!strcasecmp("probe", type
)) {
1183 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), 1, client
->statusmessage
);
1184 ast_verbose("what i was looking for \n");
1186 ASTOBJ_WRLOCK(buddy
);
1187 status
= (pak
->show
) ? pak
->show
: 6;
1188 priority
= atoi((iks_find_cdata(pak
->x
, "priority")) ? iks_find_cdata(pak
->x
, "priority") : "0");
1189 tmp
= buddy
->resources
;
1190 descrip
= ast_strdup(iks_find_cdata(pak
->x
,"status"));
1192 while (tmp
&& pak
->from
->resource
) {
1193 if (!strcasecmp(tmp
->resource
, pak
->from
->resource
)) {
1194 tmp
->status
= status
;
1195 if (tmp
->description
) free(tmp
->description
);
1196 tmp
->description
= descrip
;
1198 if (status
== 6) { /* Sign off Destroy resource */
1199 if (last
&& found
->next
) {
1200 last
->next
= found
->next
;
1203 buddy
->resources
= found
->next
;
1205 buddy
->resources
= NULL
;
1206 } else if (!found
->next
) {
1210 buddy
->resources
= NULL
;
1216 /* resource list is sorted by descending priority */
1217 if (tmp
->priority
!= priority
) {
1218 found
->priority
= priority
;
1219 if (!last
&& !found
->next
)
1220 /* resource was found to be unique,
1223 /* search for resource in our list
1224 and take it out for the moment */
1226 last
->next
= found
->next
;
1228 buddy
->resources
= found
->next
;
1231 tmp
= buddy
->resources
;
1232 if (!buddy
->resources
)
1233 buddy
->resources
= found
;
1234 /* priority processing */
1236 /* insert resource back according to
1237 its priority value */
1238 if (found
->priority
> tmp
->priority
) {
1240 /* insert within list */
1245 buddy
->resources
= found
;
1249 /* insert at the end of the list */
1264 /* resource not found in our list, create it */
1265 if (!found
&& status
!= 6 && pak
->from
->resource
) {
1266 found
= (struct aji_resource
*) malloc(sizeof(struct aji_resource
));
1267 memset(found
, 0, sizeof(struct aji_resource
));
1270 ast_log(LOG_ERROR
, "Out of memory!\n");
1273 ast_copy_string(found
->resource
, pak
->from
->resource
, sizeof(found
->resource
));
1274 found
->status
= status
;
1275 found
->description
= descrip
;
1276 found
->priority
= priority
;
1279 tmp
= buddy
->resources
;
1281 if (found
->priority
> tmp
->priority
) {
1286 buddy
->resources
= found
;
1297 buddy
->resources
= found
;
1300 ASTOBJ_UNLOCK(buddy
);
1301 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
1303 node
= iks_find_attrib(iks_find(pak
->x
, "c"), "node");
1304 ver
= iks_find_attrib(iks_find(pak
->x
, "c"), "ver");
1306 /* handle gmail client's special caps:c tag */
1307 if (!node
&& !ver
) {
1308 node
= iks_find_attrib(iks_find(pak
->x
, "caps:c"), "node");
1309 ver
= iks_find_attrib(iks_find(pak
->x
, "caps:c"), "ver");
1312 /* retrieve capabilites of the new resource */
1313 if(status
!=6 && found
&& !found
->cap
) {
1314 found
->cap
= aji_find_version(node
, ver
, pak
);
1315 if(gtalk_yuck(pak
->x
)) /* gtalk should do discover */
1316 found
->cap
->jingle
= 1;
1317 if(found
->cap
->jingle
&& option_debug
> 4)
1318 ast_log(LOG_DEBUG
,"Special case for google till they support discover.\n");
1322 query
= iks_new("query");
1324 iks_insert_attrib(iq
, "type", "get");
1325 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1326 iks_insert_attrib(iq
,"from", client
->jid
->full
);
1327 iks_insert_attrib(iq
, "id", client
->mid
);
1328 ast_aji_increment_mid(client
->mid
);
1329 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1330 iks_insert_node(iq
, query
);
1331 iks_send(client
->p
, iq
);
1334 ast_log(LOG_ERROR
, "Out of memory.\n");
1341 if (option_verbose
> 4) {
1342 switch (pak
->subtype
) {
1343 case IKS_TYPE_AVAILABLE
:
1344 ast_verbose(VERBOSE_PREFIX_3
"JABBER: I am available ^_* %i\n", pak
->subtype
);
1346 case IKS_TYPE_UNAVAILABLE
:
1347 ast_verbose(VERBOSE_PREFIX_3
"JABBER: I am unavailable ^_* %i\n", pak
->subtype
);
1350 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Ohh sexy and the wrong type: %i\n", pak
->subtype
);
1352 switch (pak
->show
) {
1353 case IKS_SHOW_UNAVAILABLE
:
1354 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1356 case IKS_SHOW_AVAILABLE
:
1357 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type is available\n");
1360 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1363 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type is away\n");
1366 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1369 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1372 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Kinky! how did that happen %i\n", pak
->show
);
1378 * \brief handles subscription requests.
1379 * \param aji_client struct and xml packet.
1382 static void aji_handle_subscribe(struct aji_client
*client
, ikspak
*pak
)
1384 if(pak
->subtype
== IKS_TYPE_SUBSCRIBE
) {
1385 iks
*presence
= NULL
, *status
= NULL
;
1386 presence
= iks_new("presence");
1387 status
= iks_new("status");
1388 if(presence
&& status
) {
1389 iks_insert_attrib(presence
, "type", "subscribed");
1390 iks_insert_attrib(presence
, "to", pak
->from
->full
);
1391 iks_insert_attrib(presence
, "from", client
->jid
->full
);
1393 iks_insert_attrib(presence
, "id", pak
->id
);
1394 iks_insert_cdata(status
, "Asterisk has approved subscription", 0);
1395 iks_insert_node(presence
, status
);
1396 iks_send(client
->p
, presence
);
1398 ast_log(LOG_ERROR
, "Unable to allocate nodes\n");
1400 iks_delete(presence
);
1403 if(client
->component
)
1404 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), 1, client
->statusmessage
);
1406 if (option_verbose
> 4) {
1407 switch (pak
->subtype
) {
1408 case IKS_TYPE_SUBSCRIBE
:
1409 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1411 case IKS_TYPE_SUBSCRIBED
:
1412 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1414 case IKS_TYPE_UNSUBSCRIBE
:
1415 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1417 case IKS_TYPE_UNSUBSCRIBED
:
1418 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1420 default: /*IKS_TYPE_ERROR: */
1421 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1428 * \brief sends messages.
1429 * \param aji_client struct , reciever, message.
1432 int ast_aji_send(struct aji_client
*client
, const char *address
, const char *message
)
1435 iks
*message_packet
= NULL
;
1436 if (client
->state
== AJI_CONNECTED
) {
1437 message_packet
= iks_make_msg(IKS_TYPE_CHAT
, address
, message
);
1438 if (message_packet
) {
1439 iks_insert_attrib(message_packet
, "from", client
->jid
->full
);
1440 res
= iks_send(client
->p
, message_packet
);
1442 ast_log(LOG_ERROR
, "Out of memory.\n");
1445 iks_delete(message_packet
);
1447 ast_log(LOG_WARNING
, "JABBER: Not connected can't send\n");
1452 * \brief create a chatroom.
1453 * \param aji_client struct , room, server, topic for the room.
1456 int ast_aji_create_chat(struct aji_client
*client
, char *room
, char *server
, char *topic
)
1462 iks_insert_attrib(iq
, "type", "get");
1463 iks_insert_attrib(iq
, "to", server
);
1464 iks_insert_attrib(iq
, "id", client
->mid
);
1465 ast_aji_increment_mid(client
->mid
);
1466 iks_send(client
->p
, iq
);
1468 ast_log(LOG_ERROR
, "Out of memory.\n");
1473 * \brief join a chatroom.
1474 * \param aji_client struct , room.
1477 int ast_aji_join_chat(struct aji_client
*client
, char *room
)
1480 iks
*presence
= NULL
, *priority
= NULL
;
1481 presence
= iks_new("presence");
1482 priority
= iks_new("priority");
1483 if (presence
&& priority
&& client
) {
1484 iks_insert_cdata(priority
, "0", 1);
1485 iks_insert_attrib(presence
, "to", room
);
1486 iks_insert_node(presence
, priority
);
1487 res
= iks_send(client
->p
, presence
);
1488 iks_insert_cdata(priority
, "5", 1);
1489 iks_insert_attrib(presence
, "to", room
);
1490 res
= iks_send(client
->p
, presence
);
1492 ast_log(LOG_ERROR
, "Out of memory.\n");
1494 iks_delete(presence
);
1496 iks_delete(priority
);
1501 * \brief invite to a chatroom.
1502 * \param aji_client struct ,user, room, message.
1505 int ast_aji_invite_chat(struct aji_client
*client
, char *user
, char *room
, char *message
)
1508 iks
*invite
, *body
, *namespace;
1510 invite
= iks_new("message");
1511 body
= iks_new("body");
1512 namespace = iks_new("x");
1513 if (client
&& invite
&& body
&& namespace) {
1514 iks_insert_attrib(invite
, "to", user
);
1515 iks_insert_attrib(invite
, "id", client
->mid
);
1516 ast_aji_increment_mid(client
->mid
);
1517 iks_insert_cdata(body
, message
, 0);
1518 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
1519 iks_insert_attrib(namespace, "jid", room
);
1520 iks_insert_node(invite
, body
);
1521 iks_insert_node(invite
, namespace);
1522 res
= iks_send(client
->p
, invite
);
1524 ast_log(LOG_ERROR
, "Out of memory.\n");
1528 iks_delete(namespace);
1536 * \brief receive message loop.
1537 * \param aji_client struct.
1540 static void *aji_recv_loop(void *data
)
1542 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1545 if (res
!= IKS_OK
) {
1546 while(res
!= IKS_OK
) {
1547 if(option_verbose
> 3)
1548 ast_verbose("JABBER: reconnecting.\n");
1549 res
= aji_reconnect(client
);
1554 res
= iks_recv(client
->p
, 1);
1556 if (client
->state
== AJI_DISCONNECTING
) {
1557 if (option_debug
> 1)
1558 ast_log(LOG_DEBUG
, "Ending our Jabber client's thread due to a disconnect\n");
1562 if (res
== IKS_HOOK
)
1563 ast_log(LOG_WARNING
, "JABBER: Got hook event.\n");
1564 else if (res
== IKS_NET_TLSFAIL
)
1565 ast_log(LOG_WARNING
, "JABBER: Failure in TLS.\n");
1566 else if (client
->timeout
== 0 && client
->state
== AJI_CONNECTED
) {
1567 res
= iks_send_raw(client
->p
, " ");
1569 client
->timeout
= 50;
1571 ast_log(LOG_WARNING
, "JABBER: Network Timeout\n");
1572 } else if (res
== IKS_NET_RWERR
)
1573 ast_log(LOG_WARNING
, "JABBER: socket read error\n");
1575 ASTOBJ_UNREF(client
, aji_client_destroy
);
1580 * \brief increments the mid field for messages and other events.
1581 * \param message id.
1584 void ast_aji_increment_mid(char *mid
)
1588 for (i
= strlen(mid
) - 1; i
>= 0; i
--) {
1589 if (mid
[i
] != 'z') {
1590 mid
[i
] = mid
[i
] + 1;
1599 * \brief attempts to register to a transport.
1600 * \param aji_client struct, and xml packet.
1601 * \return IKS_FILTER_EAT.
1603 /*allows for registering to transport , was too sketch and is out for now. */
1604 /*static int aji_register_transport(void *data, ikspak *pak)
1606 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1608 struct aji_buddy *buddy = NULL;
1609 iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
1611 if (client && send) {
1612 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
1613 ASTOBJ_RDLOCK(iterator);
1614 if (iterator->btype == AJI_TRANS) {
1617 ASTOBJ_UNLOCK(iterator);
1619 iks_filter_remove_hook(client->f, aji_register_transport);
1620 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);
1621 iks_insert_attrib(send, "to", buddy->host);
1622 iks_insert_attrib(send, "id", client->mid);
1623 ast_aji_increment_mid(client->mid);
1624 iks_insert_attrib(send, "from", client->user);
1625 res = iks_send(client->p, send);
1627 ast_log(LOG_ERROR, "Out of memory.\n");
1631 ASTOBJ_UNREF(client, aji_client_destroy);
1632 return IKS_FILTER_EAT;
1637 * \brief attempts to register to a transport step 2.
1638 * \param aji_client struct, and xml packet.
1639 * \return IKS_FILTER_EAT.
1641 /* more of the same blob of code, too wonky for now*/
1642 /* static int aji_register_transport2(void *data, ikspak *pak)
1644 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1646 struct aji_buddy *buddy = NULL;
1648 iks *regiq = iks_new("iq");
1649 iks *regquery = iks_new("query");
1650 iks *reguser = iks_new("username");
1651 iks *regpass = iks_new("password");
1653 if (client && regquery && reguser && regpass && regiq) {
1654 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
1655 ASTOBJ_RDLOCK(iterator);
1656 if (iterator->btype == AJI_TRANS)
1657 buddy = iterator; ASTOBJ_UNLOCK(iterator);
1659 iks_filter_remove_hook(client->f, aji_register_transport2);
1660 iks_insert_attrib(regiq, "to", buddy->host);
1661 iks_insert_attrib(regiq, "type", "set");
1662 iks_insert_attrib(regiq, "id", client->mid);
1663 ast_aji_increment_mid(client->mid);
1664 iks_insert_attrib(regiq, "from", client->user);
1665 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
1666 iks_insert_cdata(reguser, buddy->user, 0);
1667 iks_insert_cdata(regpass, buddy->pass, 0);
1668 iks_insert_node(regiq, regquery);
1669 iks_insert_node(regquery, reguser);
1670 iks_insert_node(regquery, regpass);
1671 res = iks_send(client->p, regiq);
1673 ast_log(LOG_ERROR, "Out of memory.\n");
1677 iks_delete(regquery);
1679 iks_delete(reguser);
1681 iks_delete(regpass);
1682 ASTOBJ_UNREF(client, aji_client_destroy);
1683 return IKS_FILTER_EAT;
1687 * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
1688 * \param aji_client struct.
1691 static void aji_pruneregister(struct aji_client
*client
)
1694 iks
*removeiq
= iks_new("iq");
1695 iks
*removequery
= iks_new("query");
1696 iks
*removeitem
= iks_new("item");
1697 iks
*send
= iks_make_iq(IKS_TYPE_GET
, "http://jabber.org/protocol/disco#items");
1699 if (client
&& removeiq
&& removequery
&& removeitem
&& send
) {
1700 iks_insert_node(removeiq
, removequery
);
1701 iks_insert_node(removequery
, removeitem
);
1702 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1703 ASTOBJ_RDLOCK(iterator
);
1704 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
1705 * be called at the same time */
1706 if (ast_test_flag(iterator
, AJI_AUTOPRUNE
)) {
1707 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE
, iterator
->name
,
1708 "GoodBye your status is no longer needed by Asterisk the Open Source PBX"
1709 " so I am no longer subscribing to your presence.\n"));
1710 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED
, iterator
->name
,
1711 "GoodBye you are no longer in the asterisk config file so I am removing"
1712 " your access to my presence.\n"));
1713 iks_insert_attrib(removeiq
, "from", client
->jid
->full
);
1714 iks_insert_attrib(removeiq
, "type", "set");
1715 iks_insert_attrib(removequery
, "xmlns", "jabber:iq:roster");
1716 iks_insert_attrib(removeitem
, "jid", iterator
->name
);
1717 iks_insert_attrib(removeitem
, "subscription", "remove");
1718 res
= iks_send(client
->p
, removeiq
);
1719 } else if (ast_test_flag(iterator
, AJI_AUTOREGISTER
)) {
1720 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_SUBSCRIBE
, iterator
->name
,
1721 "Greetings I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
1722 ast_clear_flag(iterator
, AJI_AUTOREGISTER
);
1724 ASTOBJ_UNLOCK(iterator
);
1727 ast_log(LOG_ERROR
, "Out of memory.\n");
1729 iks_delete(removeiq
);
1731 iks_delete(removequery
);
1733 iks_delete(removeitem
);
1736 ASTOBJ_CONTAINER_PRUNE_MARKED(&client
->buddies
, aji_buddy_destroy
);
1740 * \brief filters the roster packet we get back from server.
1741 * \param aji_client struct, and xml packet.
1742 * \return IKS_FILTER_EAT.
1744 static int aji_filter_roster(void *data
, ikspak
*pak
)
1746 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1749 struct aji_buddy
*buddy
;
1751 client
->state
= AJI_CONNECTED
;
1752 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1753 ASTOBJ_RDLOCK(iterator
);
1754 x
= iks_child(pak
->query
);
1757 if (!iks_strcmp(iks_name(x
), "item")) {
1758 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid"))) {
1760 ast_clear_flag(iterator
, AJI_AUTOPRUNE
| AJI_AUTOREGISTER
);
1766 ast_copy_flags(iterator
, client
, AJI_AUTOREGISTER
);
1769 ASTOBJ_UNLOCK(iterator
);
1772 x
= iks_child(pak
->query
);
1775 if (iks_strcmp(iks_name(x
), "item") == 0) {
1776 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1777 ASTOBJ_RDLOCK(iterator
);
1778 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid")))
1780 ASTOBJ_UNLOCK(iterator
);
1784 buddy
= (struct aji_buddy
*) malloc(sizeof(struct aji_buddy
));
1786 ast_log(LOG_WARNING
, "Out of memory\n");
1789 memset(buddy
, 0, sizeof(struct aji_buddy
));
1791 ASTOBJ_WRLOCK(buddy
);
1792 ast_copy_string(buddy
->name
, iks_find_attrib(x
, "jid"), sizeof(buddy
->name
));
1793 ast_clear_flag(buddy
, AST_FLAGS_ALL
);
1794 if(ast_test_flag(client
, AJI_AUTOPRUNE
)) {
1795 ast_set_flag(buddy
, AJI_AUTOPRUNE
);
1796 buddy
->objflags
|= ASTOBJ_FLAG_MARKED
;
1798 ast_set_flag(buddy
, AJI_AUTOREGISTER
);
1799 ASTOBJ_UNLOCK(buddy
);
1801 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
1802 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
1810 aji_pruneregister(client
);
1812 ASTOBJ_UNREF(client
, aji_client_destroy
);
1813 return IKS_FILTER_EAT
;
1816 static int aji_reconnect(struct aji_client
*client
)
1821 client
->state
= AJI_DISCONNECTED
;
1824 iks_parser_reset(client
->p
);
1825 if (client
->authorized
)
1826 client
->authorized
= 0;
1828 if(client
->component
)
1829 res
= aji_component_initialize(client
);
1831 res
= aji_client_initialize(client
);
1836 static int aji_get_roster(struct aji_client
*client
)
1839 roster
= iks_make_iq(IKS_TYPE_GET
, IKS_NS_ROSTER
);
1841 iks_insert_attrib(roster
, "id", "roster");
1842 aji_set_presence(client
, NULL
, client
->jid
->full
, 1, client
->statusmessage
);
1843 iks_send(client
->p
, roster
);
1851 * \brief connects as a client to jabber server.
1852 * \param aji_client struct, and xml packet.
1855 static int aji_client_connect(void *data
, ikspak
*pak
)
1857 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1861 if (client
->state
== AJI_DISCONNECTED
) {
1862 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
);
1863 client
->state
= AJI_CONNECTING
;
1864 client
->jid
= (iks_find_cdata(pak
->query
, "jid")) ? iks_id_new(client
->stack
, iks_find_cdata(pak
->query
, "jid")) : client
->jid
;
1865 iks_filter_remove_hook(client
->f
, aji_client_connect
);
1866 if(!client
->component
) /*client*/
1867 aji_get_roster(client
);
1870 ast_log(LOG_ERROR
, "Out of memory.\n");
1872 ASTOBJ_UNREF(client
, aji_client_destroy
);
1877 * \brief prepares client for connect.
1878 * \param aji_client struct.
1881 static int aji_client_initialize(struct aji_client
*client
)
1885 connected
= iks_connect_via(client
->p
, S_OR(client
->serverhost
, client
->jid
->server
), client
->port
, client
->jid
->server
);
1887 if (connected
== IKS_NET_NOCONN
) {
1888 ast_log(LOG_ERROR
, "JABBER ERROR: No Connection\n");
1890 } else if (connected
== IKS_NET_NODNS
) {
1891 ast_log(LOG_ERROR
, "JABBER ERROR: No DNS %s for client to %s\n", client
->name
, S_OR(client
->serverhost
, client
->jid
->server
));
1894 iks_recv(client
->p
, 30);
1899 * \brief prepares component for connect.
1900 * \param aji_client struct.
1903 static int aji_component_initialize(struct aji_client
*client
)
1907 connected
= iks_connect_via(client
->p
, S_OR(client
->serverhost
, client
->jid
->server
), client
->port
, client
->user
);
1908 if (connected
== IKS_NET_NOCONN
) {
1909 ast_log(LOG_ERROR
, "JABBER ERROR: No Connection\n");
1911 } else if (connected
== IKS_NET_NODNS
) {
1912 ast_log(LOG_ERROR
, "JABBER ERROR: No DNS %s for client to %s\n", client
->name
, S_OR(client
->serverhost
, client
->jid
->server
));
1914 } else if (!connected
)
1915 iks_recv(client
->p
, 30);
1920 * \brief disconnect from jabber server.
1921 * \param aji_client struct.
1924 int ast_aji_disconnect(struct aji_client
*client
)
1927 if (option_verbose
> 3)
1928 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Disconnecting\n");
1929 iks_disconnect(client
->p
);
1930 iks_parser_delete(client
->p
);
1931 ASTOBJ_UNREF(client
, aji_client_destroy
);
1938 * \brief set presence of client.
1939 * \param aji_client struct, user to send it to, and from, level, description.
1942 static void aji_set_presence(struct aji_client
*client
, char *to
, char *from
, int level
, char *desc
)
1945 iks
*presence
= iks_make_pres(level
, desc
);
1946 iks
*cnode
= iks_new("c");
1947 iks
*priority
= iks_new("priority");
1949 iks_insert_cdata(priority
, "0", 1);
1950 if (presence
&& cnode
&& client
) {
1952 iks_insert_attrib(presence
, "to", to
);
1954 iks_insert_attrib(presence
, "from", from
);
1955 iks_insert_attrib(cnode
, "node", "http://www.asterisk.org/xmpp/client/caps");
1956 iks_insert_attrib(cnode
, "ver", "asterisk-xmpp");
1957 iks_insert_attrib(cnode
, "ext", "voice-v1");
1958 iks_insert_attrib(cnode
, "xmlns", "http://jabber.org/protocol/caps");
1959 iks_insert_node(presence
, cnode
);
1960 res
= iks_send(client
->p
, presence
);
1962 ast_log(LOG_ERROR
, "Out of memory.\n");
1966 iks_delete(presence
);
1970 * \brief turnon console debugging.
1971 * \param fd, number of args, args.
1972 * \return RESULT_SUCCESS.
1974 static int aji_do_debug(int fd
, int argc
, char *argv
[])
1976 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
1977 ASTOBJ_RDLOCK(iterator
);
1978 iterator
->debug
= 1;
1979 ASTOBJ_UNLOCK(iterator
);
1981 ast_cli(fd
, "Jabber Debugging Enabled.\n");
1982 return RESULT_SUCCESS
;
1986 * \brief reload jabber module.
1987 * \param fd, number of args, args.
1988 * \return RESULT_SUCCESS.
1990 static int aji_do_reload(int fd
, int argc
, char *argv
[])
1993 ast_cli(fd
, "Jabber Reloaded.\n");
1994 return RESULT_SUCCESS
;
1998 * \brief turnoff console debugging.
1999 * \param fd, number of args, args.
2000 * \return RESULT_SUCCESS.
2002 static int aji_no_debug(int fd
, int argc
, char *argv
[])
2004 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2005 ASTOBJ_RDLOCK(iterator
);
2006 iterator
->debug
= 0;
2007 ASTOBJ_UNLOCK(iterator
);
2009 ast_cli(fd
, "Jabber Debugging Disabled.\n");
2010 return RESULT_SUCCESS
;
2014 * \brief show client status.
2015 * \param fd, number of args, args.
2016 * \return RESULT_SUCCESS.
2018 static int aji_show_clients(int fd
, int argc
, char *argv
[])
2022 ast_cli(fd
, "Jabber Users and their status:\n");
2023 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2024 ASTOBJ_RDLOCK(iterator
);
2026 switch (iterator
->state
) {
2027 case AJI_DISCONNECTED
:
2028 status
= "Disconnected";
2030 case AJI_CONNECTING
:
2031 status
= "Connecting";
2034 status
= "Connected";
2039 ast_cli(fd
, " User: %s - %s\n", iterator
->user
, status
);
2040 ASTOBJ_UNLOCK(iterator
);
2042 ast_cli(fd
, "----\n");
2043 ast_cli(fd
, " Number of users: %d\n", count
);
2044 return RESULT_SUCCESS
;
2048 * \brief send test message for debugging.
2049 * \param fd, number of args, args.
2050 * \return RESULT_SUCCESS.
2052 static int aji_test(int fd
, int argc
, char *argv
[])
2054 struct aji_client
*client
;
2055 struct aji_resource
*resource
;
2056 const char *name
= "asterisk";
2057 struct aji_message
*tmp
;
2060 return RESULT_SHOWUSAGE
;
2064 if (!(client
= ASTOBJ_CONTAINER_FIND(&clients
, name
))) {
2065 ast_cli(fd
, "Unable to find client '%s'!\n", name
);
2066 return RESULT_FAILURE
;
2069 /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
2070 ast_aji_send(client
, "mogorman@astjab.org", "blahblah");
2071 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2072 ASTOBJ_RDLOCK(iterator
);
2073 ast_verbose("User: %s\n", iterator
->name
);
2074 for (resource
= iterator
->resources
; resource
; resource
= resource
->next
) {
2075 ast_verbose("Resource: %s\n", resource
->resource
);
2077 ast_verbose(" client: %s\n", resource
->cap
->parent
->node
);
2078 ast_verbose(" version: %s\n", resource
->cap
->version
);
2079 ast_verbose(" Jingle Capable: %d\n", resource
->cap
->jingle
);
2081 ast_verbose(" Priority: %d\n", resource
->priority
);
2082 ast_verbose(" Status: %d\n", resource
->status
);
2083 ast_verbose(" Message: %s\n", S_OR(resource
->description
,""));
2085 ASTOBJ_UNLOCK(iterator
);
2087 ast_verbose("\nOooh a working message stack!\n");
2088 AST_LIST_LOCK(&client
->messages
);
2089 AST_LIST_TRAVERSE(&client
->messages
, tmp
, list
) {
2090 ast_verbose(" Message from: %s with id %s @ %s %s\n",tmp
->from
, S_OR(tmp
->id
,""), ctime(&tmp
->arrived
), S_OR(tmp
->message
, ""));
2092 AST_LIST_UNLOCK(&client
->messages
);
2093 ASTOBJ_UNREF(client
, aji_client_destroy
);
2095 return RESULT_SUCCESS
;
2099 * \brief creates aji_client structure.
2100 * \param label, ast_variable, debug, pruneregister, component/client, aji_client to dump into.
2103 static int aji_create_client(char *label
, struct ast_variable
*var
, int debug
)
2106 struct aji_client
*client
= NULL
;
2109 client
= ASTOBJ_CONTAINER_FIND(&clients
,label
);
2112 client
= (struct aji_client
*) malloc(sizeof(struct aji_client
));
2114 ast_log(LOG_ERROR
, "Out of memory!\n");
2117 memset(client
, 0, sizeof(struct aji_client
));
2118 ASTOBJ_INIT(client
);
2119 ASTOBJ_WRLOCK(client
);
2120 ASTOBJ_CONTAINER_INIT(&client
->buddies
);
2122 ASTOBJ_WRLOCK(client
);
2123 ASTOBJ_UNMARK(client
);
2125 ASTOBJ_CONTAINER_MARKALL(&client
->buddies
);
2126 ast_copy_string(client
->name
, label
, sizeof(client
->name
));
2127 ast_copy_string(client
->mid
, "aaaaa", sizeof(client
->mid
));
2129 /* Set default values for the client object */
2130 client
->debug
= debug
;
2131 ast_copy_flags(client
, &globalflags
, AST_FLAGS_ALL
);
2132 client
->port
= 5222;
2134 client
->usesasl
= 1;
2135 client
->forcessl
= 0;
2136 client
->keepalive
= 1;
2137 client
->timeout
= 50;
2138 client
->message_timeout
= 100;
2139 AST_LIST_HEAD_INIT(&client
->messages
);
2140 client
->component
= 0;
2141 ast_copy_string(client
->statusmessage
, "Online and Available", sizeof(client
->statusmessage
));
2144 client
->authorized
= 0;
2145 client
->state
= AJI_DISCONNECTED
;
2148 if (!strcasecmp(var
->name
, "username"))
2149 ast_copy_string(client
->user
, var
->value
, sizeof(client
->user
));
2150 else if (!strcasecmp(var
->name
, "serverhost"))
2151 ast_copy_string(client
->serverhost
, var
->value
, sizeof(client
->serverhost
));
2152 else if (!strcasecmp(var
->name
, "secret"))
2153 ast_copy_string(client
->password
, var
->value
, sizeof(client
->password
));
2154 else if (!strcasecmp(var
->name
, "statusmessage"))
2155 ast_copy_string(client
->statusmessage
, var
->value
, sizeof(client
->statusmessage
));
2156 else if (!strcasecmp(var
->name
, "port"))
2157 client
->port
= atoi(var
->value
);
2158 else if (!strcasecmp(var
->name
, "timeout"))
2159 client
->message_timeout
= atoi(var
->value
);
2160 else if (!strcasecmp(var
->name
, "debug"))
2161 client
->debug
= (ast_false(var
->value
)) ? 0 : 1;
2162 else if (!strcasecmp(var
->name
, "type")) {
2163 if (!strcasecmp(var
->value
, "component"))
2164 client
->component
= 1;
2165 } else if (!strcasecmp(var
->name
, "usetls")) {
2166 client
->usetls
= (ast_false(var
->value
)) ? 0 : 1;
2167 } else if (!strcasecmp(var
->name
, "usesasl")) {
2168 client
->usesasl
= (ast_false(var
->value
)) ? 0 : 1;
2169 } else if (!strcasecmp(var
->name
, "forceoldssl"))
2170 client
->forcessl
= (ast_false(var
->value
)) ? 0 : 1;
2171 else if (!strcasecmp(var
->name
, "keepalive"))
2172 client
->keepalive
= (ast_false(var
->value
)) ? 0 : 1;
2173 else if (!strcasecmp(var
->name
, "autoprune"))
2174 ast_set2_flag(client
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2175 else if (!strcasecmp(var
->name
, "autoregister"))
2176 ast_set2_flag(client
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2177 else if (!strcasecmp(var
->name
, "buddy"))
2178 aji_create_buddy(var
->value
, client
);
2179 /* no transport support in this version */
2180 /* else if (!strcasecmp(var->name, "transport"))
2181 aji_create_transport(var->value, client);
2186 ASTOBJ_UNLOCK(client
);
2187 ASTOBJ_UNREF(client
, aji_client_destroy
);
2190 client
->p
= iks_stream_new(((client
->component
) ? "jabber:component:accept" : "jabber:client"), client
, aji_act_hook
);
2192 ast_log(LOG_ERROR
, "Failed to create stream for client '%s'!\n", client
->name
);
2195 client
->stack
= iks_stack_new(8192, 8192);
2196 if (!client
->stack
) {
2197 ast_log(LOG_ERROR
, "Failed to allocate stack for client '%s'\n", client
->name
);
2200 client
->f
= iks_filter_new();
2202 ast_log(LOG_ERROR
, "Failed to create filter for client '%s'\n", client
->name
);
2205 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2207 asprintf(&resource
, "%s/asterisk", client
->user
);
2209 client
->jid
= iks_id_new(client
->stack
, resource
);
2213 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2214 if (client
->component
) {
2215 iks_filter_add_rule(client
->f
, aji_dinfo_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2216 iks_filter_add_rule(client
->f
, aji_ditems_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE
);
2217 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
);
2218 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
);
2220 iks_filter_add_rule(client
->f
, aji_client_info_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2222 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2224 asprintf(&resource
, "%s/asterisk", client
->user
);
2226 client
->jid
= iks_id_new(client
->stack
, resource
);
2230 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2231 iks_set_log_hook(client
->p
, aji_log_hook
);
2232 ASTOBJ_UNLOCK(client
);
2233 ASTOBJ_CONTAINER_LINK(&clients
,client
);
2238 * \brief creates transport.
2239 * \param label, buddy to dump it into.
2242 /* no connecting to transports today */
2244 static int aji_create_transport(char *label, struct aji_client *client)
2246 char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
2247 struct aji_buddy *buddy = NULL;
2249 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
2251 buddy = malloc(sizeof(struct aji_buddy));
2253 ast_log(LOG_WARNING, "Out of memory\n");
2256 memset(buddy, 0, sizeof(struct aji_buddy));
2259 ASTOBJ_WRLOCK(buddy);
2261 if ((buddyname = strchr(label, ','))) {
2264 if (buddyname && buddyname[0] != '\0') {
2265 if ((user = strchr(buddyname, ','))) {
2268 if (user && user[0] != '\0') {
2269 if ((pass = strchr(user, ','))) {
2272 ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
2273 ast_copy_string(buddy->user, user, sizeof(buddy->user));
2274 ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
2275 ast_copy_string(buddy->server, server, sizeof(buddy->server));
2282 ASTOBJ_UNLOCK(buddy);
2283 ASTOBJ_UNMARK(buddy);
2284 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
2290 * \brief creates buddy.
2291 * \param label, buddy to dump it into.
2294 static int aji_create_buddy(char *label
, struct aji_client
*client
)
2296 struct aji_buddy
*buddy
= NULL
;
2298 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
,label
);
2301 buddy
= malloc(sizeof(struct aji_buddy
));
2303 ast_log(LOG_WARNING
, "Out of memory\n");
2306 memset(buddy
, 0, sizeof(struct aji_buddy
));
2309 ASTOBJ_WRLOCK(buddy
);
2310 ast_copy_string(buddy
->name
, label
, sizeof(buddy
->name
));
2311 ASTOBJ_UNLOCK(buddy
);
2313 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
2315 ASTOBJ_UNMARK(buddy
);
2316 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
2322 * \brief load config file.
2326 static int aji_load_config(void)
2330 struct ast_config
*cfg
= NULL
;
2331 struct ast_variable
*var
= NULL
;
2333 cfg
= ast_config_load(JABBER_CONFIG
);
2335 ast_log(LOG_WARNING
, "No such configuration file %s\n", JABBER_CONFIG
);
2339 cat
= ast_category_browse(cfg
, NULL
);
2340 for (var
= ast_variable_browse(cfg
, "general"); var
; var
= var
->next
) {
2341 if (!strcasecmp(var
->name
, "debug"))
2342 debug
= (ast_false(ast_variable_retrieve(cfg
, "general", "debug"))) ? 0 : 1;
2343 else if (!strcasecmp(var
->name
, "autoprune"))
2344 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2345 else if (!strcasecmp(var
->name
, "autoregister"))
2346 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2350 if (strcasecmp(cat
, "general")) {
2351 var
= ast_variable_browse(cfg
, cat
);
2352 aji_create_client(cat
, var
, debug
);
2354 cat
= ast_category_browse(cfg
, cat
);
2360 * \brief grab a aji_client structure by label name.
2364 struct aji_client
*ast_aji_get_client(const char *name
)
2366 struct aji_client
*client
= NULL
;
2368 client
= ASTOBJ_CONTAINER_FIND(&clients
, name
);
2369 if (!client
&& !strchr(name
, '@'))
2370 client
= ASTOBJ_CONTAINER_FIND_FULL(&clients
, name
, user
,,, strcasecmp
);
2374 struct aji_client_container
*ast_aji_get_clients(void)
2379 static char mandescr_jabber_send
[] =
2380 "Description: Sends a message to a Jabber Client.\n"
2382 " Jabber: Client or transport Asterisk uses to connect to JABBER.\n"
2383 " ScreenName: User Name to message.\n"
2384 " Message: Message to be sent to the buddy\n";
2386 /*! \brief Send a Jabber Message via call from the Manager */
2387 static int manager_jabber_send(struct mansession
*s
, const struct message
*m
)
2389 struct aji_client
*client
= NULL
;
2390 const char *id
= astman_get_header(m
,"ActionID");
2391 const char *jabber
= astman_get_header(m
,"Jabber");
2392 const char *screenname
= astman_get_header(m
,"ScreenName");
2393 const char *message
= astman_get_header(m
,"Message");
2395 if (ast_strlen_zero(jabber
)) {
2396 astman_send_error(s
, m
, "No transport specified");
2399 if (ast_strlen_zero(screenname
)) {
2400 astman_send_error(s
, m
, "No ScreenName specified");
2403 if (ast_strlen_zero(message
)) {
2404 astman_send_error(s
, m
, "No Message specified");
2408 astman_send_ack(s
, m
, "Attempting to send Jabber Message");
2409 client
= ast_aji_get_client(jabber
);
2411 astman_send_error(s
, m
, "Could not find Sender");
2414 if (strchr(screenname
, '@') && message
){
2415 ast_aji_send(client
, screenname
, message
);
2416 if (!ast_strlen_zero(id
))
2417 astman_append(s
, "ActionID: %s\r\n",id
);
2418 astman_append(s
, "Response: Success\r\n");
2421 if (!ast_strlen_zero(id
))
2422 astman_append(s
, "ActionID: %s\r\n",id
);
2423 astman_append(s
, "Response: Failure\r\n");
2428 static int aji_reload()
2430 ASTOBJ_CONTAINER_MARKALL(&clients
);
2431 if (!aji_load_config()) {
2432 ast_log(LOG_ERROR
, "JABBER: Failed to load config.\n");
2435 ASTOBJ_CONTAINER_PRUNE_MARKED(&clients
, aji_client_destroy
);
2436 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2437 ASTOBJ_RDLOCK(iterator
);
2438 if(iterator
->state
== AJI_DISCONNECTED
) {
2439 if (!iterator
->thread
)
2440 ast_pthread_create_background(&iterator
->thread
, NULL
, aji_recv_loop
, iterator
);
2441 } else if (iterator
->state
== AJI_CONNECTING
)
2442 aji_get_roster(iterator
);
2443 ASTOBJ_UNLOCK(iterator
);
2449 static int unload_module(void)
2452 /* Check if TLS is initialized. If that's the case, we can't unload this
2453 module due to a bug in the iksemel library that will cause a crash or
2454 a deadlock. We're trying to find a way to handle this, but in the meantime
2455 we will simply refuse to die...
2457 if (tls_initialized
) {
2458 ast_log(LOG_ERROR
, "Module can't be unloaded due to a bug in the Iksemel library when using TLS.\n");
2459 return 1; /* You need a forced unload to get rid of this module */
2462 ast_cli_unregister_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
2463 ast_unregister_application(app_ajisend
);
2464 ast_unregister_application(app_ajistatus
);
2465 ast_manager_unregister("JabberSend");
2467 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2468 ASTOBJ_RDLOCK(iterator
);
2469 if (option_debug
> 2)
2470 ast_log(LOG_DEBUG
, "JABBER: Releasing and disconneing client: %s\n", iterator
->name
);
2471 iterator
->state
= AJI_DISCONNECTING
;
2472 ast_aji_disconnect(iterator
);
2473 pthread_join(iterator
->thread
, NULL
);
2474 ASTOBJ_UNLOCK(iterator
);
2477 ASTOBJ_CONTAINER_DESTROYALL(&clients
, aji_client_destroy
);
2478 ASTOBJ_CONTAINER_DESTROY(&clients
);
2482 static int load_module(void)
2484 ASTOBJ_CONTAINER_INIT(&clients
);
2486 return AST_MODULE_LOAD_DECLINE
;
2487 ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM
, manager_jabber_send
,
2488 "Sends a message to a Jabber Client", mandescr_jabber_send
);
2489 ast_register_application(app_ajisend
, aji_send_exec
, ajisend_synopsis
, ajisend_descrip
);
2490 ast_register_application(app_ajistatus
, aji_status_exec
, ajistatus_synopsis
, ajistatus_descrip
);
2491 ast_cli_register_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
2496 static int reload(void)
2502 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "AJI - Asterisk Jabber Interface",
2503 .load
= load_module
,
2504 .unload
= unload_module
,