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");
283 ast_copy_string(list
->node
, node
, sizeof(list
->node
));
284 ast_copy_string(res
->version
, version
, sizeof(res
->version
));
288 list
->versions
= res
;
289 list
->next
= capabilities
;
295 static struct aji_resource
*aji_find_resource(struct aji_buddy
*buddy
, char *name
)
297 struct aji_resource
*res
= NULL
;
300 res
= buddy
->resources
;
302 if (!strcasecmp(res
->resource
, name
)) {
310 static int gtalk_yuck(iks
*node
)
312 if (iks_find_with_attrib(node
, "c", "node", "http://www.google.com/xmpp/client/caps"))
318 * \brief Detects the highest bit in a number.
319 * \param Number you want to have evaluated.
320 * \return the highest power of 2 that can go into the number.
322 static int aji_highest_bit(int number
)
324 int x
= sizeof(number
) * 8 - 1;
328 if (number
& (1 << x
))
334 static iks
*jabber_make_auth(iksid
* id
, const char *pass
, const char *sid
)
338 iks_insert_attrib(x
, "type", "set");
339 y
= iks_insert(x
, "query");
340 iks_insert_attrib(y
, "xmlns", IKS_NS_AUTH
);
341 iks_insert_cdata(iks_insert(y
, "username"), id
->user
, 0);
342 iks_insert_cdata(iks_insert(y
, "resource"), id
->resource
, 0);
346 snprintf(sidpass
, sizeof(sidpass
), "%s%s", sid
, pass
);
347 ast_sha1_hash(buf
, sidpass
);
348 iks_insert_cdata(iks_insert(y
, "digest"), buf
, 0);
350 iks_insert_cdata(iks_insert(y
, "password"), pass
, 0);
356 * \brief Dial plan function status(). puts the status of watched user
357 into a channel variable.
358 * \param channel, and username,watched user, status var
361 static int aji_status_exec(struct ast_channel
*chan
, void *data
)
363 struct aji_client
*client
= NULL
;
364 struct aji_buddy
*buddy
= NULL
;
365 struct aji_resource
*r
= NULL
;
366 char *s
= NULL
, *sender
= NULL
, *jid
= NULL
, *screenname
= NULL
, *resource
= NULL
, *variable
= NULL
;
371 ast_log(LOG_ERROR
, "This application requires arguments.\n");
374 s
= ast_strdupa(data
);
376 sender
= strsep(&s
, "|");
377 if (sender
&& (sender
[0] != '\0')) {
378 jid
= strsep(&s
, "|");
379 if (jid
&& (jid
[0] != '\0')) {
382 ast_log(LOG_ERROR
, "Bad arguments\n");
388 if(!strchr(jid
, '/')) {
391 screenname
= strsep(&jid
, "/");
394 client
= ast_aji_get_client(sender
);
396 ast_log(LOG_WARNING
, "Could not find sender connection: %s\n", sender
);
399 if(!&client
->buddies
) {
400 ast_log(LOG_WARNING
, "No buddies for connection : %s\n", sender
);
403 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, resource
? screenname
: jid
);
405 ast_log(LOG_WARNING
, "Could not find buddy in list : %s\n", resource
? screenname
: jid
);
408 r
= aji_find_resource(buddy
, resource
);
409 if(!r
&& buddy
->resources
)
410 r
= buddy
->resources
;
412 ast_log(LOG_NOTICE
, "Resource %s of buddy %s not found \n", resource
, screenname
);
415 sprintf(status
, "%d", stat
);
416 pbx_builtin_setvar_helper(chan
, variable
, status
);
421 * \brief Dial plan function to send a message.
422 * \param channel, and data, data is sender, reciever, message.
425 static int aji_send_exec(struct ast_channel
*chan
, void *data
)
427 struct aji_client
*client
= NULL
;
429 char *s
= NULL
, *sender
= NULL
, *recipient
= NULL
, *message
= NULL
;
432 ast_log(LOG_ERROR
, "This application requires arguments.\n");
435 s
= ast_strdupa(data
);
437 sender
= strsep(&s
, "|");
438 if (sender
&& (sender
[0] != '\0')) {
439 recipient
= strsep(&s
, "|");
440 if (recipient
&& (recipient
[0] != '\0')) {
443 ast_log(LOG_ERROR
, "Bad arguments: %s\n", (char *) data
);
448 if (!(client
= ast_aji_get_client(sender
))) {
449 ast_log(LOG_WARNING
, "Could not find sender connection: %s\n", sender
);
452 if (strchr(recipient
, '@') && message
)
453 ast_aji_send(client
, recipient
, message
);
458 * \brief the debug loop.
459 * \param aji_client structure, xml data as string, size of string, direction of packet, 1 for inbound 0 for outbound.
461 static void aji_log_hook(void *data
, const char *xmpp
, size_t size
, int is_incoming
)
463 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
464 manager_event(EVENT_FLAG_USER
, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client
->name
, xmpp
);
468 ast_verbose("\nJABBER: %s INCOMING: %s\n", client
->name
, xmpp
);
470 if( strlen(xmpp
) == 1) {
471 if(option_debug
> 2 && xmpp
[0] == ' ')
472 ast_verbose("\nJABBER: Keep alive packet\n");
474 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client
->name
, xmpp
);
478 ASTOBJ_UNREF(client
, aji_client_destroy
);
482 * \brief The action hook parses the inbound packets, constantly running.
483 * \param data aji client structure
484 * \param type type of packet
485 * \param node the actual packet.
486 * \return IKS_OK or IKS_HOOK .
488 static int aji_act_hook(void *data
, int type
, iks
*node
)
490 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
495 ast_log(LOG_ERROR
, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
496 ASTOBJ_UNREF(client
, aji_client_destroy
);
500 if (client
->state
== AJI_DISCONNECTING
) {
501 ASTOBJ_UNREF(client
, aji_client_destroy
);
505 pak
= iks_packet(node
);
507 if (!client
->component
) { /*client */
510 if (client
->usetls
&& !iks_is_secure(client
->p
)) {
512 iks_start_tls(client
->p
);
513 tls_initialized
= TRUE
;
515 ast_log(LOG_ERROR
, "gnuTLS not installed. You need to recompile the Iksemel library with gnuTLS support\n");
518 if (!client
->usesasl
) {
519 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
);
520 auth
= jabber_make_auth(client
->jid
, client
->password
, iks_find_attrib(node
, "id"));
522 iks_insert_attrib(auth
, "id", client
->mid
);
523 iks_insert_attrib(auth
, "to", client
->jid
->server
);
524 ast_aji_increment_mid(client
->mid
);
525 iks_send(client
->p
, auth
);
528 ast_log(LOG_ERROR
, "Out of memory.\n");
532 case IKS_NODE_NORMAL
:
533 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
);
586 /* exclude the NULL training byte from the base64 encoding operation
587 as some XMPP servers will refuse it.
588 The format for authentication is [authzid]\0authcid\0password
589 not [authzid]\0authcid\0password\0 */
590 ast_base64encode(base64
, (const unsigned char *) s
, len
- 1, len
* 2);
591 iks_insert_cdata(x
, base64
, 0);
592 iks_send(client
->p
, x
);
599 ast_log(LOG_ERROR
, "Out of memory.\n");
605 } else if (!strcmp("failure", iks_name(node
))) {
606 ast_log(LOG_ERROR
, "JABBER: encryption failure. possible bad password.\n");
607 } else if (!strcmp("success", iks_name(node
))) {
608 client
->authorized
= 1;
609 iks_send_header(client
->p
, client
->jid
->server
);
613 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
614 ASTOBJ_UNREF(client
, aji_client_destroy
);
618 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
619 ASTOBJ_UNREF(client
, aji_client_destroy
);
623 } else if (client
->state
!= AJI_CONNECTED
&& client
->component
) {
626 if (client
->state
== AJI_DISCONNECTED
) {
627 char secret
[160], shasum
[320], *handshake
;
629 sprintf(secret
, "%s%s", pak
->id
, client
->password
);
630 ast_sha1_hash(shasum
, secret
);
632 asprintf(&handshake
, "<handshake>%s</handshake>", shasum
);
634 iks_send_raw(client
->p
, handshake
);
638 client
->state
= AJI_CONNECTING
;
639 if(iks_recv(client
->p
,1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
640 client
->state
= AJI_CONNECTED
;
642 ast_log(LOG_WARNING
,"Jabber didn't seem to handshake, failed to authenicate.\n");
647 case IKS_NODE_NORMAL
:
651 ast_log(LOG_ERROR
, "JABBER: Node Error\n");
652 ASTOBJ_UNREF(client
, aji_client_destroy
);
656 ast_log(LOG_WARNING
, "JABBER: Disconnected\n");
657 ASTOBJ_UNREF(client
, aji_client_destroy
);
665 ast_log(LOG_DEBUG
, "JABBER: I Don't know what to do with you NONE\n");
667 case IKS_PAK_MESSAGE
:
668 aji_handle_message(client
, pak
);
670 ast_log(LOG_DEBUG
, "JABBER: I Don't know what to do with you MESSAGE\n");
672 case IKS_PAK_PRESENCE
:
673 aji_handle_presence(client
, pak
);
675 ast_log(LOG_DEBUG
, "JABBER: I Do know how to handle presence!!\n");
678 aji_handle_subscribe(client
, pak
);
680 ast_log(LOG_DEBUG
, "JABBER: I Dont know S10N subscribe!!\n");
684 ast_log(LOG_DEBUG
, "JABBER: I Dont have an IQ!!!\n");
685 aji_handle_iq(client
, node
);
689 ast_log(LOG_DEBUG
, "JABBER: I Dont know %i\n", pak
->type
);
693 iks_filter_packet(client
->f
, pak
);
698 ASTOBJ_UNREF(client
, aji_client_destroy
);
702 static int aji_register_approve_handler(void *data
, ikspak
*pak
)
704 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
705 iks
*iq
= NULL
, *presence
= NULL
, *x
= NULL
;
708 presence
= iks_new("presence");
710 if (client
&& iq
&& presence
&& x
) {
711 if (!iks_find(pak
->query
, "remove")) {
712 iks_insert_attrib(iq
, "from", client
->jid
->full
);
713 iks_insert_attrib(iq
, "to", pak
->from
->full
);
714 iks_insert_attrib(iq
, "id", pak
->id
);
715 iks_insert_attrib(iq
, "type", "result");
716 iks_send(client
->p
, iq
);
718 iks_insert_attrib(presence
, "from", client
->jid
->full
);
719 iks_insert_attrib(presence
, "to", pak
->from
->partial
);
720 iks_insert_attrib(presence
, "id", client
->mid
);
721 ast_aji_increment_mid(client
->mid
);
722 iks_insert_attrib(presence
, "type", "subscribe");
723 iks_insert_attrib(x
, "xmlns", "vcard-temp:x:update");
724 iks_insert_node(presence
, x
);
725 iks_send(client
->p
, presence
);
728 ast_log(LOG_ERROR
, "Out of memory.\n");
734 iks_delete(presence
);
737 ASTOBJ_UNREF(client
, aji_client_destroy
);
738 return IKS_FILTER_EAT
;
741 static int aji_register_query_handler(void *data
, ikspak
*pak
)
743 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
744 struct aji_buddy
*buddy
= NULL
;
747 client
= (struct aji_client
*) data
;
749 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
751 iks
*iq
= NULL
, *query
= NULL
, *error
= NULL
, *notacceptable
= NULL
;
752 ast_verbose("Someone.... %s tried to register but they aren't allowed\n", pak
->from
->partial
);
754 query
= iks_new("query");
755 error
= iks_new("error");
756 notacceptable
= iks_new("not-acceptable");
757 if(iq
&& query
&& error
&& notacceptable
) {
758 iks_insert_attrib(iq
, "type", "error");
759 iks_insert_attrib(iq
, "from", client
->user
);
760 iks_insert_attrib(iq
, "to", pak
->from
->full
);
761 iks_insert_attrib(iq
, "id", pak
->id
);
762 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
763 iks_insert_attrib(error
, "code" , "406");
764 iks_insert_attrib(error
, "type", "modify");
765 iks_insert_attrib(notacceptable
, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
766 iks_insert_node(iq
, query
);
767 iks_insert_node(iq
, error
);
768 iks_insert_node(error
, notacceptable
);
769 iks_send(client
->p
, iq
);
771 ast_log(LOG_ERROR
, "Out of memory.\n");
780 iks_delete(notacceptable
);
781 } else if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
782 iks
*iq
= NULL
, *query
= NULL
, *instructions
= NULL
;
783 char *explain
= "Welcome to Asterisk - the Open Source PBX.\n";
785 query
= iks_new("query");
786 instructions
= iks_new("instructions");
787 if (iq
&& query
&& instructions
&& client
) {
788 iks_insert_attrib(iq
, "from", client
->user
);
789 iks_insert_attrib(iq
, "to", pak
->from
->full
);
790 iks_insert_attrib(iq
, "id", pak
->id
);
791 iks_insert_attrib(iq
, "type", "result");
792 iks_insert_attrib(query
, "xmlns", "jabber:iq:register");
793 iks_insert_cdata(instructions
, explain
, 0);
794 iks_insert_node(iq
, query
);
795 iks_insert_node(query
, instructions
);
796 iks_send(client
->p
, iq
);
798 ast_log(LOG_ERROR
, "Out of memory.\n");
805 iks_delete(instructions
);
807 ASTOBJ_UNREF(client
, aji_client_destroy
);
808 return IKS_FILTER_EAT
;
811 static int aji_ditems_handler(void *data
, ikspak
*pak
)
813 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
816 if (!(node
= iks_find_attrib(pak
->query
, "node"))) {
817 iks
*iq
= NULL
, *query
= NULL
, *item
= NULL
;
819 query
= iks_new("query");
820 item
= iks_new("item");
822 if (iq
&& query
&& item
) {
823 iks_insert_attrib(iq
, "from", client
->user
);
824 iks_insert_attrib(iq
, "to", pak
->from
->full
);
825 iks_insert_attrib(iq
, "id", pak
->id
);
826 iks_insert_attrib(iq
, "type", "result");
827 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
828 iks_insert_attrib(item
, "node", "http://jabber.org/protocol/commands");
829 iks_insert_attrib(item
, "name", "Million Dollar Asterisk Commands");
830 iks_insert_attrib(item
, "jid", client
->user
);
832 iks_insert_node(iq
, query
);
833 iks_insert_node(query
, item
);
834 iks_send(client
->p
, iq
);
836 ast_log(LOG_ERROR
, "Out of memory.\n");
845 } else if (!strcasecmp(node
, "http://jabber.org/protocol/commands")) {
846 iks
*iq
, *query
, *confirm
;
848 query
= iks_new("query");
849 confirm
= iks_new("item");
850 if (iq
&& query
&& confirm
&& client
) {
851 iks_insert_attrib(iq
, "from", client
->user
);
852 iks_insert_attrib(iq
, "to", pak
->from
->full
);
853 iks_insert_attrib(iq
, "id", pak
->id
);
854 iks_insert_attrib(iq
, "type", "result");
855 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
856 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
857 iks_insert_attrib(confirm
, "node", "confirmaccount");
858 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
859 iks_insert_attrib(confirm
, "jid", "blog.astjab.org");
861 iks_insert_node(iq
, query
);
862 iks_insert_node(query
, confirm
);
863 iks_send(client
->p
, iq
);
865 ast_log(LOG_ERROR
, "Out of memory.\n");
874 } else if (!strcasecmp(node
, "confirmaccount")) {
875 iks
*iq
= NULL
, *query
= NULL
, *feature
= NULL
;
878 query
= iks_new("query");
879 feature
= iks_new("feature");
881 if (iq
&& query
&& feature
&& client
) {
882 iks_insert_attrib(iq
, "from", client
->user
);
883 iks_insert_attrib(iq
, "to", pak
->from
->full
);
884 iks_insert_attrib(iq
, "id", pak
->id
);
885 iks_insert_attrib(iq
, "type", "result");
886 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
887 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
888 iks_insert_node(iq
, query
);
889 iks_insert_node(query
, feature
);
890 iks_send(client
->p
, iq
);
892 ast_log(LOG_ERROR
, "Out of memory.\n");
902 ASTOBJ_UNREF(client
, aji_client_destroy
);
903 return IKS_FILTER_EAT
;
907 static int aji_client_info_handler(void *data
, ikspak
*pak
)
909 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
910 struct aji_resource
*resource
= NULL
;
911 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
913 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
914 if (pak
->subtype
== IKS_TYPE_RESULT
) {
916 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
917 ASTOBJ_UNREF(client
, aji_client_destroy
);
918 return IKS_FILTER_EAT
;
920 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
921 resource
->cap
->jingle
= 1;
923 resource
->cap
->jingle
= 0;
924 } else if (pak
->subtype
== IKS_TYPE_GET
) {
925 iks
*iq
, *disco
, *ident
, *google
, *query
;
927 query
= iks_new("query");
928 ident
= iks_new("identity");
929 disco
= iks_new("feature");
930 google
= iks_new("feature");
931 if (iq
&& ident
&& disco
&& google
) {
932 iks_insert_attrib(iq
, "from", client
->jid
->full
);
933 iks_insert_attrib(iq
, "to", pak
->from
->full
);
934 iks_insert_attrib(iq
, "type", "result");
935 iks_insert_attrib(iq
, "id", pak
->id
);
936 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
937 iks_insert_attrib(ident
, "category", "client");
938 iks_insert_attrib(ident
, "type", "pc");
939 iks_insert_attrib(ident
, "name", "asterisk");
940 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco#info");
941 iks_insert_attrib(google
, "var", "http://www.google.com/xmpp/protocol/voice/v1");
942 iks_insert_node(iq
, query
);
943 iks_insert_node(query
, ident
);
944 iks_insert_node(query
, google
);
945 iks_insert_node(query
, disco
);
946 iks_send(client
->p
, iq
);
948 ast_log(LOG_ERROR
, "Out of Memory.\n");
959 } else if (pak
->subtype
== IKS_TYPE_ERROR
) {
960 ast_log(LOG_NOTICE
, "User %s does not support discovery.\n", pak
->from
->full
);
962 ASTOBJ_UNREF(client
, aji_client_destroy
);
963 return IKS_FILTER_EAT
;
966 static int aji_dinfo_handler(void *data
, ikspak
*pak
)
968 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
970 struct aji_resource
*resource
= NULL
;
971 struct aji_buddy
*buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
973 resource
= aji_find_resource(buddy
, pak
->from
->resource
);
974 if (pak
->subtype
== IKS_TYPE_ERROR
) {
975 ast_log(LOG_WARNING
, "Recieved error from a client, turn on jabber debug!\n");
976 return IKS_FILTER_EAT
;
978 if (pak
->subtype
== IKS_TYPE_RESULT
) {
980 ast_log(LOG_NOTICE
,"JABBER: Received client info from %s when not requested.\n", pak
->from
->full
);
981 ASTOBJ_UNREF(client
, aji_client_destroy
);
982 return IKS_FILTER_EAT
;
984 if (iks_find_with_attrib(pak
->query
, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
985 resource
->cap
->jingle
= 1;
987 resource
->cap
->jingle
= 0;
988 } else if (pak
->subtype
== IKS_TYPE_GET
&& !(node
= iks_find_attrib(pak
->query
, "node"))) {
989 iks
*iq
, *query
, *identity
, *disco
, *reg
, *commands
, *gateway
, *version
, *vcard
, *search
;
992 query
= iks_new("query");
993 identity
= iks_new("identity");
994 disco
= iks_new("feature");
995 reg
= iks_new("feature");
996 commands
= iks_new("feature");
997 gateway
= iks_new("feature");
998 version
= iks_new("feature");
999 vcard
= iks_new("feature");
1000 search
= iks_new("feature");
1002 if (iq
&& query
&& identity
&& disco
&& reg
&& commands
&& gateway
&& version
&& vcard
&& search
&& client
) {
1003 iks_insert_attrib(iq
, "from", client
->user
);
1004 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1005 iks_insert_attrib(iq
, "id", pak
->id
);
1006 iks_insert_attrib(iq
, "type", "result");
1007 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1008 iks_insert_attrib(identity
, "category", "gateway");
1009 iks_insert_attrib(identity
, "type", "pstn");
1010 iks_insert_attrib(identity
, "name", "Asterisk The Open Source PBX");
1011 iks_insert_attrib(disco
, "var", "http://jabber.org/protocol/disco");
1012 iks_insert_attrib(reg
, "var", "jabber:iq:register");
1013 iks_insert_attrib(commands
, "var", "http://jabber.org/protocol/commands");
1014 iks_insert_attrib(gateway
, "var", "jabber:iq:gateway");
1015 iks_insert_attrib(version
, "var", "jabber:iq:version");
1016 iks_insert_attrib(vcard
, "var", "vcard-temp");
1017 iks_insert_attrib(search
, "var", "jabber:iq:search");
1019 iks_insert_node(iq
, query
);
1020 iks_insert_node(query
, identity
);
1021 iks_insert_node(query
, disco
);
1022 iks_insert_node(query
, reg
);
1023 iks_insert_node(query
, commands
);
1024 iks_insert_node(query
, gateway
);
1025 iks_insert_node(query
, version
);
1026 iks_insert_node(query
, vcard
);
1027 iks_insert_node(query
, search
);
1028 iks_send(client
->p
, iq
);
1030 ast_log(LOG_ERROR
, "Out of memory.\n");
1038 iks_delete(identity
);
1044 iks_delete(commands
);
1046 iks_delete(gateway
);
1048 iks_delete(version
);
1054 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "http://jabber.org/protocol/commands")) {
1055 iks
*iq
, *query
, *confirm
;
1057 query
= iks_new("query");
1058 confirm
= iks_new("item");
1060 if (iq
&& query
&& confirm
&& client
) {
1061 iks_insert_attrib(iq
, "from", client
->user
);
1062 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1063 iks_insert_attrib(iq
, "id", pak
->id
);
1064 iks_insert_attrib(iq
, "type", "result");
1065 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#items");
1066 iks_insert_attrib(query
, "node", "http://jabber.org/protocol/commands");
1067 iks_insert_attrib(confirm
, "node", "confirmaccount");
1068 iks_insert_attrib(confirm
, "name", "Confirm AIM account");
1069 iks_insert_attrib(confirm
, "jid", client
->user
);
1070 iks_insert_node(iq
, query
);
1071 iks_insert_node(query
, confirm
);
1072 iks_send(client
->p
, iq
);
1074 ast_log(LOG_ERROR
, "Out of memory.\n");
1081 iks_delete(confirm
);
1083 } else if (pak
->subtype
== IKS_TYPE_GET
&& !strcasecmp(node
, "confirmaccount")) {
1084 iks
*iq
, *query
, *feature
;
1087 query
= iks_new("query");
1088 feature
= iks_new("feature");
1090 if (iq
&& query
&& feature
&& client
) {
1091 iks_insert_attrib(iq
, "from", client
->user
);
1092 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1093 iks_insert_attrib(iq
, "id", pak
->id
);
1094 iks_insert_attrib(iq
, "type", "result");
1095 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1096 iks_insert_attrib(feature
, "var", "http://jabber.org/protocol/commands");
1097 iks_insert_node(iq
, query
);
1098 iks_insert_node(query
, feature
);
1099 iks_send(client
->p
, iq
);
1101 ast_log(LOG_ERROR
, "Out of memory.\n");
1108 iks_delete(feature
);
1111 ASTOBJ_UNREF(client
, aji_client_destroy
);
1112 return IKS_FILTER_EAT
;
1116 * \brief Handles <iq> tags.
1117 * \param client structure and the iq node.
1120 static void aji_handle_iq(struct aji_client
*client
, iks
*node
)
1122 /*Nothing to see here */
1126 * \brief Handles presence packets.
1127 * \param client structure and the node.
1130 static void aji_handle_message(struct aji_client
*client
, ikspak
*pak
)
1132 struct aji_message
*insert
, *tmp
;
1135 if (!(insert
= ast_calloc(1, sizeof(struct aji_message
))))
1137 time(&insert
->arrived
);
1138 if (iks_find_cdata(pak
->x
, "body"))
1139 insert
->message
= ast_strdup(iks_find_cdata(pak
->x
, "body"));
1141 ast_copy_string(insert
->id
, pak
->id
, sizeof(insert
->message
));
1143 insert
->from
= ast_strdup(pak
->from
->full
);
1144 AST_LIST_LOCK(&client
->messages
);
1145 AST_LIST_TRAVERSE_SAFE_BEGIN(&client
->messages
, tmp
, list
) {
1147 AST_LIST_REMOVE_CURRENT(&client
->messages
, list
);
1152 } else if (difftime(time(NULL
), tmp
->arrived
) >= client
->message_timeout
) {
1154 AST_LIST_REMOVE_CURRENT(&client
->messages
, list
);
1161 AST_LIST_TRAVERSE_SAFE_END
;
1162 AST_LIST_INSERT_HEAD(&client
->messages
, insert
, list
);
1163 AST_LIST_UNLOCK(&client
->messages
);
1166 static void aji_handle_presence(struct aji_client
*client
, ikspak
*pak
)
1168 int status
, priority
;
1169 struct aji_buddy
*buddy
;
1170 struct aji_resource
*tmp
= NULL
, *last
= NULL
, *found
= NULL
;
1171 char *ver
, *node
, *descrip
, *type
;
1173 if(client
->state
!= AJI_CONNECTED
)
1174 aji_create_buddy(pak
->from
->partial
, client
);
1176 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1177 if (!buddy
&& pak
->from
->partial
) {
1178 /* allow our jid to be used to log in with another resource */
1179 if (!strcmp((const char *)pak
->from
->partial
, (const char *)client
->jid
->partial
))
1180 aji_create_buddy(pak
->from
->partial
, client
);
1182 ast_log(LOG_NOTICE
, "Got presence packet from %s, someone not in our roster!!!!\n", pak
->from
->partial
);
1185 type
= iks_find_attrib(pak
->x
, "type");
1186 if(client
->component
&& type
&&!strcasecmp("probe", type
)) {
1187 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), 1, client
->statusmessage
);
1188 ast_verbose("what i was looking for \n");
1190 ASTOBJ_WRLOCK(buddy
);
1191 status
= (pak
->show
) ? pak
->show
: 6;
1192 priority
= atoi((iks_find_cdata(pak
->x
, "priority")) ? iks_find_cdata(pak
->x
, "priority") : "0");
1193 tmp
= buddy
->resources
;
1194 descrip
= ast_strdup(iks_find_cdata(pak
->x
,"status"));
1196 while (tmp
&& pak
->from
->resource
) {
1197 if (!strcasecmp(tmp
->resource
, pak
->from
->resource
)) {
1198 tmp
->status
= status
;
1199 if (tmp
->description
) free(tmp
->description
);
1200 tmp
->description
= descrip
;
1202 if (status
== 6) { /* Sign off Destroy resource */
1203 if (last
&& found
->next
) {
1204 last
->next
= found
->next
;
1207 buddy
->resources
= found
->next
;
1209 buddy
->resources
= NULL
;
1210 } else if (!found
->next
) {
1214 buddy
->resources
= NULL
;
1220 /* resource list is sorted by descending priority */
1221 if (tmp
->priority
!= priority
) {
1222 found
->priority
= priority
;
1223 if (!last
&& !found
->next
)
1224 /* resource was found to be unique,
1227 /* search for resource in our list
1228 and take it out for the moment */
1230 last
->next
= found
->next
;
1232 buddy
->resources
= found
->next
;
1235 tmp
= buddy
->resources
;
1236 if (!buddy
->resources
)
1237 buddy
->resources
= found
;
1238 /* priority processing */
1240 /* insert resource back according to
1241 its priority value */
1242 if (found
->priority
> tmp
->priority
) {
1244 /* insert within list */
1249 buddy
->resources
= found
;
1253 /* insert at the end of the list */
1268 /* resource not found in our list, create it */
1269 if (!found
&& status
!= 6 && pak
->from
->resource
) {
1270 found
= (struct aji_resource
*) malloc(sizeof(struct aji_resource
));
1271 memset(found
, 0, sizeof(struct aji_resource
));
1274 ast_log(LOG_ERROR
, "Out of memory!\n");
1277 ast_copy_string(found
->resource
, pak
->from
->resource
, sizeof(found
->resource
));
1278 found
->status
= status
;
1279 found
->description
= descrip
;
1280 found
->priority
= priority
;
1283 tmp
= buddy
->resources
;
1285 if (found
->priority
> tmp
->priority
) {
1290 buddy
->resources
= found
;
1301 buddy
->resources
= found
;
1304 ASTOBJ_UNLOCK(buddy
);
1305 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
1307 node
= iks_find_attrib(iks_find(pak
->x
, "c"), "node");
1308 ver
= iks_find_attrib(iks_find(pak
->x
, "c"), "ver");
1310 /* handle gmail client's special caps:c tag */
1311 if (!node
&& !ver
) {
1312 node
= iks_find_attrib(iks_find(pak
->x
, "caps:c"), "node");
1313 ver
= iks_find_attrib(iks_find(pak
->x
, "caps:c"), "ver");
1316 /* retrieve capabilites of the new resource */
1317 if(status
!=6 && found
&& !found
->cap
) {
1318 found
->cap
= aji_find_version(node
, ver
, pak
);
1319 if(gtalk_yuck(pak
->x
)) /* gtalk should do discover */
1320 found
->cap
->jingle
= 1;
1321 if(found
->cap
->jingle
&& option_debug
> 4)
1322 ast_log(LOG_DEBUG
,"Special case for google till they support discover.\n");
1326 query
= iks_new("query");
1328 iks_insert_attrib(iq
, "type", "get");
1329 iks_insert_attrib(iq
, "to", pak
->from
->full
);
1330 iks_insert_attrib(iq
,"from", client
->jid
->full
);
1331 iks_insert_attrib(iq
, "id", client
->mid
);
1332 ast_aji_increment_mid(client
->mid
);
1333 iks_insert_attrib(query
, "xmlns", "http://jabber.org/protocol/disco#info");
1334 iks_insert_node(iq
, query
);
1335 iks_send(client
->p
, iq
);
1338 ast_log(LOG_ERROR
, "Out of memory.\n");
1345 if (option_verbose
> 4) {
1346 switch (pak
->subtype
) {
1347 case IKS_TYPE_AVAILABLE
:
1348 ast_verbose(VERBOSE_PREFIX_3
"JABBER: I am available ^_* %i\n", pak
->subtype
);
1350 case IKS_TYPE_UNAVAILABLE
:
1351 ast_verbose(VERBOSE_PREFIX_3
"JABBER: I am unavailable ^_* %i\n", pak
->subtype
);
1354 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Ohh sexy and the wrong type: %i\n", pak
->subtype
);
1356 switch (pak
->show
) {
1357 case IKS_SHOW_UNAVAILABLE
:
1358 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1360 case IKS_SHOW_AVAILABLE
:
1361 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type is available\n");
1364 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1367 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type is away\n");
1370 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1373 ast_verbose(VERBOSE_PREFIX_3
"JABBER: type: %i subtype %i\n", pak
->subtype
, pak
->show
);
1376 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Kinky! how did that happen %i\n", pak
->show
);
1382 * \brief handles subscription requests.
1383 * \param aji_client struct and xml packet.
1386 static void aji_handle_subscribe(struct aji_client
*client
, ikspak
*pak
)
1388 iks
*presence
= NULL
, *status
= NULL
;
1389 struct aji_buddy
* buddy
= NULL
;
1391 switch (pak
->subtype
) {
1392 case IKS_TYPE_SUBSCRIBE
:
1393 presence
= iks_new("presence");
1394 status
= iks_new("status");
1395 if(presence
&& status
) {
1396 iks_insert_attrib(presence
, "type", "subscribed");
1397 iks_insert_attrib(presence
, "to", pak
->from
->full
);
1398 iks_insert_attrib(presence
, "from", client
->jid
->full
);
1400 iks_insert_attrib(presence
, "id", pak
->id
);
1401 iks_insert_cdata(status
, "Asterisk has approved subscription", 0);
1402 iks_insert_node(presence
, status
);
1403 iks_send(client
->p
, presence
);
1405 ast_log(LOG_ERROR
, "Unable to allocate nodes\n");
1407 iks_delete(presence
);
1410 if(client
->component
)
1411 aji_set_presence(client
, pak
->from
->full
, iks_find_attrib(pak
->x
, "to"), 1, client
->statusmessage
);
1412 case IKS_TYPE_SUBSCRIBED
:
1413 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
, pak
->from
->partial
);
1414 if (!buddy
&& pak
->from
->partial
) {
1415 aji_create_buddy(pak
->from
->partial
, client
);
1418 if (option_verbose
> 4) {
1419 ast_verbose(VERBOSE_PREFIX_3
"JABBER: This is a subcription of type %i\n", pak
->subtype
);
1425 * \brief sends messages.
1426 * \param aji_client struct , reciever, message.
1429 int ast_aji_send(struct aji_client
*client
, const char *address
, const char *message
)
1432 iks
*message_packet
= NULL
;
1433 if (client
->state
== AJI_CONNECTED
) {
1434 message_packet
= iks_make_msg(IKS_TYPE_CHAT
, address
, message
);
1435 if (message_packet
) {
1436 iks_insert_attrib(message_packet
, "from", client
->jid
->full
);
1437 res
= iks_send(client
->p
, message_packet
);
1439 ast_log(LOG_ERROR
, "Out of memory.\n");
1442 iks_delete(message_packet
);
1444 ast_log(LOG_WARNING
, "JABBER: Not connected can't send\n");
1449 * \brief create a chatroom.
1450 * \param aji_client struct , room, server, topic for the room.
1453 int ast_aji_create_chat(struct aji_client
*client
, char *room
, char *server
, char *topic
)
1460 iks_insert_attrib(iq
, "type", "get");
1461 iks_insert_attrib(iq
, "to", server
);
1462 iks_insert_attrib(iq
, "id", client
->mid
);
1463 ast_aji_increment_mid(client
->mid
);
1464 iks_send(client
->p
, iq
);
1466 ast_log(LOG_ERROR
, "Out of memory.\n");
1474 * \brief join a chatroom.
1475 * \param aji_client struct , room.
1478 int ast_aji_join_chat(struct aji_client
*client
, char *room
)
1481 iks
*presence
= NULL
, *priority
= NULL
;
1482 presence
= iks_new("presence");
1483 priority
= iks_new("priority");
1484 if (presence
&& priority
&& client
) {
1485 iks_insert_cdata(priority
, "0", 1);
1486 iks_insert_attrib(presence
, "to", room
);
1487 iks_insert_node(presence
, priority
);
1488 res
= iks_send(client
->p
, presence
);
1489 iks_insert_cdata(priority
, "5", 1);
1490 iks_insert_attrib(presence
, "to", room
);
1491 res
= iks_send(client
->p
, presence
);
1493 ast_log(LOG_ERROR
, "Out of memory.\n");
1495 iks_delete(presence
);
1497 iks_delete(priority
);
1502 * \brief invite to a chatroom.
1503 * \param aji_client struct ,user, room, message.
1506 int ast_aji_invite_chat(struct aji_client
*client
, char *user
, char *room
, char *message
)
1509 iks
*invite
, *body
, *namespace;
1511 invite
= iks_new("message");
1512 body
= iks_new("body");
1513 namespace = iks_new("x");
1514 if (client
&& invite
&& body
&& namespace) {
1515 iks_insert_attrib(invite
, "to", user
);
1516 iks_insert_attrib(invite
, "id", client
->mid
);
1517 ast_aji_increment_mid(client
->mid
);
1518 iks_insert_cdata(body
, message
, 0);
1519 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
1520 iks_insert_attrib(namespace, "jid", room
);
1521 iks_insert_node(invite
, body
);
1522 iks_insert_node(invite
, namespace);
1523 res
= iks_send(client
->p
, invite
);
1525 ast_log(LOG_ERROR
, "Out of memory.\n");
1529 iks_delete(namespace);
1537 * \brief receive message loop.
1538 * \param aji_client struct.
1541 static void *aji_recv_loop(void *data
)
1543 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1546 if (res
!= IKS_OK
) {
1547 while(res
!= IKS_OK
) {
1548 if(option_verbose
> 3)
1549 ast_verbose("JABBER: reconnecting.\n");
1550 res
= aji_reconnect(client
);
1555 res
= iks_recv(client
->p
, 1);
1557 if (client
->state
== AJI_DISCONNECTING
) {
1558 if (option_debug
> 1)
1559 ast_log(LOG_DEBUG
, "Ending our Jabber client's thread due to a disconnect\n");
1563 if (res
== IKS_HOOK
)
1564 ast_log(LOG_WARNING
, "JABBER: Got hook event.\n");
1565 else if (res
== IKS_NET_TLSFAIL
)
1566 ast_log(LOG_WARNING
, "JABBER: Failure in TLS.\n");
1567 else if (client
->timeout
== 0 && client
->state
== AJI_CONNECTED
) {
1568 res
= iks_send_raw(client
->p
, " ");
1570 client
->timeout
= 50;
1572 ast_log(LOG_WARNING
, "JABBER: Network Timeout\n");
1573 } else if (res
== IKS_NET_RWERR
)
1574 ast_log(LOG_WARNING
, "JABBER: socket read error\n");
1576 ASTOBJ_UNREF(client
, aji_client_destroy
);
1581 * \brief increments the mid field for messages and other events.
1582 * \param message id.
1585 void ast_aji_increment_mid(char *mid
)
1589 for (i
= strlen(mid
) - 1; i
>= 0; i
--) {
1590 if (mid
[i
] != 'z') {
1591 mid
[i
] = mid
[i
] + 1;
1600 * \brief attempts to register to a transport.
1601 * \param aji_client struct, and xml packet.
1602 * \return IKS_FILTER_EAT.
1604 /*allows for registering to transport , was too sketch and is out for now. */
1605 /*static int aji_register_transport(void *data, ikspak *pak)
1607 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1609 struct aji_buddy *buddy = NULL;
1610 iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
1612 if (client && send) {
1613 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
1614 ASTOBJ_RDLOCK(iterator);
1615 if (iterator->btype == AJI_TRANS) {
1618 ASTOBJ_UNLOCK(iterator);
1620 iks_filter_remove_hook(client->f, aji_register_transport);
1621 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);
1622 iks_insert_attrib(send, "to", buddy->host);
1623 iks_insert_attrib(send, "id", client->mid);
1624 ast_aji_increment_mid(client->mid);
1625 iks_insert_attrib(send, "from", client->user);
1626 res = iks_send(client->p, send);
1628 ast_log(LOG_ERROR, "Out of memory.\n");
1632 ASTOBJ_UNREF(client, aji_client_destroy);
1633 return IKS_FILTER_EAT;
1638 * \brief attempts to register to a transport step 2.
1639 * \param aji_client struct, and xml packet.
1640 * \return IKS_FILTER_EAT.
1642 /* more of the same blob of code, too wonky for now*/
1643 /* static int aji_register_transport2(void *data, ikspak *pak)
1645 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1647 struct aji_buddy *buddy = NULL;
1649 iks *regiq = iks_new("iq");
1650 iks *regquery = iks_new("query");
1651 iks *reguser = iks_new("username");
1652 iks *regpass = iks_new("password");
1654 if (client && regquery && reguser && regpass && regiq) {
1655 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
1656 ASTOBJ_RDLOCK(iterator);
1657 if (iterator->btype == AJI_TRANS)
1658 buddy = iterator; ASTOBJ_UNLOCK(iterator);
1660 iks_filter_remove_hook(client->f, aji_register_transport2);
1661 iks_insert_attrib(regiq, "to", buddy->host);
1662 iks_insert_attrib(regiq, "type", "set");
1663 iks_insert_attrib(regiq, "id", client->mid);
1664 ast_aji_increment_mid(client->mid);
1665 iks_insert_attrib(regiq, "from", client->user);
1666 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
1667 iks_insert_cdata(reguser, buddy->user, 0);
1668 iks_insert_cdata(regpass, buddy->pass, 0);
1669 iks_insert_node(regiq, regquery);
1670 iks_insert_node(regquery, reguser);
1671 iks_insert_node(regquery, regpass);
1672 res = iks_send(client->p, regiq);
1674 ast_log(LOG_ERROR, "Out of memory.\n");
1678 iks_delete(regquery);
1680 iks_delete(reguser);
1682 iks_delete(regpass);
1683 ASTOBJ_UNREF(client, aji_client_destroy);
1684 return IKS_FILTER_EAT;
1688 * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
1689 * \param aji_client struct.
1692 static void aji_pruneregister(struct aji_client
*client
)
1695 iks
*removeiq
= iks_new("iq");
1696 iks
*removequery
= iks_new("query");
1697 iks
*removeitem
= iks_new("item");
1698 iks
*send
= iks_make_iq(IKS_TYPE_GET
, "http://jabber.org/protocol/disco#items");
1700 if (client
&& removeiq
&& removequery
&& removeitem
&& send
) {
1701 iks_insert_node(removeiq
, removequery
);
1702 iks_insert_node(removequery
, removeitem
);
1703 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1704 ASTOBJ_RDLOCK(iterator
);
1705 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
1706 * be called at the same time */
1707 if (ast_test_flag(iterator
, AJI_AUTOPRUNE
)) {
1708 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE
, iterator
->name
,
1709 "GoodBye your status is no longer needed by Asterisk the Open Source PBX"
1710 " so I am no longer subscribing to your presence.\n"));
1711 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED
, iterator
->name
,
1712 "GoodBye you are no longer in the asterisk config file so I am removing"
1713 " your access to my presence.\n"));
1714 iks_insert_attrib(removeiq
, "from", client
->jid
->full
);
1715 iks_insert_attrib(removeiq
, "type", "set");
1716 iks_insert_attrib(removequery
, "xmlns", "jabber:iq:roster");
1717 iks_insert_attrib(removeitem
, "jid", iterator
->name
);
1718 iks_insert_attrib(removeitem
, "subscription", "remove");
1719 res
= iks_send(client
->p
, removeiq
);
1720 } else if (ast_test_flag(iterator
, AJI_AUTOREGISTER
)) {
1721 res
= iks_send(client
->p
, iks_make_s10n(IKS_TYPE_SUBSCRIBE
, iterator
->name
,
1722 "Greetings I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
1723 ast_clear_flag(iterator
, AJI_AUTOREGISTER
);
1725 ASTOBJ_UNLOCK(iterator
);
1728 ast_log(LOG_ERROR
, "Out of memory.\n");
1730 iks_delete(removeiq
);
1732 iks_delete(removequery
);
1734 iks_delete(removeitem
);
1737 ASTOBJ_CONTAINER_PRUNE_MARKED(&client
->buddies
, aji_buddy_destroy
);
1741 * \brief filters the roster packet we get back from server.
1742 * \param aji_client struct, and xml packet.
1743 * \return IKS_FILTER_EAT.
1745 static int aji_filter_roster(void *data
, ikspak
*pak
)
1747 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1750 struct aji_buddy
*buddy
;
1752 client
->state
= AJI_CONNECTED
;
1753 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1754 ASTOBJ_RDLOCK(iterator
);
1755 x
= iks_child(pak
->query
);
1758 if (!iks_strcmp(iks_name(x
), "item")) {
1759 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid"))) {
1761 ast_clear_flag(iterator
, AJI_AUTOPRUNE
| AJI_AUTOREGISTER
);
1767 ast_copy_flags(iterator
, client
, AJI_AUTOREGISTER
);
1770 ASTOBJ_UNLOCK(iterator
);
1773 x
= iks_child(pak
->query
);
1776 if (iks_strcmp(iks_name(x
), "item") == 0) {
1777 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
1778 ASTOBJ_RDLOCK(iterator
);
1779 if (!strcasecmp(iterator
->name
, iks_find_attrib(x
, "jid")))
1781 ASTOBJ_UNLOCK(iterator
);
1785 buddy
= (struct aji_buddy
*) malloc(sizeof(struct aji_buddy
));
1787 ast_log(LOG_WARNING
, "Out of memory\n");
1790 memset(buddy
, 0, sizeof(struct aji_buddy
));
1792 ASTOBJ_WRLOCK(buddy
);
1793 ast_copy_string(buddy
->name
, iks_find_attrib(x
, "jid"), sizeof(buddy
->name
));
1794 ast_clear_flag(buddy
, AST_FLAGS_ALL
);
1795 if(ast_test_flag(client
, AJI_AUTOPRUNE
)) {
1796 ast_set_flag(buddy
, AJI_AUTOPRUNE
);
1797 buddy
->objflags
|= ASTOBJ_FLAG_MARKED
;
1799 ast_set_flag(buddy
, AJI_AUTOREGISTER
);
1800 ASTOBJ_UNLOCK(buddy
);
1802 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
1803 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
1811 aji_pruneregister(client
);
1813 ASTOBJ_UNREF(client
, aji_client_destroy
);
1814 return IKS_FILTER_EAT
;
1817 static int aji_reconnect(struct aji_client
*client
)
1822 client
->state
= AJI_DISCONNECTED
;
1825 iks_parser_reset(client
->p
);
1826 if (client
->authorized
)
1827 client
->authorized
= 0;
1829 if(client
->component
)
1830 res
= aji_component_initialize(client
);
1832 res
= aji_client_initialize(client
);
1837 static int aji_get_roster(struct aji_client
*client
)
1840 roster
= iks_make_iq(IKS_TYPE_GET
, IKS_NS_ROSTER
);
1842 iks_insert_attrib(roster
, "id", "roster");
1843 aji_set_presence(client
, NULL
, client
->jid
->full
, 1, client
->statusmessage
);
1844 iks_send(client
->p
, roster
);
1852 * \brief connects as a client to jabber server.
1853 * \param aji_client struct, and xml packet.
1856 static int aji_client_connect(void *data
, ikspak
*pak
)
1858 struct aji_client
*client
= ASTOBJ_REF((struct aji_client
*) data
);
1862 if (client
->state
== AJI_DISCONNECTED
) {
1863 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
);
1864 client
->state
= AJI_CONNECTING
;
1865 client
->jid
= (iks_find_cdata(pak
->query
, "jid")) ? iks_id_new(client
->stack
, iks_find_cdata(pak
->query
, "jid")) : client
->jid
;
1866 iks_filter_remove_hook(client
->f
, aji_client_connect
);
1867 if(!client
->component
) /*client*/
1868 aji_get_roster(client
);
1871 ast_log(LOG_ERROR
, "Out of memory.\n");
1873 ASTOBJ_UNREF(client
, aji_client_destroy
);
1878 * \brief prepares client for connect.
1879 * \param aji_client struct.
1882 static int aji_client_initialize(struct aji_client
*client
)
1886 connected
= iks_connect_via(client
->p
, S_OR(client
->serverhost
, client
->jid
->server
), client
->port
, client
->jid
->server
);
1888 if (connected
== IKS_NET_NOCONN
) {
1889 ast_log(LOG_ERROR
, "JABBER ERROR: No Connection\n");
1891 } else if (connected
== IKS_NET_NODNS
) {
1892 ast_log(LOG_ERROR
, "JABBER ERROR: No DNS %s for client to %s\n", client
->name
, S_OR(client
->serverhost
, client
->jid
->server
));
1895 iks_recv(client
->p
, 30);
1900 * \brief prepares component for connect.
1901 * \param aji_client struct.
1904 static int aji_component_initialize(struct aji_client
*client
)
1908 connected
= iks_connect_via(client
->p
, S_OR(client
->serverhost
, client
->jid
->server
), client
->port
, client
->user
);
1909 if (connected
== IKS_NET_NOCONN
) {
1910 ast_log(LOG_ERROR
, "JABBER ERROR: No Connection\n");
1912 } else if (connected
== IKS_NET_NODNS
) {
1913 ast_log(LOG_ERROR
, "JABBER ERROR: No DNS %s for client to %s\n", client
->name
, S_OR(client
->serverhost
, client
->jid
->server
));
1915 } else if (!connected
)
1916 iks_recv(client
->p
, 30);
1921 * \brief disconnect from jabber server.
1922 * \param aji_client struct.
1925 int ast_aji_disconnect(struct aji_client
*client
)
1928 if (option_verbose
> 3)
1929 ast_verbose(VERBOSE_PREFIX_3
"JABBER: Disconnecting\n");
1930 iks_disconnect(client
->p
);
1931 iks_parser_delete(client
->p
);
1932 ASTOBJ_UNREF(client
, aji_client_destroy
);
1939 * \brief set presence of client.
1940 * \param aji_client struct, user to send it to, and from, level, description.
1943 static void aji_set_presence(struct aji_client
*client
, char *to
, char *from
, int level
, char *desc
)
1946 iks
*presence
= iks_make_pres(level
, desc
);
1947 iks
*cnode
= iks_new("c");
1948 iks
*priority
= iks_new("priority");
1950 iks_insert_cdata(priority
, "0", 1);
1951 if (presence
&& cnode
&& client
) {
1953 iks_insert_attrib(presence
, "to", to
);
1955 iks_insert_attrib(presence
, "from", from
);
1956 iks_insert_attrib(cnode
, "node", "http://www.asterisk.org/xmpp/client/caps");
1957 iks_insert_attrib(cnode
, "ver", "asterisk-xmpp");
1958 iks_insert_attrib(cnode
, "ext", "voice-v1");
1959 iks_insert_attrib(cnode
, "xmlns", "http://jabber.org/protocol/caps");
1960 iks_insert_node(presence
, cnode
);
1961 res
= iks_send(client
->p
, presence
);
1963 ast_log(LOG_ERROR
, "Out of memory.\n");
1967 iks_delete(presence
);
1971 * \brief turnon console debugging.
1972 * \param fd, number of args, args.
1973 * \return RESULT_SUCCESS.
1975 static int aji_do_debug(int fd
, int argc
, char *argv
[])
1977 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
1978 ASTOBJ_RDLOCK(iterator
);
1979 iterator
->debug
= 1;
1980 ASTOBJ_UNLOCK(iterator
);
1982 ast_cli(fd
, "Jabber Debugging Enabled.\n");
1983 return RESULT_SUCCESS
;
1987 * \brief reload jabber module.
1988 * \param fd, number of args, args.
1989 * \return RESULT_SUCCESS.
1991 static int aji_do_reload(int fd
, int argc
, char *argv
[])
1994 ast_cli(fd
, "Jabber Reloaded.\n");
1995 return RESULT_SUCCESS
;
1999 * \brief turnoff console debugging.
2000 * \param fd, number of args, args.
2001 * \return RESULT_SUCCESS.
2003 static int aji_no_debug(int fd
, int argc
, char *argv
[])
2005 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2006 ASTOBJ_RDLOCK(iterator
);
2007 iterator
->debug
= 0;
2008 ASTOBJ_UNLOCK(iterator
);
2010 ast_cli(fd
, "Jabber Debugging Disabled.\n");
2011 return RESULT_SUCCESS
;
2015 * \brief show client status.
2016 * \param fd, number of args, args.
2017 * \return RESULT_SUCCESS.
2019 static int aji_show_clients(int fd
, int argc
, char *argv
[])
2023 ast_cli(fd
, "Jabber Users and their status:\n");
2024 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2025 ASTOBJ_RDLOCK(iterator
);
2027 switch (iterator
->state
) {
2028 case AJI_DISCONNECTED
:
2029 status
= "Disconnected";
2031 case AJI_CONNECTING
:
2032 status
= "Connecting";
2035 status
= "Connected";
2040 ast_cli(fd
, " User: %s - %s\n", iterator
->user
, status
);
2041 ASTOBJ_UNLOCK(iterator
);
2043 ast_cli(fd
, "----\n");
2044 ast_cli(fd
, " Number of users: %d\n", count
);
2045 return RESULT_SUCCESS
;
2049 * \brief send test message for debugging.
2050 * \param fd, number of args, args.
2051 * \return RESULT_SUCCESS.
2053 static int aji_test(int fd
, int argc
, char *argv
[])
2055 struct aji_client
*client
;
2056 struct aji_resource
*resource
;
2057 const char *name
= "asterisk";
2058 struct aji_message
*tmp
;
2061 return RESULT_SHOWUSAGE
;
2065 if (!(client
= ASTOBJ_CONTAINER_FIND(&clients
, name
))) {
2066 ast_cli(fd
, "Unable to find client '%s'!\n", name
);
2067 return RESULT_FAILURE
;
2070 /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
2071 ast_aji_send(client
, "mogorman@astjab.org", "blahblah");
2072 ASTOBJ_CONTAINER_TRAVERSE(&client
->buddies
, 1, {
2073 ASTOBJ_RDLOCK(iterator
);
2074 ast_verbose("User: %s\n", iterator
->name
);
2075 for (resource
= iterator
->resources
; resource
; resource
= resource
->next
) {
2076 ast_verbose("Resource: %s\n", resource
->resource
);
2078 ast_verbose(" client: %s\n", resource
->cap
->parent
->node
);
2079 ast_verbose(" version: %s\n", resource
->cap
->version
);
2080 ast_verbose(" Jingle Capable: %d\n", resource
->cap
->jingle
);
2082 ast_verbose(" Priority: %d\n", resource
->priority
);
2083 ast_verbose(" Status: %d\n", resource
->status
);
2084 ast_verbose(" Message: %s\n", S_OR(resource
->description
,""));
2086 ASTOBJ_UNLOCK(iterator
);
2088 ast_verbose("\nOooh a working message stack!\n");
2089 AST_LIST_LOCK(&client
->messages
);
2090 AST_LIST_TRAVERSE(&client
->messages
, tmp
, list
) {
2091 ast_verbose(" Message from: %s with id %s @ %s %s\n",tmp
->from
, S_OR(tmp
->id
,""), ctime(&tmp
->arrived
), S_OR(tmp
->message
, ""));
2093 AST_LIST_UNLOCK(&client
->messages
);
2094 ASTOBJ_UNREF(client
, aji_client_destroy
);
2096 return RESULT_SUCCESS
;
2100 * \brief creates aji_client structure.
2101 * \param label, ast_variable, debug, pruneregister, component/client, aji_client to dump into.
2104 static int aji_create_client(char *label
, struct ast_variable
*var
, int debug
)
2107 struct aji_client
*client
= NULL
;
2110 client
= ASTOBJ_CONTAINER_FIND(&clients
,label
);
2113 client
= (struct aji_client
*) malloc(sizeof(struct aji_client
));
2115 ast_log(LOG_ERROR
, "Out of memory!\n");
2118 memset(client
, 0, sizeof(struct aji_client
));
2119 ASTOBJ_INIT(client
);
2120 ASTOBJ_WRLOCK(client
);
2121 ASTOBJ_CONTAINER_INIT(&client
->buddies
);
2123 ASTOBJ_WRLOCK(client
);
2124 ASTOBJ_UNMARK(client
);
2126 ASTOBJ_CONTAINER_MARKALL(&client
->buddies
);
2127 ast_copy_string(client
->name
, label
, sizeof(client
->name
));
2128 ast_copy_string(client
->mid
, "aaaaa", sizeof(client
->mid
));
2130 /* Set default values for the client object */
2131 client
->debug
= debug
;
2132 ast_copy_flags(client
, &globalflags
, AST_FLAGS_ALL
);
2133 client
->port
= 5222;
2135 client
->usesasl
= 1;
2136 client
->forcessl
= 0;
2137 client
->keepalive
= 1;
2138 client
->timeout
= 50;
2139 client
->message_timeout
= 100;
2140 AST_LIST_HEAD_INIT(&client
->messages
);
2141 client
->component
= 0;
2142 ast_copy_string(client
->statusmessage
, "Online and Available", sizeof(client
->statusmessage
));
2145 client
->authorized
= 0;
2146 client
->state
= AJI_DISCONNECTED
;
2149 if (!strcasecmp(var
->name
, "username"))
2150 ast_copy_string(client
->user
, var
->value
, sizeof(client
->user
));
2151 else if (!strcasecmp(var
->name
, "serverhost"))
2152 ast_copy_string(client
->serverhost
, var
->value
, sizeof(client
->serverhost
));
2153 else if (!strcasecmp(var
->name
, "secret"))
2154 ast_copy_string(client
->password
, var
->value
, sizeof(client
->password
));
2155 else if (!strcasecmp(var
->name
, "statusmessage"))
2156 ast_copy_string(client
->statusmessage
, var
->value
, sizeof(client
->statusmessage
));
2157 else if (!strcasecmp(var
->name
, "port"))
2158 client
->port
= atoi(var
->value
);
2159 else if (!strcasecmp(var
->name
, "timeout"))
2160 client
->message_timeout
= atoi(var
->value
);
2161 else if (!strcasecmp(var
->name
, "debug"))
2162 client
->debug
= (ast_false(var
->value
)) ? 0 : 1;
2163 else if (!strcasecmp(var
->name
, "type")) {
2164 if (!strcasecmp(var
->value
, "component"))
2165 client
->component
= 1;
2166 } else if (!strcasecmp(var
->name
, "usetls")) {
2167 client
->usetls
= (ast_false(var
->value
)) ? 0 : 1;
2168 } else if (!strcasecmp(var
->name
, "usesasl")) {
2169 client
->usesasl
= (ast_false(var
->value
)) ? 0 : 1;
2170 } else if (!strcasecmp(var
->name
, "forceoldssl"))
2171 client
->forcessl
= (ast_false(var
->value
)) ? 0 : 1;
2172 else if (!strcasecmp(var
->name
, "keepalive"))
2173 client
->keepalive
= (ast_false(var
->value
)) ? 0 : 1;
2174 else if (!strcasecmp(var
->name
, "autoprune"))
2175 ast_set2_flag(client
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2176 else if (!strcasecmp(var
->name
, "autoregister"))
2177 ast_set2_flag(client
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2178 else if (!strcasecmp(var
->name
, "buddy"))
2179 aji_create_buddy(var
->value
, client
);
2180 /* no transport support in this version */
2181 /* else if (!strcasecmp(var->name, "transport"))
2182 aji_create_transport(var->value, client);
2187 ASTOBJ_UNLOCK(client
);
2188 ASTOBJ_UNREF(client
, aji_client_destroy
);
2191 client
->p
= iks_stream_new(((client
->component
) ? "jabber:component:accept" : "jabber:client"), client
, aji_act_hook
);
2193 ast_log(LOG_ERROR
, "Failed to create stream for client '%s'!\n", client
->name
);
2196 client
->stack
= iks_stack_new(8192, 8192);
2197 if (!client
->stack
) {
2198 ast_log(LOG_ERROR
, "Failed to allocate stack for client '%s'\n", client
->name
);
2201 client
->f
= iks_filter_new();
2203 ast_log(LOG_ERROR
, "Failed to create filter for client '%s'\n", client
->name
);
2206 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2208 asprintf(&resource
, "%s/asterisk", client
->user
);
2210 client
->jid
= iks_id_new(client
->stack
, resource
);
2214 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2215 if (client
->component
) {
2216 iks_filter_add_rule(client
->f
, aji_dinfo_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2217 iks_filter_add_rule(client
->f
, aji_ditems_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE
);
2218 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
);
2219 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
);
2221 iks_filter_add_rule(client
->f
, aji_client_info_handler
, client
, IKS_RULE_NS
, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE
);
2223 if (!strchr(client
->user
, '/') && !client
->component
) { /*client */
2225 asprintf(&resource
, "%s/asterisk", client
->user
);
2227 client
->jid
= iks_id_new(client
->stack
, resource
);
2231 client
->jid
= iks_id_new(client
->stack
, client
->user
);
2232 iks_set_log_hook(client
->p
, aji_log_hook
);
2233 ASTOBJ_UNLOCK(client
);
2234 ASTOBJ_CONTAINER_LINK(&clients
,client
);
2239 * \brief creates transport.
2240 * \param label, buddy to dump it into.
2243 /* no connecting to transports today */
2245 static int aji_create_transport(char *label, struct aji_client *client)
2247 char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
2248 struct aji_buddy *buddy = NULL;
2250 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
2252 buddy = malloc(sizeof(struct aji_buddy));
2254 ast_log(LOG_WARNING, "Out of memory\n");
2257 memset(buddy, 0, sizeof(struct aji_buddy));
2260 ASTOBJ_WRLOCK(buddy);
2262 if ((buddyname = strchr(label, ','))) {
2265 if (buddyname && buddyname[0] != '\0') {
2266 if ((user = strchr(buddyname, ','))) {
2269 if (user && user[0] != '\0') {
2270 if ((pass = strchr(user, ','))) {
2273 ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
2274 ast_copy_string(buddy->user, user, sizeof(buddy->user));
2275 ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
2276 ast_copy_string(buddy->server, server, sizeof(buddy->server));
2283 ASTOBJ_UNLOCK(buddy);
2284 ASTOBJ_UNMARK(buddy);
2285 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
2291 * \brief creates buddy.
2292 * \param label, buddy to dump it into.
2295 static int aji_create_buddy(char *label
, struct aji_client
*client
)
2297 struct aji_buddy
*buddy
= NULL
;
2299 buddy
= ASTOBJ_CONTAINER_FIND(&client
->buddies
,label
);
2302 buddy
= malloc(sizeof(struct aji_buddy
));
2304 ast_log(LOG_WARNING
, "Out of memory\n");
2307 memset(buddy
, 0, sizeof(struct aji_buddy
));
2310 ASTOBJ_WRLOCK(buddy
);
2311 ast_copy_string(buddy
->name
, label
, sizeof(buddy
->name
));
2312 ASTOBJ_UNLOCK(buddy
);
2314 ASTOBJ_CONTAINER_LINK(&client
->buddies
, buddy
);
2316 ASTOBJ_UNMARK(buddy
);
2317 ASTOBJ_UNREF(buddy
, aji_buddy_destroy
);
2323 * \brief load config file.
2327 static int aji_load_config(void)
2331 struct ast_config
*cfg
= NULL
;
2332 struct ast_variable
*var
= NULL
;
2334 cfg
= ast_config_load(JABBER_CONFIG
);
2336 ast_log(LOG_WARNING
, "No such configuration file %s\n", JABBER_CONFIG
);
2340 cat
= ast_category_browse(cfg
, NULL
);
2341 for (var
= ast_variable_browse(cfg
, "general"); var
; var
= var
->next
) {
2342 if (!strcasecmp(var
->name
, "debug"))
2343 debug
= (ast_false(ast_variable_retrieve(cfg
, "general", "debug"))) ? 0 : 1;
2344 else if (!strcasecmp(var
->name
, "autoprune"))
2345 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOPRUNE
);
2346 else if (!strcasecmp(var
->name
, "autoregister"))
2347 ast_set2_flag(&globalflags
, ast_true(var
->value
), AJI_AUTOREGISTER
);
2351 if (strcasecmp(cat
, "general")) {
2352 var
= ast_variable_browse(cfg
, cat
);
2353 aji_create_client(cat
, var
, debug
);
2355 cat
= ast_category_browse(cfg
, cat
);
2357 ast_config_destroy(cfg
); /* or leak memory */
2362 * \brief grab a aji_client structure by label name.
2366 struct aji_client
*ast_aji_get_client(const char *name
)
2368 struct aji_client
*client
= NULL
;
2370 client
= ASTOBJ_CONTAINER_FIND(&clients
, name
);
2371 if (!client
&& !strchr(name
, '@'))
2372 client
= ASTOBJ_CONTAINER_FIND_FULL(&clients
, name
, user
,,, strcasecmp
);
2376 struct aji_client_container
*ast_aji_get_clients(void)
2381 static char mandescr_jabber_send
[] =
2382 "Description: Sends a message to a Jabber Client.\n"
2384 " Jabber: Client or transport Asterisk uses to connect to JABBER.\n"
2385 " ScreenName: User Name to message.\n"
2386 " Message: Message to be sent to the buddy\n";
2388 /*! \brief Send a Jabber Message via call from the Manager */
2389 static int manager_jabber_send(struct mansession
*s
, const struct message
*m
)
2391 struct aji_client
*client
= NULL
;
2392 const char *id
= astman_get_header(m
,"ActionID");
2393 const char *jabber
= astman_get_header(m
,"Jabber");
2394 const char *screenname
= astman_get_header(m
,"ScreenName");
2395 const char *message
= astman_get_header(m
,"Message");
2397 if (ast_strlen_zero(jabber
)) {
2398 astman_send_error(s
, m
, "No transport specified");
2401 if (ast_strlen_zero(screenname
)) {
2402 astman_send_error(s
, m
, "No ScreenName specified");
2405 if (ast_strlen_zero(message
)) {
2406 astman_send_error(s
, m
, "No Message specified");
2410 astman_send_ack(s
, m
, "Attempting to send Jabber Message");
2411 client
= ast_aji_get_client(jabber
);
2413 astman_send_error(s
, m
, "Could not find Sender");
2416 if (strchr(screenname
, '@') && message
){
2417 ast_aji_send(client
, screenname
, message
);
2418 if (!ast_strlen_zero(id
))
2419 astman_append(s
, "ActionID: %s\r\n",id
);
2420 astman_append(s
, "Response: Success\r\n");
2423 if (!ast_strlen_zero(id
))
2424 astman_append(s
, "ActionID: %s\r\n",id
);
2425 astman_append(s
, "Response: Failure\r\n");
2430 static int aji_reload()
2432 ASTOBJ_CONTAINER_MARKALL(&clients
);
2433 if (!aji_load_config()) {
2434 ast_log(LOG_ERROR
, "JABBER: Failed to load config.\n");
2437 ASTOBJ_CONTAINER_PRUNE_MARKED(&clients
, aji_client_destroy
);
2438 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2439 ASTOBJ_RDLOCK(iterator
);
2440 if(iterator
->state
== AJI_DISCONNECTED
) {
2441 if (!iterator
->thread
)
2442 ast_pthread_create_background(&iterator
->thread
, NULL
, aji_recv_loop
, iterator
);
2443 } else if (iterator
->state
== AJI_CONNECTING
)
2444 aji_get_roster(iterator
);
2445 ASTOBJ_UNLOCK(iterator
);
2451 static int unload_module(void)
2454 /* Check if TLS is initialized. If that's the case, we can't unload this
2455 module due to a bug in the iksemel library that will cause a crash or
2456 a deadlock. We're trying to find a way to handle this, but in the meantime
2457 we will simply refuse to die...
2459 if (tls_initialized
) {
2460 ast_log(LOG_ERROR
, "Module can't be unloaded due to a bug in the Iksemel library when using TLS.\n");
2461 return 1; /* You need a forced unload to get rid of this module */
2464 ast_cli_unregister_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
2465 ast_unregister_application(app_ajisend
);
2466 ast_unregister_application(app_ajistatus
);
2467 ast_manager_unregister("JabberSend");
2469 ASTOBJ_CONTAINER_TRAVERSE(&clients
, 1, {
2470 ASTOBJ_RDLOCK(iterator
);
2471 if (option_debug
> 2)
2472 ast_log(LOG_DEBUG
, "JABBER: Releasing and disconneing client: %s\n", iterator
->name
);
2473 iterator
->state
= AJI_DISCONNECTING
;
2474 ast_aji_disconnect(iterator
);
2475 pthread_join(iterator
->thread
, NULL
);
2476 ASTOBJ_UNLOCK(iterator
);
2479 ASTOBJ_CONTAINER_DESTROYALL(&clients
, aji_client_destroy
);
2480 ASTOBJ_CONTAINER_DESTROY(&clients
);
2484 static int load_module(void)
2486 ASTOBJ_CONTAINER_INIT(&clients
);
2488 return AST_MODULE_LOAD_DECLINE
;
2489 ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM
, manager_jabber_send
,
2490 "Sends a message to a Jabber Client", mandescr_jabber_send
);
2491 ast_register_application(app_ajisend
, aji_send_exec
, ajisend_synopsis
, ajisend_descrip
);
2492 ast_register_application(app_ajistatus
, aji_status_exec
, ajistatus_synopsis
, ajistatus_descrip
);
2493 ast_cli_register_multiple(aji_cli
, sizeof(aji_cli
) / sizeof(struct ast_cli_entry
));
2498 static int reload(void)
2504 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "AJI - Asterisk Jabber Interface",
2505 .load
= load_module
,
2506 .unload
= unload_module
,