Add auth_policy option to jabber.conf for auto user registration.
[asterisk/asterisk.git] / res / res_jabber.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Matt O'Gorman <mogorman@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  * \brief A resource for interfacing Asterisk directly as a client
21  * or a component to a XMPP/Jabber compliant server.
22  *
23  * References:
24  * - http://www.xmpp.org - The XMPP standards foundation
25  *
26  * \extref Iksemel http://code.google.com/p/iksemel/
27  *
28  * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
29  * \todo Dialplan applications need RETURN variable, like JABBERSENDSTATUS
30  *
31  */
32
33 /*** MODULEINFO
34         <depend>iksemel</depend>
35         <use>openssl</use>
36  ***/
37
38 #include "asterisk.h"
39
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41
42 #include <ctype.h>
43 #include <iksemel.h>
44
45 #include "asterisk/channel.h"
46 #include "asterisk/jabber.h"
47 #include "asterisk/file.h"
48 #include "asterisk/config.h"
49 #include "asterisk/callerid.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/cli.h"
52 #include "asterisk/app.h"
53 #include "asterisk/pbx.h"
54 #include "asterisk/md5.h"
55 #include "asterisk/acl.h"
56 #include "asterisk/utils.h"
57 #include "asterisk/module.h"
58 #include "asterisk/astobj.h"
59 #include "asterisk/astdb.h"
60 #include "asterisk/manager.h"
61
62 /*** DOCUMENTATION
63         <application name="JabberSend" language="en_US">
64                 <synopsis>
65                         Sends an XMPP message to a buddy.
66                 </synopsis>
67                 <syntax>
68                         <parameter name="account" required="true">
69                                 <para>The local named account to listen on (specified in
70                                 jabber.conf)</para>
71                         </parameter>
72                         <parameter name="jid" required="true">
73                                 <para>Jabber ID of the buddy to send the message to. It can be a
74                                 bare JID (username@domain) or a full JID (username@domain/resource).</para>
75                         </parameter>
76                         <parameter name="message" required="true">
77                                 <para>The message to send.</para>
78                         </parameter>
79                 </syntax>
80                 <description>
81                         <para>Sends the content of <replaceable>message</replaceable> as text message
82                         from the given <replaceable>account</replaceable> to the buddy identified by
83                         <replaceable>jid</replaceable></para>
84                         <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
85                         to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
86                         <replaceable>asterisk</replaceable>, configured in jabber.conf.</para>
87                 </description>
88                 <see-also>
89                         <ref type="function">JABBER_STATUS</ref>
90                         <ref type="function">JABBER_RECEIVE</ref>
91                 </see-also>
92         </application>
93         <function name="JABBER_RECEIVE" language="en_US">
94                 <synopsis>
95                         Reads XMPP messages.
96                 </synopsis>
97                 <syntax>
98                         <parameter name="account" required="true">
99                                 <para>The local named account to listen on (specified in
100                                 jabber.conf)</para>
101                         </parameter>
102                         <parameter name="jid" required="true">
103                                 <para>Jabber ID of the buddy to receive message from. It can be a
104                                 bare JID (username@domain) or a full JID (username@domain/resource).</para>
105                         </parameter>
106                         <parameter name="timeout">
107                                 <para>In seconds, defaults to <literal>20</literal>.</para>
108                         </parameter>
109                 </syntax>
110                 <description>
111                         <para>Receives a text message on the given <replaceable>account</replaceable>
112                         from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
113                         <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
114                         sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
115                         the <replaceable>asterisk</replaceable> XMPP account configured in jabber.conf.</para>
116                 </description>
117                 <see-also>
118                         <ref type="function">JABBER_STATUS</ref>
119                         <ref type="application">JabberSend</ref>
120                 </see-also>
121         </function>
122         <function name="JABBER_STATUS" language="en_US">
123                 <synopsis>
124                         Retrieves a buddy's status.
125                 </synopsis>
126                 <syntax>
127                         <parameter name="account" required="true">
128                                 <para>The local named account to listen on (specified in
129                                 jabber.conf)</para>
130                         </parameter>
131                         <parameter name="jid" required="true">
132                                 <para>Jabber ID of the buddy to receive message from. It can be a
133                                 bare JID (username@domain) or a full JID (username@domain/resource).</para>
134                         </parameter>
135                 </syntax>
136                 <description>
137                         <para>Retrieves the numeric status associated with the buddy identified
138                         by <replaceable>jid</replaceable>.
139                         If the buddy does not exist in the buddylist, returns 7.</para>
140                         <para>Status will be 1-7.</para>
141                         <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
142                         <para>If not in roster variable will be set to 7.</para>
143                         <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
144                         <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
145                         the associated XMPP account configured in jabber.conf.</para>
146                 </description>
147                 <see-also>
148                         <ref type="function">JABBER_RECEIVE</ref>
149                         <ref type="application">JabberSend</ref>
150                 </see-also>
151         </function>
152         <application name="JabberSendGroup" language="en_US">
153                 <synopsis>
154                         Send a Jabber Message to a specified chat room
155                 </synopsis>
156                 <syntax>
157                         <parameter name="Jabber" required="true">
158                                 <para>Client or transport Asterisk uses to connect to Jabber.</para>
159                         </parameter>
160                         <parameter name="RoomJID" required="true">
161                                 <para>XMPP/Jabber JID (Name) of chat room.</para>
162                         </parameter>
163                         <parameter name="Message" required="true">
164                                 <para>Message to be sent to the chat room.</para>
165                         </parameter>
166                         <parameter name="Nickname" required="false">
167                                 <para>The nickname Asterisk uses in the chat room.</para>
168                         </parameter>
169                 </syntax>
170                 <description>
171                         <para>Allows user to send a message to a chat room via XMPP.</para>
172                         <note><para>To be able to send messages to a chat room, a user must have previously joined it. Use the <replaceable>JabberJoin</replaceable> function to do so.</para></note>
173                 </description>
174         </application>
175         <application name="JabberJoin" language="en_US">
176                 <synopsis>
177                         <para>Join a chat room</para>
178                 </synopsis>
179                 <syntax>
180                         <parameter name="Jabber" required="true">
181                                 <para>Client or transport Asterisk uses to connect to Jabber.</para>
182                         </parameter>
183                         <parameter name="RoomJID" required="true">
184                                 <para>XMPP/Jabber JID (Name) of chat room.</para>
185                         </parameter>
186                         <parameter name="Nickname" required="false">
187                                 <para>The nickname Asterisk will use in the chat room.</para>
188                                 <note><para>If a different nickname is supplied to an already joined room, the old nick will be changed to the new one.</para></note>
189                         </parameter>
190                 </syntax>
191                 <description>
192                         <para>Allows Asterisk to join a chat room.</para>
193                 </description>
194         </application>
195         <application name="JabberLeave" language="en_US">
196                 <synopsis>
197                         <para>Leave a chat room</para>
198                 </synopsis>
199                 <syntax>
200                         <parameter name="Jabber" required="true">
201                                 <para>Client or transport Asterisk uses to connect to Jabber.</para>
202                         </parameter>
203                         <parameter name="RoomJID" required="true">
204                                 <para>XMPP/Jabber JID (Name) of chat room.</para>
205                         </parameter>
206                         <parameter name="Nickname" required="false">
207                                 <para>The nickname Asterisk uses in the chat room.</para>
208                         </parameter>
209                 </syntax>
210                 <description>
211                         <para>Allows Asterisk to leave a chat room.</para>
212                 </description>
213         </application>
214         <application name="JabberStatus" language="en_US">
215                 <synopsis>
216                         Retrieve the status of a jabber list member
217                 </synopsis>
218                 <syntax>
219                         <parameter name="Jabber" required="true">
220                                 <para>Client or transport Asterisk users to connect to Jabber.</para>
221                         </parameter>
222                         <parameter name="JID" required="true">
223                                 <para>XMPP/Jabber JID (Name) of recipient.</para>
224                         </parameter>
225                         <parameter name="Variable" required="true">
226                                 <para>Variable to store the status of requested user.</para>
227                         </parameter>
228                 </syntax>
229                 <description>
230                         <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
231                         <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
232                         The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
233                         <enumlist>
234                                 <enum name="1">
235                                         <para>Online.</para>
236                                 </enum>
237                                 <enum name="2">
238                                         <para>Chatty.</para>
239                                 </enum>
240                                 <enum name="3">
241                                         <para>Away.</para>
242                                 </enum>
243                                 <enum name="4">
244                                         <para>Extended Away.</para>
245                                 </enum>
246                                 <enum name="5">
247                                         <para>Do Not Disturb.</para>
248                                 </enum>
249                                 <enum name="6">
250                                         <para>Offline.</para>
251                                 </enum>
252                                 <enum name="7">
253                                         <para>Not In Roster.</para>
254                                 </enum>
255                         </enumlist>
256                 </description>
257         </application>
258         <manager name="JabberSend" language="en_US">
259                 <synopsis>
260                         Sends a message to a Jabber Client.
261                 </synopsis>
262                 <syntax>
263                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
264                         <parameter name="Jabber" required="true">
265                                 <para>Client or transport Asterisk uses to connect to JABBER.</para>
266                         </parameter>
267                         <parameter name="JID" required="true">
268                                 <para>XMPP/Jabber JID (Name) of recipient.</para>
269                         </parameter>
270                         <parameter name="Message" required="true">
271                                 <para>Message to be sent to the buddy.</para>
272                         </parameter>
273                 </syntax>
274                 <description>
275                         <para>Sends a message to a Jabber Client.</para>
276                 </description>
277         </manager>
278  ***/
279
280 /*! \todo This should really be renamed to xmpp.conf. For backwards compatibility, we
281         need to read both files */
282 #define JABBER_CONFIG "jabber.conf"
283
284 /*-- Forward declarations */
285 static void aji_message_destroy(struct aji_message *obj);
286 static void aji_buddy_destroy(struct aji_buddy *obj);
287 static void aji_client_destroy(struct aji_client *obj);
288 static int aji_is_secure(struct aji_client *client);
289 #ifdef HAVE_OPENSSL
290 static int aji_start_tls(struct aji_client *client);
291 static int aji_tls_handshake(struct aji_client *client);
292 #endif
293 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
294 static int aji_recv(struct aji_client *client, int timeout);
295 static int aji_send_header(struct aji_client *client, const char *to);
296 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
297 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
298 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
299 static int aji_act_hook(void *data, int type, iks *node);
300 static void aji_handle_iq(struct aji_client *client, iks *node);
301 static void aji_handle_message(struct aji_client *client, ikspak *pak);
302 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
303 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
304 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message);
305 static void *aji_recv_loop(void *data);
306 static int aji_initialize(struct aji_client *client);
307 static int aji_client_connect(void *data, ikspak *pak);
308 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
309 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc);
310 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
311 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
312 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
313 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
314 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
315 static int aji_create_client(char *label, struct ast_variable *var, int debug);
316 static int aji_create_buddy(char *label, struct aji_client *client);
317 static int aji_reload(int reload);
318 static int aji_load_config(int reload);
319 static void aji_pruneregister(struct aji_client *client);
320 static int aji_filter_roster(void *data, ikspak *pak);
321 static int aji_get_roster(struct aji_client *client);
322 static int aji_client_info_handler(void *data, ikspak *pak);
323 static int aji_dinfo_handler(void *data, ikspak *pak);
324 static int aji_ditems_handler(void *data, ikspak *pak);
325 static int aji_register_query_handler(void *data, ikspak *pak);
326 static int aji_register_approve_handler(void *data, ikspak *pak);
327 static int aji_reconnect(struct aji_client *client);
328 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
329 /* No transports in this version */
330 /*
331 static int aji_create_transport(char *label, struct aji_client *client);
332 static int aji_register_transport(void *data, ikspak *pak);
333 static int aji_register_transport2(void *data, ikspak *pak);
334 */
335
336 static struct ast_cli_entry aji_cli[] = {
337         AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug"),
338         AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
339         AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
340         AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
341         AST_CLI_DEFINE(aji_test, "Shows roster, but is generally used for mog's debugging."),
342 };
343
344 static char *app_ajisend = "JabberSend";
345 static char *app_ajisendgroup = "JabberSendGroup";
346 static char *app_ajistatus = "JabberStatus";
347 static char *app_ajijoin = "JabberJoin";
348 static char *app_ajileave = "JabberLeave";
349
350 static struct aji_client_container clients;
351 static struct aji_capabilities *capabilities = NULL;
352 static ast_cond_t message_received_condition;
353 static ast_mutex_t messagelock;
354
355 /*! \brief Global flags, initialized to default values */
356 static struct ast_flags globalflags = { AJI_AUTOREGISTER | AJI_AUTOACCEPT };
357
358 /*!
359  * \internal
360  * \brief Deletes the aji_client data structure.
361  * \param obj aji_client The structure we will delete.
362  * \return void.
363  */
364 static void aji_client_destroy(struct aji_client *obj)
365 {
366         struct aji_message *tmp;
367         ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, aji_buddy_destroy);
368         ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
369         iks_filter_delete(obj->f);
370         iks_parser_delete(obj->p);
371         iks_stack_delete(obj->stack);
372         AST_LIST_LOCK(&obj->messages);
373         while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
374                 aji_message_destroy(tmp);
375         }
376         AST_LIST_HEAD_DESTROY(&obj->messages);
377         ast_free(obj);
378 }
379
380 /*!
381  * \internal
382  * \brief Deletes the aji_buddy data structure.
383  * \param obj aji_buddy The structure we will delete.
384  * \return void.
385  */
386 static void aji_buddy_destroy(struct aji_buddy *obj)
387 {
388         struct aji_resource *tmp;
389
390         while ((tmp = obj->resources)) {
391                 obj->resources = obj->resources->next;
392                 ast_free(tmp->description);
393                 ast_free(tmp);
394         }
395
396         ast_free(obj);
397 }
398
399 /*!
400  * \internal
401  * \brief Deletes the aji_message data structure.
402  * \param obj aji_message The structure we will delete.
403  * \return void.
404  */
405 static void aji_message_destroy(struct aji_message *obj)
406 {
407         if (obj->from) {
408                 ast_free(obj->from);
409         }
410         if (obj->message) {
411                 ast_free(obj->message);
412         }
413         ast_free(obj);
414 }
415
416 /*!
417  * \internal
418  * \brief Find version in XML stream and populate our capabilities list
419  * \param node the node attribute in the caps element we'll look for or add to
420  * our list
421  * \param version the version attribute in the caps element we'll look for or
422  * add to our list
423  * \param pak struct The XML stanza we're processing
424  * \return a pointer to the added or found aji_version structure
425  */
426 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
427 {
428         struct aji_capabilities *list = NULL;
429         struct aji_version *res = NULL;
430
431         list = capabilities;
432
433         if (!node)
434                 node = pak->from->full;
435         if (!version)
436                 version = "none supplied.";
437         while(list) {
438                 if (!strcasecmp(list->node, node)) {
439                         res = list->versions;
440                         while(res) {
441                                  if (!strcasecmp(res->version, version))
442                                          return res;
443                                  res = res->next;
444                         }
445                         /* Specified version not found. Let's add it to
446                            this node in our capabilities list */
447                         if (!res) {
448                                 res = ast_malloc(sizeof(*res));
449                                 if (!res) {
450                                         ast_log(LOG_ERROR, "Out of memory!\n");
451                                         return NULL;
452                                 }
453                                 res->jingle = 0;
454                                 res->parent = list;
455                                 ast_copy_string(res->version, version, sizeof(res->version));
456                                 res->next = list->versions;
457                                 list->versions = res;
458                                 return res;
459                         }
460                 }
461                 list = list->next;
462         }
463         /* Specified node not found. Let's add it our capabilities list */
464         if (!list) {
465                 list = ast_malloc(sizeof(*list));
466                 if (!list) {
467                         ast_log(LOG_ERROR, "Out of memory!\n");
468                         return NULL;
469                 }
470                 res = ast_malloc(sizeof(*res));
471                 if (!res) {
472                         ast_log(LOG_ERROR, "Out of memory!\n");
473                         ast_free(list);
474                         return NULL;
475                 }
476                 ast_copy_string(list->node, node, sizeof(list->node));
477                 ast_copy_string(res->version, version, sizeof(res->version));
478                 res->jingle = 0;
479                 res->parent = list;
480                 res->next = NULL;
481                 list->versions = res;
482                 list->next = capabilities;
483                 capabilities = list;
484         }
485         return res;
486 }
487
488 /*!
489  * \internal
490  * \brief Find the aji_resource we want
491  * \param buddy aji_buddy A buddy
492  * \param name 
493  * \return aji_resource object
494 */
495 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
496 {
497         struct aji_resource *res = NULL;
498         if (!buddy || !name)
499                 return res;
500         res = buddy->resources;
501         while (res) {
502                 if (!strcasecmp(res->resource, name)) {
503                         break;
504                 }
505                 res = res->next;
506         }
507         return res;
508 }
509
510 /*!
511  * \internal
512  * \brief Jabber GTalk function
513  * \param node iks
514  * \return 1 on success, 0 on failure.
515 */
516 static int gtalk_yuck(iks *node)
517 {
518         if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps"))
519                 return 1;
520         return 0;
521 }
522
523 /*!
524  * \internal
525  * \brief Setup the authentication struct
526  * \param id iksid 
527  * \param pass password
528  * \param sid
529  * \return x iks
530 */
531 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
532 {
533         iks *x, *y;
534         x = iks_new("iq");
535         iks_insert_attrib(x, "type", "set");
536         y = iks_insert(x, "query");
537         iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
538         iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
539         iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
540         if (sid) {
541                 char buf[41];
542                 char sidpass[100];
543                 snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
544                 ast_sha1_hash(buf, sidpass);
545                 iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
546         } else {
547                 iks_insert_cdata(iks_insert(y, "password"), pass, 0);
548         }
549         return x;
550 }
551
552 /*!
553  * \internal
554  * \brief Dial plan function status(). puts the status of watched user 
555  * into a channel variable.
556  * \param chan ast_channel
557  * \param data
558  * \retval 0 success
559  * \retval -1 error
560  */
561 static int aji_status_exec(struct ast_channel *chan, const char *data)
562 {
563         struct aji_client *client = NULL;
564         struct aji_buddy *buddy = NULL;
565         struct aji_resource *r = NULL;
566         char *s = NULL;
567         int stat = 7;
568         char status[2];
569         static int deprecation_warning = 0;
570         AST_DECLARE_APP_ARGS(args,
571                 AST_APP_ARG(sender);
572                 AST_APP_ARG(jid);
573                 AST_APP_ARG(variable);
574         );
575         AST_DECLARE_APP_ARGS(jid,
576                 AST_APP_ARG(screenname);
577                 AST_APP_ARG(resource);
578         );
579
580         if (deprecation_warning++ % 10 == 0)
581                 ast_log(LOG_WARNING, "JabberStatus is deprecated.  Please use the JABBER_STATUS dialplan function in the future.\n");
582
583         if (!data) {
584                 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
585                 return 0;
586         }
587         s = ast_strdupa(data);
588         AST_STANDARD_APP_ARGS(args, s);
589
590         if (args.argc != 3) {
591                 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
592                 return -1;
593         }
594
595         AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
596         if (jid.argc < 1 || jid.argc > 2) {
597                 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
598                 return -1;
599         }
600
601         if (!(client = ast_aji_get_client(args.sender))) {
602                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
603                 return -1;
604         }
605         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
606         if (!buddy) {
607                 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
608                 return -1;
609         }
610         r = aji_find_resource(buddy, jid.resource);
611         if (!r && buddy->resources) 
612                 r = buddy->resources;
613         if (!r)
614                 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
615         else
616                 stat = r->status;
617         snprintf(status, sizeof(status), "%d", stat);
618         pbx_builtin_setvar_helper(chan, args.variable, status);
619         return 0;
620 }
621
622 /*!
623  * \internal
624  * \brief Dial plan funtcion to retrieve the status of a buddy.
625  * \param channel The associated ast_channel, if there is one
626  * \param data The account, buddy JID, and optional timeout
627  * timeout.
628  * \retval 0 success
629  * \retval -1 failure
630  */
631 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
632 {
633         struct aji_client *client = NULL;
634         struct aji_buddy *buddy = NULL;
635         struct aji_resource *r = NULL;
636         int stat = 7;
637         AST_DECLARE_APP_ARGS(args,
638                 AST_APP_ARG(sender);
639                 AST_APP_ARG(jid);
640         );
641         AST_DECLARE_APP_ARGS(jid,
642                 AST_APP_ARG(screenname);
643                 AST_APP_ARG(resource);
644         );
645
646         if (!data) {
647                 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
648                 return 0;
649         }
650         AST_STANDARD_APP_ARGS(args, data);
651
652         if (args.argc != 2) {
653                 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
654                 return -1;
655         }
656
657         AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
658         if (jid.argc < 1 || jid.argc > 2) {
659                 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
660                 return -1;
661         }
662
663         if (!(client = ast_aji_get_client(args.sender))) {
664                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
665                 return -1;
666         }
667         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
668         if (!buddy) {
669                 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
670                 return -1;
671         }
672         r = aji_find_resource(buddy, jid.resource);
673         if (!r && buddy->resources) 
674                 r = buddy->resources;
675         if (!r)
676                 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
677         else
678                 stat = r->status;
679         snprintf(buf, buflen, "%d", stat);
680         return 0;
681 }
682
683 static struct ast_custom_function jabberstatus_function = {
684         .name = "JABBER_STATUS",
685         .read = acf_jabberstatus_read,
686 };
687
688 /*!
689  * \internal
690  * \brief Dial plan function to receive a message.
691  * \param channel The associated ast_channel, if there is one
692  * \param data The account, JID, and optional timeout
693  * timeout.
694  * \retval 0 success
695  * \retval -1 failure
696  */
697 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
698 {
699         char *aux = NULL, *parse = NULL;
700         int timeout;
701         int jidlen, resourcelen;
702         struct timeval start;
703         long diff = 0;
704         struct aji_client *client = NULL;
705         int found = 0;
706         struct aji_message *tmp = NULL;
707         AST_DECLARE_APP_ARGS(args,
708                         AST_APP_ARG(account);
709                         AST_APP_ARG(jid);
710                         AST_APP_ARG(timeout);
711                         );
712         AST_DECLARE_APP_ARGS(jid,
713                         AST_APP_ARG(screenname);
714                         AST_APP_ARG(resource);
715         );
716
717         if (ast_strlen_zero(data)) {
718                 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
719                 return -1;
720         }
721
722         parse = ast_strdupa(data);
723         AST_STANDARD_APP_ARGS(args, parse);
724
725         if (args.argc < 2 || args.argc > 3) {
726                 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
727                 return -1;
728         }
729
730         client = ast_aji_get_client(args.account);
731         if (!client) {
732                 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
733                 return -1;
734         }
735
736         parse = ast_strdupa(args.jid);
737         AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
738         if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > AJI_MAX_JIDLEN) {
739                 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
740                 ASTOBJ_UNREF(client, aji_client_destroy);
741                 return -1;
742         }
743
744         if (ast_strlen_zero(args.timeout)) {
745                 timeout = 20;
746         } else {
747                 sscanf(args.timeout, "%d", &timeout);
748                 if (timeout <= 0) {
749                         ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
750                         ASTOBJ_UNREF(client, aji_client_destroy);
751                         return -1;
752                 }
753         }
754
755         jidlen = strlen(jid.screenname);
756         resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
757
758         ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
759
760         start = ast_tvnow();
761
762         if (ast_autoservice_start(chan) < 0) {
763                 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", chan->name);
764                 return -1;
765         }
766
767         /* search the messages list, grab the first message that matches with
768          * the from JID we're expecting, and remove it from the messages list */
769         while (diff < timeout) {
770                 struct timespec ts = { 0, };
771                 struct timeval wait;
772                 int res;
773
774                 wait = ast_tvadd(start, ast_tv(timeout, 0));
775                 ts.tv_sec = wait.tv_sec;
776                 ts.tv_nsec = wait.tv_usec * 1000;
777
778                 /* wait up to timeout seconds for an incoming message */
779                 ast_mutex_lock(&messagelock);
780                 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
781                 ast_mutex_unlock(&messagelock);
782                 if (res == ETIMEDOUT) {
783                         ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
784                         break;
785                 };
786
787                 AST_LIST_LOCK(&client->messages);
788                 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
789                         if (jid.argc == 1) {
790                                 /* no resource provided, compare bare JIDs */
791                                 if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
792                                         continue;
793                                 }
794                         } else {
795                                 /* resource appended, compare bare JIDs and resources */
796                                 char *resource = strchr(tmp->from, '/');
797                                 if (!resource || strlen(resource) == 0) {
798                                         ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", tmp->from);
799                                         if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
800                                                 continue;
801                                         }
802                                 } else {
803                                         resource ++;
804                                         if (strncasecmp(jid.screenname, tmp->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
805                                                 continue;
806                                         }
807                                 }
808                         }
809                         /* check if the message is not too old */
810                         if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
811                                 ast_debug(3, "Found old message from %s, deleting it\n", tmp->from);
812                                 AST_LIST_REMOVE_CURRENT(list);
813                                 aji_message_destroy(tmp);
814                                 continue;
815                         }
816                         found = 1;
817                         aux = ast_strdupa(tmp->message);
818                         AST_LIST_REMOVE_CURRENT(list);
819                         aji_message_destroy(tmp);
820                         break;
821                 }
822                 AST_LIST_TRAVERSE_SAFE_END;
823                 AST_LIST_UNLOCK(&client->messages);
824                 if (found) {
825                         break;
826                 }
827
828                 /* check timeout */
829                 diff = ast_tvdiff_ms(ast_tvnow(), start);
830         }
831
832         ASTOBJ_UNREF(client, aji_client_destroy);
833         if (ast_autoservice_stop(chan) < 0) {
834                 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", chan->name);
835         }
836
837         /* return if we timed out */
838         if (!found) {
839                 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
840                 return -1;
841         }
842         ast_copy_string(buf, aux, buflen);
843
844         return 0;
845 }
846
847 static struct ast_custom_function jabberreceive_function = {
848         .name = "JABBER_RECEIVE",
849         .read = acf_jabberreceive_read,
850 };
851
852 /*!
853  * \internal
854  * \brief Delete old messages from a given JID
855  * Messages stored during more than client->message_timeout are deleted
856  * \param client Asterisk's XMPP client
857  * \param from the JID we received messages from
858  * \retval the number of deleted messages
859  * \retval -1 failure
860  */
861 static int delete_old_messages(struct aji_client *client, char *from)
862 {
863         int deleted = 0;
864         int isold = 0;
865         struct aji_message *tmp = NULL;
866         if (!client) {
867                 ast_log(LOG_ERROR, "Cannot find our XMPP client\n");
868                 return -1;
869         }
870
871         /* remove old messages */
872         AST_LIST_LOCK(&client->messages);
873         if (AST_LIST_EMPTY(&client->messages)) {
874                 AST_LIST_UNLOCK(&client->messages);
875                 return 0;
876         }
877
878         AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
879                 if (isold) {
880                         if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
881                                 AST_LIST_REMOVE_CURRENT(list);
882                                 aji_message_destroy(tmp);
883                                 deleted ++;
884                         }
885                 } else if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
886                         isold = 1;
887                         if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
888                                 AST_LIST_REMOVE_CURRENT(list);
889                                 aji_message_destroy(tmp);
890                                 deleted ++;
891                         }
892                 }
893         }
894         AST_LIST_TRAVERSE_SAFE_END;
895         AST_LIST_UNLOCK(&client->messages);
896
897         return deleted;
898 }
899
900 /*!
901  * \internal
902  * \brief Delete old messages
903  * Messages stored during more than client->message_timeout are deleted
904  * \param client Asterisk's XMPP client
905  * \retval the number of deleted messages
906  * \retval -1 failure
907  */
908 static int delete_old_messages_all(struct aji_client *client)
909 {
910         return delete_old_messages(client, NULL);
911 }
912
913 /*!
914 * \brief Application to join a chat room
915 * \param chan ast_channel
916 * \param data  Data is sender|jid|nickname.
917 * \retval 0 success
918 * \retval -1 error
919 */
920 static int aji_join_exec(struct ast_channel *chan, const char *data)
921 {
922         struct aji_client *client = NULL;
923         char *s;
924         char nick[AJI_MAX_RESJIDLEN];
925
926         AST_DECLARE_APP_ARGS(args,
927                 AST_APP_ARG(sender);
928                 AST_APP_ARG(jid);
929                 AST_APP_ARG(nick);
930         );
931
932         if (!data) {
933                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
934                 return -1;
935         }
936         s = ast_strdupa(data);
937
938         AST_STANDARD_APP_ARGS(args, s);
939         if (args.argc < 2 || args.argc > 3) {
940                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
941                 return -1;
942         }
943         
944         if (!(client = ast_aji_get_client(args.sender))) {
945                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
946                 return -1;
947         }
948         
949         if (strchr(args.jid, '/')) {
950                 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
951                 ASTOBJ_UNREF(client, aji_client_destroy);
952                 return -1;
953         }       
954         
955         if (!ast_strlen_zero(args.nick)) {
956                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
957         } else {
958                 if (client->component) {
959                         sprintf(nick, "asterisk");                      
960                 } else {
961                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
962                 }
963         }
964
965         if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
966                 ast_aji_join_chat(client, args.jid, nick);
967         } else {
968                 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
969         }
970         
971         ASTOBJ_UNREF(client, aji_client_destroy);
972         return 0;
973 }
974         
975 /*!
976 * \brief Application to leave a chat room
977 * \param chan ast_channel
978 * \param data  Data is sender|jid|nickname.
979 * \retval 0 success
980 * \retval -1 error
981 */
982 static int aji_leave_exec(struct ast_channel *chan, const char *data)
983 {
984         struct aji_client *client = NULL;
985         char *s;
986         char nick[AJI_MAX_RESJIDLEN];
987         AST_DECLARE_APP_ARGS(args,
988                 AST_APP_ARG(sender);
989                 AST_APP_ARG(jid);
990                 AST_APP_ARG(nick);
991         );
992         
993         if (!data) {
994                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
995                 return -1;
996         }
997         s = ast_strdupa(data);
998         
999         AST_STANDARD_APP_ARGS(args, s);
1000         if (args.argc < 2 || args.argc > 3) {
1001                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1002                 return -1;
1003         }
1004         
1005         if (!(client = ast_aji_get_client(args.sender))) {
1006                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1007                 return -1;
1008         }
1009         
1010         if (strchr(args.jid, '/')) {
1011                 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1012                 ASTOBJ_UNREF(client, aji_client_destroy);
1013                 return -1;
1014         } 
1015         if (!ast_strlen_zero(args.nick)) {
1016                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1017         } else {
1018                 if (client->component) {
1019                         sprintf(nick, "asterisk");
1020                 } else {
1021                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1022                 }
1023         }
1024         
1025         if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1026                 ast_aji_leave_chat(client, args.jid, nick);
1027         } 
1028         ASTOBJ_UNREF(client, aji_client_destroy);
1029         return 0;
1030 }
1031
1032 /*!
1033  * \internal
1034  * \brief Dial plan function to send a message.
1035  * \param chan ast_channel
1036  * \param data  Data is account,jid,message.
1037  * \retval 0 success
1038  * \retval -1 failure
1039  */
1040 static int aji_send_exec(struct ast_channel *chan, const char *data)
1041 {
1042         struct aji_client *client = NULL;
1043         char *s;
1044         AST_DECLARE_APP_ARGS(args,
1045                 AST_APP_ARG(sender);
1046                 AST_APP_ARG(recipient);
1047                 AST_APP_ARG(message);
1048         );
1049
1050         if (!data) {
1051                 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1052                 return -1;
1053         }
1054         s = ast_strdupa(data);
1055
1056         AST_STANDARD_APP_ARGS(args, s);
1057         if (args.argc < 3) {
1058                 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1059                 return -1;
1060         }
1061
1062         if (!(client = ast_aji_get_client(args.sender))) {
1063                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1064                 return -1;
1065         }
1066         if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message)) {
1067                 ast_aji_send_chat(client, args.recipient, args.message);
1068         }
1069         return 0;
1070 }
1071
1072 /*!
1073 * \brief Application to send a message to a groupchat.
1074 * \param chan ast_channel
1075 * \param data  Data is sender|groupchat|message.
1076 * \retval 0 success
1077 * \retval -1 error
1078 */
1079 static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
1080 {
1081         struct aji_client *client = NULL;
1082         char *s;
1083         char nick[AJI_MAX_RESJIDLEN];
1084         int res = 0;
1085         AST_DECLARE_APP_ARGS(args,
1086                 AST_APP_ARG(sender);
1087                 AST_APP_ARG(groupchat);
1088                 AST_APP_ARG(message);
1089                 AST_APP_ARG(nick);
1090         );
1091         
1092         if (!data) {
1093                 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1094                 return -1;
1095         }
1096         s = ast_strdupa(data);
1097         
1098         AST_STANDARD_APP_ARGS(args, s);
1099         if (args.argc < 3 || args.argc > 4) {
1100                 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1101                 return -1;
1102         }
1103         
1104         if (!(client = ast_aji_get_client(args.sender))) {
1105                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1106                 return -1;
1107         }
1108         
1109         if (ast_strlen_zero(args.nick) || args.argc == 3) {
1110                 if (client->component) {
1111                         sprintf(nick, "asterisk");
1112                 } else {
1113                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1114                 }
1115         } else {
1116                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1117         }
1118         
1119         if (strchr(args.groupchat, '@') && !ast_strlen_zero(args.message)) {
1120                 res = ast_aji_send_groupchat(client, nick, args.groupchat, args.message);
1121         }
1122         
1123         ASTOBJ_UNREF(client, aji_client_destroy);
1124         if (res != IKS_OK) {
1125                 return -1;
1126         }
1127         return 0;
1128 }
1129
1130 /*!
1131  * \internal
1132  * \brief Tests whether the connection is secured or not
1133  * \return 0 if the connection is not secured
1134  */
1135 static int aji_is_secure(struct aji_client *client)
1136 {
1137 #ifdef HAVE_OPENSSL
1138         return client->stream_flags & SECURE;
1139 #else
1140         return 0;
1141 #endif
1142 }
1143
1144 #ifdef HAVE_OPENSSL
1145 /*!
1146  * \internal
1147  * \brief Starts the TLS procedure
1148  * \param client the configured XMPP client we use to connect to a XMPP server
1149  * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
1150  * if OpenSSL is not installed
1151  */
1152 static int aji_start_tls(struct aji_client *client)
1153 {
1154         int ret;
1155
1156         /* This is sent not encrypted */
1157         ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
1158         if (ret)
1159                 return ret;
1160
1161         client->stream_flags |= TRY_SECURE;
1162         return IKS_OK;
1163 }
1164
1165 /*! 
1166  * \internal
1167  * \brief TLS handshake, OpenSSL initialization
1168  * \param client the configured XMPP client we use to connect to a XMPP server
1169  * \return IKS_OK on success, IKS_NET_TLSFAIL on failure 
1170  */
1171 static int aji_tls_handshake(struct aji_client *client)
1172 {
1173         int ret;
1174         int sock;
1175         
1176         ast_debug(1, "Starting TLS handshake\n"); 
1177
1178         /* Choose an SSL/TLS protocol version, create SSL_CTX */
1179         client->ssl_method = SSLv3_method();
1180         client->ssl_context = SSL_CTX_new(client->ssl_method);                
1181         if (!client->ssl_context)
1182                 return IKS_NET_TLSFAIL;
1183
1184         /* Create new SSL session */
1185         client->ssl_session = SSL_new(client->ssl_context);
1186         if (!client->ssl_session)
1187                 return IKS_NET_TLSFAIL;
1188
1189         /* Enforce TLS on our XMPP connection */
1190         sock = iks_fd(client->p);
1191         ret = SSL_set_fd(client->ssl_session, sock);
1192         if (!ret)
1193                 return IKS_NET_TLSFAIL;
1194
1195         /* Perform SSL handshake */
1196         ret = SSL_connect(client->ssl_session);
1197         if (!ret)
1198                 return IKS_NET_TLSFAIL;
1199
1200         client->stream_flags &= (~TRY_SECURE);
1201         client->stream_flags |= SECURE;
1202
1203         /* Sent over the established TLS connection */
1204         ret = aji_send_header(client, client->jid->server);
1205         if (ret != IKS_OK)
1206                 return IKS_NET_TLSFAIL;
1207
1208         ast_debug(1, "TLS started with server\n"); 
1209
1210         return IKS_OK;
1211 }
1212 #endif /* HAVE_OPENSSL */
1213
1214 /*! 
1215  * \internal
1216  * \brief Secured or unsecured IO socket receiving function
1217  * \param client the configured XMPP client we use to connect to a XMPP server
1218  * \param buffer the reception buffer
1219  * \param buf_len the size of the buffer
1220  * \param timeout the select timer
1221  * \retval the number of read bytes
1222  * \retval 0 timeout expiration
1223  * \retval -1 error
1224  */
1225 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
1226 {
1227         int sock;
1228         fd_set fds;
1229         struct timeval tv, *tvptr = NULL;
1230         int len, res;
1231
1232 #ifdef HAVE_OPENSSL
1233         if (aji_is_secure(client)) {
1234                 sock = SSL_get_fd(client->ssl_session);
1235                 if (sock < 0)
1236                         return -1;              
1237         } else
1238 #endif /* HAVE_OPENSSL */
1239                 sock = iks_fd(client->p);       
1240
1241         memset(&tv, 0, sizeof(struct timeval));
1242         FD_ZERO(&fds);
1243         FD_SET(sock, &fds);
1244         tv.tv_sec = timeout;
1245
1246         /* NULL value for tvptr makes ast_select wait indefinitely */
1247         tvptr = (timeout != -1) ? &tv : NULL;
1248
1249         /* ast_select emulates linux behaviour in terms of timeout handling */
1250         res = ast_select(sock + 1, &fds, NULL, NULL, tvptr);
1251         if (res > 0) {
1252 #ifdef HAVE_OPENSSL
1253                 if (aji_is_secure(client)) {
1254                         len = SSL_read(client->ssl_session, buffer, buf_len);
1255                 } else
1256 #endif /* HAVE_OPENSSL */
1257                         len = recv(sock, buffer, buf_len, 0);
1258
1259                 if (len > 0) {
1260                         return len;
1261                 } else if (len <= 0) {
1262                         return -1;
1263                 }
1264         }
1265         return res;
1266 }
1267
1268 /*! 
1269  * \internal
1270  * \brief Tries to receive data from the Jabber server
1271  * \param client the configured XMPP client we use to connect to a XMPP server
1272  * \param timeout the timeout value
1273  * This function receives (encrypted or unencrypted) data from the XMPP server,
1274  * and passes it to the parser.
1275  * \retval IKS_OK success
1276  * \retval IKS_NET_RWERR IO error
1277  * \retval IKS_NET_NOCONN no connection available
1278  * \retval IKS_NET_EXPIRED timeout expiration
1279  */
1280 static int aji_recv (struct aji_client *client, int timeout)
1281 {
1282         int len, ret;
1283         char buf[NET_IO_BUF_SIZE - 1];
1284         char newbuf[NET_IO_BUF_SIZE - 1];
1285         int pos = 0;
1286         int newbufpos = 0;
1287         unsigned char c;
1288
1289         memset(buf, 0, sizeof(buf));
1290         memset(newbuf, 0, sizeof(newbuf));
1291
1292         while (1) {
1293                 len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
1294                 if (len < 0) return IKS_NET_RWERR;
1295                 if (len == 0) return IKS_NET_EXPIRED;
1296                 buf[len] = '\0';
1297
1298                 /* our iksemel parser won't work as expected if we feed
1299                    it with XML packets that contain multiple whitespace 
1300                    characters between tags */
1301                 while (pos < len) {
1302                         c = buf[pos];
1303                         /* if we stumble on the ending tag character,
1304                            we skip any whitespace that follows it*/
1305                         if (c == '>') {
1306                                 while (isspace(buf[pos+1])) {
1307                                         pos++;
1308                                 }
1309                         }
1310                         newbuf[newbufpos] = c;
1311                         newbufpos ++;
1312                         pos++;
1313                 }
1314                 pos = 0;
1315                 newbufpos = 0;
1316
1317                 /* Log the message here, because iksemel's logHook is 
1318                    unaccessible */
1319                 aji_log_hook(client, buf, len, 1);
1320
1321                 /* let iksemel deal with the string length, 
1322                    and reset our buffer */
1323                 ret = iks_parse(client->p, newbuf, 0, 0);
1324                 memset(newbuf, 0, sizeof(newbuf));
1325
1326                 switch (ret) {
1327                 case IKS_NOMEM:
1328                         ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
1329                         break;
1330                 case IKS_BADXML:
1331                         ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
1332                         break;
1333                 case IKS_HOOK:
1334                         ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
1335                         break;
1336                 }
1337                 if (ret != IKS_OK) {
1338                         return ret;
1339                 }
1340                 ast_debug(3, "XML parsing successful\n");       
1341         }
1342         return IKS_OK;
1343 }
1344
1345 /*! 
1346  * \internal
1347  * \brief Sends XMPP header to the server
1348  * \param client the configured XMPP client we use to connect to a XMPP server
1349  * \param to the target XMPP server
1350  * \return IKS_OK on success, any other value on failure
1351  */
1352 static int aji_send_header(struct aji_client *client, const char *to)
1353 {
1354         char *msg;
1355         int len, err;
1356
1357         len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
1358         msg = iks_malloc(len);
1359         if (!msg)
1360                 return IKS_NOMEM;
1361         sprintf(msg, "<?xml version='1.0'?>"
1362                 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
1363                 "%s' to='%s' version='1.0'>", client->name_space, to);
1364         err = aji_send_raw(client, msg);
1365         iks_free(msg);
1366         if (err != IKS_OK)
1367                 return err;
1368
1369         return IKS_OK;
1370 }
1371
1372 /*! 
1373  * \brief Wraps raw sending
1374  * \param client the configured XMPP client we use to connect to a XMPP server
1375  * \param x the XMPP packet to send
1376  * \return IKS_OK on success, any other value on failure
1377  */
1378 int ast_aji_send(struct aji_client *client, iks *x)
1379 {
1380         return aji_send_raw(client, iks_string(iks_stack(x), x));
1381 }
1382
1383 /*! 
1384  * \internal
1385  * \brief Sends an XML string over an XMPP connection
1386  * \param client the configured XMPP client we use to connect to a XMPP server
1387  * \param xmlstr the XML string to send
1388  * The XML data is sent whether the connection is secured or not. In the 
1389  * latter case, we just call iks_send_raw().
1390  * \return IKS_OK on success, any other value on failure
1391  */
1392 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
1393 {
1394         int ret;
1395 #ifdef HAVE_OPENSSL
1396         int len = strlen(xmlstr);
1397
1398         if (aji_is_secure(client)) {
1399                 ret = SSL_write(client->ssl_session, xmlstr, len);
1400                 if (ret) {
1401                         /* Log the message here, because iksemel's logHook is 
1402                            unaccessible */
1403                         aji_log_hook(client, xmlstr, len, 0);
1404                         return IKS_OK;
1405                 }
1406         }
1407 #endif
1408         /* If needed, data will be sent unencrypted, and logHook will 
1409            be called inside iks_send_raw */
1410         ret = iks_send_raw(client->p, xmlstr);
1411         if (ret != IKS_OK)
1412                 return ret;     
1413
1414         return IKS_OK;
1415 }
1416
1417 /*!
1418  * \internal
1419  * \brief the debug loop.
1420  * \param data void
1421  * \param xmpp xml data as string
1422  * \param size size of string
1423  * \param is_incoming direction of packet 1 for inbound 0 for outbound.
1424  */
1425 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
1426 {
1427         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1428
1429         if (!ast_strlen_zero(xmpp))
1430                 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
1431
1432         if (client->debug) {
1433                 if (is_incoming)
1434                         ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
1435                 else {
1436                         if (strlen(xmpp) == 1) {
1437                                 if (option_debug > 2  && xmpp[0] == ' ') {
1438                                         ast_verbose("\nJABBER: Keep alive packet\n");
1439                                 }
1440                         } else
1441                                 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
1442                 }
1443
1444         }
1445         ASTOBJ_UNREF(client, aji_client_destroy);
1446 }
1447
1448 /*!
1449  * \internal
1450  * \brief A wrapper function for iks_start_sasl
1451  * \param client the configured XMPP client we use to connect to a XMPP server
1452  * \param type the SASL authentication type. Supported types are PLAIN and MD5
1453  * \param username
1454  * \param pass password.
1455  *
1456  * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
1457  */
1458 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
1459 {
1460         iks *x = NULL;
1461         int len;
1462         char *s;
1463         char *base64;
1464
1465         /* trigger SASL DIGEST-MD5 only over an unsecured connection.
1466            iks_start_sasl is an iksemel API function and relies on GnuTLS,
1467            whereas we use OpenSSL */
1468         if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
1469                 return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass); 
1470         if (!(type & IKS_STREAM_SASL_PLAIN)) {
1471                 ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
1472                 return IKS_NET_NOTSUPP;
1473         }
1474
1475         x = iks_new("auth"); 
1476         if (!x) {
1477                 ast_log(LOG_ERROR, "Out of memory.\n");
1478                 return IKS_NET_NOTSUPP;
1479         }
1480
1481         iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
1482         len = strlen(username) + strlen(pass) + 3;
1483         s = alloca(len);
1484         base64 = alloca((len + 2) * 4 / 3);
1485         iks_insert_attrib(x, "mechanism", "PLAIN");
1486         snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
1487
1488         /* exclude the NULL training byte from the base64 encoding operation
1489            as some XMPP servers will refuse it.
1490            The format for authentication is [authzid]\0authcid\0password
1491            not [authzid]\0authcid\0password\0 */
1492         ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
1493         iks_insert_cdata(x, base64, 0);
1494         ast_aji_send(client, x);
1495         iks_delete(x);
1496
1497         return IKS_OK;
1498 }
1499
1500 /*!
1501  * \internal
1502  * \brief The action hook parses the inbound packets, constantly running.
1503  * \param data aji client structure 
1504  * \param type type of packet 
1505  * \param node the actual packet.
1506  * \return IKS_OK or IKS_HOOK .
1507  */
1508 static int aji_act_hook(void *data, int type, iks *node)
1509 {
1510         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1511         ikspak *pak = NULL;
1512         iks *auth = NULL;
1513         int features = 0;
1514
1515         if (!node) {
1516                 ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
1517                 ASTOBJ_UNREF(client, aji_client_destroy);
1518                 return IKS_HOOK;
1519         }
1520
1521         if (client->state == AJI_DISCONNECTING) {
1522                 ASTOBJ_UNREF(client, aji_client_destroy);
1523                 return IKS_HOOK;
1524         }
1525
1526         pak = iks_packet(node);
1527
1528         if (!client->component) { /*client */
1529                 switch (type) {
1530                 case IKS_NODE_START:
1531                         if (client->usetls && !aji_is_secure(client)) {
1532 #ifndef HAVE_OPENSSL
1533                                 ast_log(LOG_ERROR, "OpenSSL not installed. You need to install OpenSSL on this system, or disable the TLS option in your configuration file\n");
1534                                 ASTOBJ_UNREF(client, aji_client_destroy);
1535                                 return IKS_HOOK;
1536 #else
1537                                 if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
1538                                         ast_log(LOG_ERROR, "Could not start TLS\n");
1539                                         ASTOBJ_UNREF(client, aji_client_destroy);
1540                                         return IKS_HOOK;                
1541                                 }
1542 #endif
1543                                 break;
1544                         }
1545                         if (!client->usesasl) {
1546                                 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);
1547                                 auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
1548                                 if (auth) {
1549                                         iks_insert_attrib(auth, "id", client->mid);
1550                                         iks_insert_attrib(auth, "to", client->jid->server);
1551                                         ast_aji_increment_mid(client->mid);
1552                                         ast_aji_send(client, auth);
1553                                         iks_delete(auth);
1554                                 } else
1555                                         ast_log(LOG_ERROR, "Out of memory.\n");
1556                         }
1557                         break;
1558
1559                 case IKS_NODE_NORMAL:
1560 #ifdef HAVE_OPENSSL
1561                         if (client->stream_flags & TRY_SECURE) {
1562                                 if (!strcmp("proceed", iks_name(node))) {
1563                                         return aji_tls_handshake(client);
1564                                 }
1565                         }
1566 #endif
1567                         if (!strcmp("stream:features", iks_name(node))) {
1568                                 features = iks_stream_features(node);
1569                                 if (client->usesasl) {
1570                                         if (client->usetls && !aji_is_secure(client))
1571                                                 break;
1572                                         if (client->authorized) {
1573                                                 if (features & IKS_STREAM_BIND) {
1574                                                         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);
1575                                                         auth = iks_make_resource_bind(client->jid);
1576                                                         if (auth) {
1577                                                                 iks_insert_attrib(auth, "id", client->mid);
1578                                                                 ast_aji_increment_mid(client->mid);
1579                                                                 ast_aji_send(client, auth);
1580                                                                 iks_delete(auth);
1581                                                         } else {
1582                                                                 ast_log(LOG_ERROR, "Out of memory.\n");
1583                                                                 break;
1584                                                         }
1585                                                 }
1586                                                 if (features & IKS_STREAM_SESSION) {
1587                                                         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);
1588                                                         auth = iks_make_session();
1589                                                         if (auth) {
1590                                                                 iks_insert_attrib(auth, "id", "auth");
1591                                                                 ast_aji_increment_mid(client->mid);
1592                                                                 ast_aji_send(client, auth);
1593                                                                 iks_delete(auth);
1594                                                         } else {
1595                                                                 ast_log(LOG_ERROR, "Out of memory.\n");
1596                                                         }
1597                                                 }
1598                                         } else {
1599                                                 int ret;
1600                                                 if (!client->jid->user) {
1601                                                         ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
1602                                                         break;
1603                                                 }
1604
1605                                                 ret = aji_start_sasl(client, features, client->jid->user, client->password);
1606                                                 if (ret != IKS_OK) {
1607                                                         ASTOBJ_UNREF(client, aji_client_destroy);
1608                                                         return IKS_HOOK;
1609                                                 }
1610                                                 break;
1611                                         }
1612                                 }
1613                         } else if (!strcmp("failure", iks_name(node))) {
1614                                 ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
1615                         } else if (!strcmp("success", iks_name(node))) {
1616                                 client->authorized = 1;
1617                                 aji_send_header(client, client->jid->server);
1618                         }
1619                         break;
1620                 case IKS_NODE_ERROR: 
1621                                 ast_log(LOG_ERROR, "JABBER: Node Error\n");
1622                                 ASTOBJ_UNREF(client, aji_client_destroy);
1623                                 return IKS_HOOK;
1624                                 break;
1625                 case IKS_NODE_STOP: 
1626                                 ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1627                                 ASTOBJ_UNREF(client, aji_client_destroy);
1628                                 return IKS_HOOK;
1629                                 break;
1630                 }
1631         } else if (client->state != AJI_CONNECTED && client->component) {
1632                 switch (type) {
1633                 case IKS_NODE_START:
1634                         if (client->state == AJI_DISCONNECTED) {
1635                                 char secret[160], shasum[320], *handshake;
1636
1637                                 sprintf(secret, "%s%s", pak->id, client->password);
1638                                 ast_sha1_hash(shasum, secret);
1639                                 handshake = NULL;
1640                                 if (asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
1641                                         aji_send_raw(client, handshake);
1642                                         ast_free(handshake);
1643                                         handshake = NULL;
1644                                 }
1645                                 client->state = AJI_CONNECTING;
1646                                 if (aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
1647                                         client->state = AJI_CONNECTED;
1648                                 else
1649                                         ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
1650                                 break;
1651                         }
1652                         break;
1653
1654                 case IKS_NODE_NORMAL:
1655                         break;
1656
1657                 case IKS_NODE_ERROR:
1658                         ast_log(LOG_ERROR, "JABBER: Node Error\n");
1659                         ASTOBJ_UNREF(client, aji_client_destroy);
1660                         return IKS_HOOK;
1661
1662                 case IKS_NODE_STOP:
1663                         ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1664                         ASTOBJ_UNREF(client, aji_client_destroy);
1665                         return IKS_HOOK;
1666                 }
1667         }
1668
1669         switch (pak->type) {
1670         case IKS_PAK_NONE:
1671                 ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1672                 break;
1673         case IKS_PAK_MESSAGE:
1674                 aji_handle_message(client, pak);
1675                 ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1676                 break;
1677         case IKS_PAK_PRESENCE:
1678                 aji_handle_presence(client, pak);
1679                 ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1680                 break;
1681         case IKS_PAK_S10N:
1682                 aji_handle_subscribe(client, pak);
1683                 ast_debug(1, "JABBER: Handling paktype S10N\n");
1684                 break;
1685         case IKS_PAK_IQ:
1686                 ast_debug(1, "JABBER: Handling paktype IQ\n");
1687                 aji_handle_iq(client, node);
1688                 break;
1689         default:
1690                 ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
1691                 break;
1692         }
1693         
1694         iks_filter_packet(client->f, pak);
1695
1696         if (node)
1697                 iks_delete(node);
1698
1699         ASTOBJ_UNREF(client, aji_client_destroy);
1700         return IKS_OK;
1701 }
1702 /*!
1703  * \internal
1704  * \brief Unknown
1705  * \param data void
1706  * \param pak ikspak
1707  * \return IKS_FILTER_EAT.
1708 */
1709 static int aji_register_approve_handler(void *data, ikspak *pak)
1710 {
1711         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1712         iks *iq = NULL, *presence = NULL, *x = NULL;
1713
1714         iq = iks_new("iq");
1715         presence = iks_new("presence");
1716         x = iks_new("x");
1717         if (client && iq && presence && x) {
1718                 if (!iks_find(pak->query, "remove")) {
1719                         iks_insert_attrib(iq, "from", client->jid->full);
1720                         iks_insert_attrib(iq, "to", pak->from->full);
1721                         iks_insert_attrib(iq, "id", pak->id);
1722                         iks_insert_attrib(iq, "type", "result");
1723                         ast_aji_send(client, iq);
1724
1725                         iks_insert_attrib(presence, "from", client->jid->full);
1726                         iks_insert_attrib(presence, "to", pak->from->partial);
1727                         iks_insert_attrib(presence, "id", client->mid);
1728                         ast_aji_increment_mid(client->mid);
1729                         iks_insert_attrib(presence, "type", "subscribe");
1730                         iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
1731                         iks_insert_node(presence, x);
1732                         ast_aji_send(client, presence); 
1733                 }
1734         } else {
1735                 ast_log(LOG_ERROR, "Out of memory.\n");
1736         }
1737
1738
1739         iks_delete(iq);
1740         iks_delete(presence);
1741         iks_delete(x);
1742         
1743         ASTOBJ_UNREF(client, aji_client_destroy);
1744         return IKS_FILTER_EAT;
1745 }
1746 /*!
1747  * \internal
1748  * \brief register handler for incoming querys (IQ's)
1749  * \param data incoming aji_client request
1750  * \param pak ikspak
1751  * \return IKS_FILTER_EAT.
1752 */
1753 static int aji_register_query_handler(void *data, ikspak *pak)
1754 {
1755         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1756         struct aji_buddy *buddy = NULL; 
1757         char *node = NULL;
1758         iks *iq = NULL, *query = NULL;
1759
1760         client = (struct aji_client *) data;
1761
1762         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1763         if (!buddy) {
1764                 iks  *error = NULL, *notacceptable = NULL;
1765
1766                 ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
1767                 iq = iks_new("iq");
1768                 query = iks_new("query");
1769                 error = iks_new("error");
1770                 notacceptable = iks_new("not-acceptable");
1771                 if (iq && query && error && notacceptable) {
1772                         iks_insert_attrib(iq, "type", "error");
1773                         iks_insert_attrib(iq, "from", client->user);
1774                         iks_insert_attrib(iq, "to", pak->from->full);
1775                         iks_insert_attrib(iq, "id", pak->id);
1776                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1777                         iks_insert_attrib(error, "code" , "406");
1778                         iks_insert_attrib(error, "type", "modify");
1779                         iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1780                         iks_insert_node(iq, query);
1781                         iks_insert_node(iq, error);
1782                         iks_insert_node(error, notacceptable);
1783                         ast_aji_send(client, iq);
1784                 } else {
1785                         ast_log(LOG_ERROR, "Out of memory.\n");
1786                 }
1787
1788                 iks_delete(error);
1789                 iks_delete(notacceptable);
1790         } else  if (!(node = iks_find_attrib(pak->query, "node"))) {
1791                 iks *instructions = NULL;
1792                 char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
1793                 iq = iks_new("iq");
1794                 query = iks_new("query");
1795                 instructions = iks_new("instructions");
1796                 if (iq && query && instructions && client) {
1797                         iks_insert_attrib(iq, "from", client->user);
1798                         iks_insert_attrib(iq, "to", pak->from->full);
1799                         iks_insert_attrib(iq, "id", pak->id);
1800                         iks_insert_attrib(iq, "type", "result");
1801                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1802                         iks_insert_cdata(instructions, explain, 0);
1803                         iks_insert_node(iq, query);
1804                         iks_insert_node(query, instructions);
1805                         ast_aji_send(client, iq);
1806                 } else {
1807                         ast_log(LOG_ERROR, "Out of memory.\n");
1808                 }
1809
1810                 iks_delete(instructions);
1811         }
1812         iks_delete(iq);
1813         iks_delete(query);
1814         ASTOBJ_UNREF(client, aji_client_destroy);
1815         return IKS_FILTER_EAT;
1816 }
1817
1818 /*!
1819  * \internal
1820  * \brief Handles stuff
1821  * \param data void
1822  * \param pak ikspak 
1823  * \return IKS_FILTER_EAT.
1824 */
1825 static int aji_ditems_handler(void *data, ikspak *pak)
1826 {
1827         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1828         char *node = NULL;
1829
1830         if (!(node = iks_find_attrib(pak->query, "node"))) {
1831                 iks *iq = NULL, *query = NULL, *item = NULL;
1832                 iq = iks_new("iq");
1833                 query = iks_new("query");
1834                 item = iks_new("item");
1835
1836                 if (iq && query && item) {
1837                         iks_insert_attrib(iq, "from", client->user);
1838                         iks_insert_attrib(iq, "to", pak->from->full);
1839                         iks_insert_attrib(iq, "id", pak->id);
1840                         iks_insert_attrib(iq, "type", "result");
1841                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1842                         iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
1843                         iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
1844                         iks_insert_attrib(item, "jid", client->user);
1845
1846                         iks_insert_node(iq, query);
1847                         iks_insert_node(query, item);
1848                         ast_aji_send(client, iq);
1849                 } else {
1850                         ast_log(LOG_ERROR, "Out of memory.\n");
1851                 }
1852
1853                 iks_delete(iq);
1854                 iks_delete(query);
1855                 iks_delete(item);
1856
1857         } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
1858                 iks *iq, *query, *confirm;
1859                 iq = iks_new("iq");
1860                 query = iks_new("query");
1861                 confirm = iks_new("item");
1862                 if (iq && query && confirm && client) {
1863                         iks_insert_attrib(iq, "from", client->user);
1864                         iks_insert_attrib(iq, "to", pak->from->full);
1865                         iks_insert_attrib(iq, "id", pak->id);
1866                         iks_insert_attrib(iq, "type", "result");
1867                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1868                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1869                         iks_insert_attrib(confirm, "node", "confirmaccount");
1870                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
1871                         iks_insert_attrib(confirm, "jid", "blog.astjab.org");
1872
1873                         iks_insert_node(iq, query);
1874                         iks_insert_node(query, confirm);
1875                         ast_aji_send(client, iq);
1876                 } else {
1877                         ast_log(LOG_ERROR, "Out of memory.\n");
1878                 }
1879
1880                 iks_delete(iq);
1881                 iks_delete(query);
1882                 iks_delete(confirm);
1883
1884         } else if (!strcasecmp(node, "confirmaccount")) {
1885                 iks *iq = NULL, *query = NULL, *feature = NULL;
1886
1887                 iq = iks_new("iq");
1888                 query = iks_new("query");
1889                 feature = iks_new("feature");
1890
1891                 if (iq && query && feature && client) {
1892                         iks_insert_attrib(iq, "from", client->user);
1893                         iks_insert_attrib(iq, "to", pak->from->full);
1894                         iks_insert_attrib(iq, "id", pak->id);
1895                         iks_insert_attrib(iq, "type", "result");
1896                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1897                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
1898                         iks_insert_node(iq, query);
1899                         iks_insert_node(query, feature);
1900                         ast_aji_send(client, iq);
1901                 } else {
1902                         ast_log(LOG_ERROR, "Out of memory.\n");
1903                 }
1904
1905                 iks_delete(iq);
1906                 iks_delete(query);
1907                 iks_delete(feature);
1908         }
1909
1910         ASTOBJ_UNREF(client, aji_client_destroy);
1911         return IKS_FILTER_EAT;
1912
1913 }
1914
1915 /*!
1916  * \internal
1917  * \brief Handle add extra info
1918  * \param data void
1919  * \param pak ikspak
1920  * \return IKS_FILTER_EAT
1921 */
1922 static int aji_client_info_handler(void *data, ikspak *pak)
1923 {
1924         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1925         struct aji_resource *resource = NULL;
1926         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1927
1928         resource = aji_find_resource(buddy, pak->from->resource);
1929         if (pak->subtype == IKS_TYPE_RESULT) {
1930                 if (!resource) {
1931                         ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
1932                         ASTOBJ_UNREF(client, aji_client_destroy);
1933                         return IKS_FILTER_EAT;
1934                 }
1935                 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
1936                         resource->cap->jingle = 1;
1937                 } else
1938                         resource->cap->jingle = 0;
1939         } else if (pak->subtype == IKS_TYPE_GET) {
1940                 iks *iq, *disco, *ident, *google, *query;
1941                 iq = iks_new("iq");
1942                 query = iks_new("query");
1943                 ident = iks_new("identity");
1944                 disco = iks_new("feature");
1945                 google = iks_new("feature");
1946                 if (iq && ident && disco && google) {
1947                         iks_insert_attrib(iq, "from", client->jid->full);
1948                         iks_insert_attrib(iq, "to", pak->from->full);
1949                         iks_insert_attrib(iq, "type", "result");
1950                         iks_insert_attrib(iq, "id", pak->id);
1951                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
1952                         iks_insert_attrib(ident, "category", "client");
1953                         iks_insert_attrib(ident, "type", "pc");
1954                         iks_insert_attrib(ident, "name", "asterisk");
1955                         iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
1956                         iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
1957                         iks_insert_node(iq, query);
1958                         iks_insert_node(query, ident);
1959                         iks_insert_node(query, google);
1960                         iks_insert_node(query, disco);
1961                         ast_aji_send(client, iq);
1962                 } else
1963                         ast_log(LOG_ERROR, "Out of Memory.\n");
1964
1965                 iks_delete(iq);
1966                 iks_delete(query);
1967                 iks_delete(ident);
1968                 iks_delete(google);
1969                 iks_delete(disco);
1970         } else if (pak->subtype == IKS_TYPE_ERROR) {
1971                 ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
1972         }
1973         ASTOBJ_UNREF(client, aji_client_destroy);
1974         return IKS_FILTER_EAT;
1975 }
1976
1977 /*!
1978  * \internal
1979  * \brief Handler of the return info packet
1980  * \param data aji_client
1981  * \param pak ikspak
1982  * \return IKS_FILTER_EAT
1983 */
1984 static int aji_dinfo_handler(void *data, ikspak *pak)
1985 {
1986         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1987         char *node = NULL;
1988         struct aji_resource *resource = NULL;
1989         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1990
1991         resource = aji_find_resource(buddy, pak->from->resource);
1992         if (pak->subtype == IKS_TYPE_ERROR) {
1993                 ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
1994                 return IKS_FILTER_EAT;
1995         }
1996         if (pak->subtype == IKS_TYPE_RESULT) {
1997                 if (!resource) {
1998                         ast_log(LOG_NOTICE,"JABBER: Received client info from %s when not requested.\n", pak->from->full);
1999                         ASTOBJ_UNREF(client, aji_client_destroy);
2000                         return IKS_FILTER_EAT;
2001                 }
2002                 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2003                         resource->cap->jingle = 1;
2004                 } else
2005                         resource->cap->jingle = 0;
2006         } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
2007                 iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
2008
2009                 iq = iks_new("iq");
2010                 query = iks_new("query");
2011                 identity = iks_new("identity");
2012                 disco = iks_new("feature");
2013                 reg = iks_new("feature");
2014                 commands = iks_new("feature");
2015                 gateway = iks_new("feature");
2016                 version = iks_new("feature");
2017                 vcard = iks_new("feature");
2018                 search = iks_new("feature");
2019
2020                 if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
2021                         iks_insert_attrib(iq, "from", client->user);
2022                         iks_insert_attrib(iq, "to", pak->from->full);
2023                         iks_insert_attrib(iq, "id", pak->id);
2024                         iks_insert_attrib(iq, "type", "result");
2025                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2026                         iks_insert_attrib(identity, "category", "gateway");
2027                         iks_insert_attrib(identity, "type", "pstn");
2028                         iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2029                         iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2030                         iks_insert_attrib(reg, "var", "jabber:iq:register");
2031                         iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2032                         iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2033                         iks_insert_attrib(version, "var", "jabber:iq:version");
2034                         iks_insert_attrib(vcard, "var", "vcard-temp");
2035                         iks_insert_attrib(search, "var", "jabber:iq:search");
2036
2037                         iks_insert_node(iq, query);
2038                         iks_insert_node(query, identity);
2039                         iks_insert_node(query, disco);
2040                         iks_insert_node(query, reg);
2041                         iks_insert_node(query, commands);
2042                         iks_insert_node(query, gateway);
2043                         iks_insert_node(query, version);
2044                         iks_insert_node(query, vcard);
2045                         iks_insert_node(query, search);
2046                         ast_aji_send(client, iq);
2047                 } else {
2048                         ast_log(LOG_ERROR, "Out of memory.\n");
2049                 }
2050
2051                 iks_delete(iq);
2052                 iks_delete(query);
2053                 iks_delete(identity);
2054                 iks_delete(disco);
2055                 iks_delete(reg);
2056                 iks_delete(commands);
2057                 iks_delete(gateway);
2058                 iks_delete(version);
2059                 iks_delete(vcard);
2060                 iks_delete(search);
2061
2062         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
2063                 iks *iq, *query, *confirm;
2064                 iq = iks_new("iq");
2065                 query = iks_new("query");
2066                 confirm = iks_new("item");
2067
2068                 if (iq && query && confirm && client) {
2069                         iks_insert_attrib(iq, "from", client->user);
2070                         iks_insert_attrib(iq, "to", pak->from->full);
2071                         iks_insert_attrib(iq, "id", pak->id);
2072                         iks_insert_attrib(iq, "type", "result");
2073                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2074                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2075                         iks_insert_attrib(confirm, "node", "confirmaccount");
2076                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
2077                         iks_insert_attrib(confirm, "jid", client->user);
2078                         iks_insert_node(iq, query);
2079                         iks_insert_node(query, confirm);
2080                         ast_aji_send(client, iq);
2081                 } else {
2082                         ast_log(LOG_ERROR, "Out of memory.\n");
2083                 }
2084
2085                 iks_delete(iq);
2086                 iks_delete(query);
2087                 iks_delete(confirm);
2088
2089         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
2090                 iks *iq, *query, *feature;
2091
2092                 iq = iks_new("iq");
2093                 query = iks_new("query");
2094                 feature = iks_new("feature");
2095
2096                 if (iq && query && feature && client) {
2097                         iks_insert_attrib(iq, "from", client->user);
2098                         iks_insert_attrib(iq, "to", pak->from->full);
2099                         iks_insert_attrib(iq, "id", pak->id);
2100                         iks_insert_attrib(iq, "type", "result");
2101                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2102                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2103                         iks_insert_node(iq, query);
2104                         iks_insert_node(query, feature);
2105                         ast_aji_send(client, iq);
2106                 } else {
2107                         ast_log(LOG_ERROR, "Out of memory.\n");
2108                 }
2109
2110                 iks_delete(iq);
2111                 iks_delete(query);
2112                 iks_delete(feature);
2113         }
2114
2115         ASTOBJ_UNREF(client, aji_client_destroy);
2116         return IKS_FILTER_EAT;
2117 }
2118
2119 /*!
2120  * \internal
2121  * \brief Handles \verbatim <iq> \endverbatim stanzas.
2122  * \param client the configured XMPP client we use to connect to a XMPP server
2123  * \param node iks
2124  * \return void.
2125  */
2126 static void aji_handle_iq(struct aji_client *client, iks *node)
2127 {
2128         /*Nothing to see here */
2129 }
2130
2131 /*!
2132  * \internal
2133  * \brief Handles \verbatim <message>\endverbatim stanzas.
2134  * Adds the incoming message to the client's message list.
2135  * \param client the configured XMPP client we use to connect to a XMPP server
2136  * \param pak ikspak the node
2137  */
2138 static void aji_handle_message(struct aji_client *client, ikspak *pak)
2139 {
2140         struct aji_message *insert;
2141         int deleted = 0;
2142
2143         ast_debug(3, "client %s received a message\n", client->name);
2144
2145         if (!(insert = ast_calloc(1, sizeof(*insert)))) {
2146                 return;
2147         }
2148
2149         insert->arrived = ast_tvnow();
2150
2151         /* wake up threads waiting for messages */
2152         ast_mutex_lock(&messagelock);
2153         ast_cond_broadcast(&message_received_condition);
2154         ast_mutex_unlock(&messagelock);
2155
2156         if (iks_find_cdata(pak->x, "body")) {
2157                 insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2158         }
2159         if (pak->id) {
2160                 ast_copy_string(insert->id, pak->id, sizeof(insert->id));
2161         }
2162         if (pak->from){
2163                 /* insert will furtherly be added to message list */
2164                 insert->from = ast_strdup(pak->from->full);
2165                 if (!insert->from) {
2166                         ast_log(LOG_ERROR, "Memory allocation failure\n");
2167                         return;
2168                 }
2169                 ast_debug(3, "message comes from %s\n", insert->from);
2170         }
2171
2172         /* remove old messages received from this JID
2173          * and insert received message */
2174         deleted = delete_old_messages(client, pak->from->partial);
2175         ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
2176         AST_LIST_LOCK(&client->messages);
2177         AST_LIST_INSERT_HEAD(&client->messages, insert, list);
2178         AST_LIST_UNLOCK(&client->messages);
2179 }
2180
2181 /*!
2182  * \internal
2183  * \brief handles \verbatim <presence>\endverbatim stanzas.
2184  * \param client the configured XMPP client we use to connect to a XMPP server
2185  * \param pak ikspak
2186  */
2187 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
2188 {
2189         int status, priority;
2190         struct aji_buddy *buddy;
2191         struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
2192         char *ver, *node, *descrip, *type;
2193
2194         if (client->state != AJI_CONNECTED)
2195                 aji_create_buddy(pak->from->partial, client);
2196
2197         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2198         if (!buddy && pak->from->partial) {
2199                 /* allow our jid to be used to log in with another resource */
2200                 if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
2201                         aji_create_buddy(pak->from->partial, client);
2202                 else
2203                         ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
2204                 return;
2205         }
2206         type = iks_find_attrib(pak->x, "type");
2207         if (client->component && type &&!strcasecmp("probe", type)) {
2208                 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2209                 ast_verbose("what i was looking for \n");
2210         }
2211         ASTOBJ_WRLOCK(buddy);
2212         status = (pak->show) ? pak->show : 6;
2213         priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
2214         tmp = buddy->resources;
2215         descrip = ast_strdup(iks_find_cdata(pak->x,"status"));
2216
2217         while (tmp && pak->from->resource) {
2218                 if (!strcasecmp(tmp->resource, pak->from->resource)) {
2219                         tmp->status = status;
2220                         if (tmp->description) ast_free(tmp->description);
2221                         tmp->description = descrip;
2222                         found = tmp;
2223                         if (status == 6) {      /* Sign off Destroy resource */
2224                                 if (last && found->next) {
2225                                         last->next = found->next;
2226                                 } else if (!last) {
2227                                         if (found->next)
2228                                                 buddy->resources = found->next;
2229                                         else
2230                                                 buddy->resources = NULL;
2231                                 } else if (!found->next) {
2232                                         if (last)
2233                                                 last->next = NULL;
2234                                         else
2235                                                 buddy->resources = NULL;
2236                                 }
2237                                 ast_free(found);
2238                                 found = NULL;
2239                                 break;
2240                         }
2241                         /* resource list is sorted by descending priority */
2242                         if (tmp->priority != priority) {
2243                                 found->priority = priority;
2244                                 if (!last && !found->next)
2245                                         /* resource was found to be unique,
2246                                            leave loop */
2247                                         break;
2248                                 /* search for resource in our list
2249                                    and take it out for the moment */
2250                                 if (last)
2251                                         last->next = found->next;
2252                                 else
2253                                         buddy->resources = found->next;
2254
2255                                 last = NULL;
2256                                 tmp = buddy->resources;
2257                                 if (!buddy->resources)
2258                                         buddy->resources = found;
2259                                 /* priority processing */
2260                                 while (tmp) {
2261                                         /* insert resource back according to 
2262                                            its priority value */
2263                                         if (found->priority > tmp->priority) {
2264                                                 if (last)
2265                                                         /* insert within list */
2266                                                         last->next = found;
2267                                                 found->next = tmp;
2268                                                 if (!last)
2269                                                         /* insert on top */
2270                                                         buddy->resources = found;
2271                                                 break;
2272                                         }
2273                                         if (!tmp->next) {
2274                                                 /* insert at the end of the list */
2275                                                 tmp->next = found;
2276                                                 found->next = NULL;
2277                                                 break;
2278                                         }
2279                                         last = tmp;
2280                                         tmp = tmp->next;
2281                                 }
2282                         }
2283                         break;
2284                 }
2285                 last = tmp;
2286                 tmp = tmp->next;
2287         }
2288
2289         /* resource not found in our list, create it */
2290         if (!found && status != 6 && pak->from->resource) {
2291                 found = ast_calloc(1, sizeof(*found));
2292
2293                 if (!found) {
2294                         ast_log(LOG_ERROR, "Out of memory!\n");
2295                         return;
2296                 }
2297                 ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
2298                 found->status = status;
2299                 found->description = descrip;
2300                 found->priority = priority;
2301                 found->next = NULL;
2302                 last = NULL;
2303                 tmp = buddy->resources;
2304                 while (tmp) {
2305                         if (found->priority > tmp->priority) {
2306                                 if (last)
2307                                         last->next = found;
2308                                 found->next = tmp;
2309                                 if (!last)
2310                                         buddy->resources = found;
2311                                 break;
2312                         }
2313                         if (!tmp->next) {
2314                                 tmp->next = found;
2315                                 break;
2316                         }
2317                         last = tmp;
2318                         tmp = tmp->next;
2319                 }
2320                 if (!tmp)
2321                         buddy->resources = found;
2322         }
2323         
2324         ASTOBJ_UNLOCK(buddy);
2325         ASTOBJ_UNREF(buddy, aji_buddy_destroy);
2326
2327         node = iks_find_attrib(iks_find(pak->x, "c"), "node");
2328         ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
2329
2330         /* handle gmail client's special caps:c tag */
2331         if (!node && !ver) {
2332                 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
2333                 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
2334         }
2335
2336         /* retrieve capabilites of the new resource */
2337         if (status != 6 && found && !found->cap) {
2338                 found->cap = aji_find_version(node, ver, pak);
2339                 if (gtalk_yuck(pak->x)) /* gtalk should do discover */
2340                         found->cap->jingle = 1;
2341                 if (found->cap->jingle && option_debug > 4) {
2342                         ast_debug(1,"Special case for google till they support discover.\n");
2343                 }
2344                 else {
2345                         iks *iq, *query;
2346                         iq = iks_new("iq");
2347                         query = iks_new("query");
2348                         if (query && iq)  {
2349                                 iks_insert_attrib(iq, "type", "get");
2350                                 iks_insert_attrib(iq, "to", pak->from->full);
2351                                 iks_insert_attrib(iq,"from", client->jid->full);
2352                                 iks_insert_attrib(iq, "id", client->mid);
2353                                 ast_aji_increment_mid(client->mid);
2354                                 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2355                                 iks_insert_node(iq, query);
2356                                 ast_aji_send(client, iq);
2357                                 
2358                         } else
2359                                 ast_log(LOG_ERROR, "Out of memory.\n");
2360                         
2361                         iks_delete(query);
2362                         iks_delete(iq);
2363                 }
2364         }
2365         switch (pak->subtype) {
2366         case IKS_TYPE_AVAILABLE:
2367                 ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
2368                 break;
2369         case IKS_TYPE_UNAVAILABLE:
2370                 ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
2371                 break;
2372         default:
2373                 ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
2374         }
2375         switch (pak->show) {
2376         case IKS_SHOW_UNAVAILABLE:
2377                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2378                 break;
2379         case IKS_SHOW_AVAILABLE:
2380                 ast_debug(3, "JABBER: type is available\n");
2381                 break;
2382         case IKS_SHOW_CHAT:
2383                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2384                 break;
2385         case IKS_SHOW_AWAY:
2386                 ast_debug(3, "JABBER: type is away\n");
2387                 break;
2388         case IKS_SHOW_XA:
2389                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2390                 break;
2391         case IKS_SHOW_DND:
2392                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2393                 break;
2394         default:
2395                 ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
2396         }
2397 }
2398
2399 /*!
2400  * \internal
2401  * \brief handles subscription requests.
2402  * \param client the configured XMPP client we use to connect to a XMPP server
2403  * \param pak ikspak iksemel packet.
2404  * \return void.
2405  */
2406 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
2407 {
2408         iks *presence = NULL, *status = NULL;
2409         struct aji_buddy* buddy = NULL;
2410
2411         switch (pak->subtype) { 
2412         case IKS_TYPE_SUBSCRIBE:
2413                 if (ast_test_flag(&client->flags, AJI_AUTOACCEPT)) {
2414                         presence = iks_new("presence");
2415                         status = iks_new("status");
2416                         if (presence && status) {
2417                                 iks_insert_attrib(presence, "type", "subscribed");
2418                                 iks_insert_attrib(presence, "to", pak->from->full);
2419                                 iks_insert_attrib(presence, "from", client->jid->full);
2420                                 if (pak->id)
2421                                         iks_insert_attrib(presence, "id", pak->id);
2422                                 iks_insert_cdata(status, "Asterisk has approved subscription", 0);
2423                                 iks_insert_node(presence, status);
2424                                 ast_aji_send(client, presence);
2425                         } else
2426                                 ast_log(LOG_ERROR, "Unable to allocate nodes\n");
2427
2428                         iks_delete(presence);
2429                         iks_delete(status);
2430                 }
2431
2432                 if (client->component)
2433                         aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2434         case IKS_TYPE_SUBSCRIBED:
2435                 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2436                 if (!buddy && pak->from->partial) {
2437                         aji_create_buddy(pak->from->partial, client);
2438                 }
2439         default:
2440                 if (option_verbose > 4) {
2441                         ast_verbose(VERBOSE_PREFIX_3 "JABBER: This is a subcription of type %i\n", pak->subtype);
2442                 }
2443         }
2444 }
2445
2446 /*!
2447  * \brief sends messages.
2448  * \param client the configured XMPP client we use to connect to a XMPP server
2449  * \param address
2450  * \param message
2451  * \retval IKS_OK success
2452  * \retval -1 failure
2453  */
2454 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
2455 {
2456         return aji_send_raw_chat(client, 0, NULL, address, message);
2457 }
2458
2459 /*!
2460 * \brief sends message to a groupchat
2461 * Prior to sending messages to a groupchat, one must be connected to it.
2462 * \param client the configured XMPP client we use to connect to a XMPP server
2463 * \param nick the nickname we use in the chatroom
2464 * \param address the user the messages must be sent to
2465 * \param message the message to send
2466 * \return IKS_OK on success, any other value on failure
2467 */
2468 int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message) {
2469         return aji_send_raw_chat(client, 1, nick, address, message);
2470 }
2471
2472 /*!
2473 * \brief sends messages.
2474 * \param client the configured XMPP client we use to connect to a XMPP server
2475 * \param nick the nickname we use in chatrooms
2476 * \param address
2477 * \param message
2478 * \return IKS_OK on success, any other value on failure
2479 */
2480 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
2481 {
2482         int res = 0;
2483         iks *message_packet = NULL;
2484         char from[AJI_MAX_JIDLEN];
2485         /* the nickname is used only in component mode */
2486         if (nick && client->component) {
2487                 snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
2488         } else {
2489                 snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
2490         }
2491         
2492         if (client->state != AJI_CONNECTED) {
2493                 ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
2494                 return -1;
2495         }   
2496         
2497         message_packet = iks_make_msg(groupchat ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message);
2498         if (!message_packet) {
2499                 ast_log(LOG_ERROR, "Out of memory.\n");
2500                 return -1;
2501         }
2502         iks_insert_attrib(message_packet, "from", from);
2503         res = ast_aji_send(client, message_packet);
2504         iks_delete(message_packet);
2505         
2506         return res;
2507 }
2508
2509 /*!
2510  * \brief create a chatroom.
2511  * \param client the configured XMPP client we use to connect to a XMPP server
2512  * \param room name of room
2513  * \param server name of server
2514  * \param topic topic for the room.
2515  * \return 0.
2516  */
2517 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
2518 {
2519         int res = 0;
2520         iks *iq = NULL;
2521         iq = iks_new("iq");
2522
2523         if (iq && client) {
2524                 iks_insert_attrib(iq, "type", "get");
2525                 iks_insert_attrib(iq, "to", server);
2526                 iks_insert_attrib(iq, "id", client->mid);
2527                 ast_aji_increment_mid(client->mid);
2528                 ast_aji_send(client, iq);
2529         } else 
2530                 ast_log(LOG_ERROR, "Out of memory.\n");
2531
2532         iks_delete(iq);
2533
2534         return res;
2535 }
2536
2537 /*!
2538  * \brief join a chatroom.
2539  * \param client the configured XMPP client we use to connect to a XMPP server
2540  * \param room room to join
2541  * \param nick the nickname to use in this room
2542  * \return IKS_OK on success, any other value on failure.
2543  */
2544 int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
2545 {
2546         return aji_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nick, NULL);
2547 }
2548
2549 /*!
2550  * \brief leave a chatroom.
2551  * \param client the configured XMPP client we use to connect to a XMPP server
2552  * \param room room to leave
2553  * \param nick the nickname used in this room
2554  * \return IKS_OK on success, any other value on failure.
2555  */
2556 int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
2557 {
2558         return aji_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nick, NULL);
2559 }
2560 /*!
2561  * \brief invite to a chatroom.
2562  * \param client the configured XMPP client we use to connect to a XMPP server
2563  * \param user 
2564  * \param room
2565  * \param message
2566  * \return res.
2567  */
2568 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
2569 {
2570         int res = 0;
2571         iks *invite, *body, *namespace;
2572
2573         invite = iks_new("message");
2574         body = iks_new("body");
2575         namespace = iks_new("x");
2576         if (client && invite && body && namespace) {
2577                 iks_insert_attrib(invite, "to", user);
2578                 iks_insert_attrib(invite, "id", client->mid);
2579                 ast_aji_increment_mid(client->mid);
2580                 iks_insert_cdata(body, message, 0);
2581                 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
2582                 iks_insert_attrib(namespace, "jid", room);
2583                 iks_insert_node(invite, body);
2584                 iks_insert_node(invite, namespace);
2585                 res = ast_aji_send(client, invite);
2586         } else 
2587                 ast_log(LOG_ERROR, "Out of memory.\n");
2588
2589         iks_delete(body);
2590         iks_delete(namespace);
2591         iks_delete(invite);
2592         
2593         return res;
2594 }
2595
2596 /*!
2597  * \internal
2598  * \brief receive message loop.
2599  * \param data void
2600  * \return void.
2601  */
2602 static void *aji_recv_loop(void *data)
2603 {
2604         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2605         int res = IKS_HOOK;
2606
2607         while(res != IKS_OK) {
2608                 ast_debug(3, "JABBER: Connecting.\n");
2609                 res = aji_reconnect(client);
2610                 sleep(4);
2611         }
2612
2613         do {
2614                 if (res == IKS_NET_RWERR || client->timeout == 0) {
2615                         while(res != IKS_OK) {
2616                                 ast_debug(3, "JABBER: reconnecting.\n");
2617                                 res = aji_reconnect(client);
2618                                 sleep(4);
2619                         }
2620                 }
2621
2622                 res = aji_recv(client, 1);
2623                 
2624                 if (client->state == AJI_DISCONNECTING) {
2625                         ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
2626                         pthread_exit(NULL);
2627                 }
2628
2629                 /* Decrease timeout if no data received, and delete
2630                  * old messages globally */
2631                 if (res == IKS_NET_EXPIRED) {
2632                         client->timeout--;
2633                         delete_old_messages_all(client);
2634                 }
2635                 if (res == IKS_HOOK) {
2636                         ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
2637                 } else if (res == IKS_NET_TLSFAIL) {
2638                         ast_log(LOG_ERROR, "JABBER:  Failure in TLS.\n");
2639                 } else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
2640                         res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
2641                         if (res == IKS_OK) {
2642                                 client->timeout = 50;
2643                         } else {
2644                                 ast_log(LOG_WARNING, "JABBER:  Network Timeout\n");
2645                         }
2646                 } else if (res == IKS_NET_RWERR) {
2647                         ast_log(LOG_WARNING, "JABBER: socket read error\n");
2648                 }
2649         } while (client);
2650         ASTOBJ_UNREF(client, aji_client_destroy);
2651         return 0;
2652 }
2653
2654 /*!
2655  * \brief increments the mid field for messages and other events.
2656  * \param mid char.
2657  * \return void.
2658  */
2659 void ast_aji_increment_mid(char *mid)
2660 {
2661         int i = 0;
2662
2663         for (i = strlen(mid) - 1; i >= 0; i--) {
2664                 if (mid[i] != 'z') {
2665                         mid[i] = mid[i] + 1;
2666                         i = 0;
2667                 } else
2668                         mid[i] = 'a';
2669         }
2670 }
2671
2672 #if 0
2673 /*!
2674  * \brief attempts to register to a transport.
2675  * \param aji_client struct, and xml packet.
2676  * \return IKS_FILTER_EAT.
2677  */
2678 /*allows for registering to transport , was too sketch and is out for now. */
2679 static int aji_register_transport(void *data, ikspak *pak)
2680 {
2681         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2682         int res = 0;
2683         struct aji_buddy *buddy = NULL;
2684         iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
2685
2686         if (client && send) {
2687                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2688                         ASTOBJ_RDLOCK(iterator); 
2689                         if (iterator->btype == AJI_TRANS) {
2690                                   buddy = iterator;
2691                         }
2692                         ASTOBJ_UNLOCK(iterator);
2693                 });
2694                 iks_filter_remove_hook(client->f, aji_register_transport);
2695                 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);
2696                 iks_insert_attrib(send, "to", buddy->host);
2697                 iks_insert_attrib(send, "id", client->mid);
2698                 ast_aji_increment_mid(client->mid);
2699                 iks_insert_attrib(send, "from", client->user);
2700                 res = ast_aji_send(client, send);
2701         } else 
2702                 ast_log(LOG_ERROR, "Out of memory.\n");
2703
2704         if (send)
2705                 iks_delete(send);
2706         ASTOBJ_UNREF(client, aji_client_destroy);
2707         return IKS_FILTER_EAT;
2708
2709 }
2710 /*!
2711  * \brief attempts to register to a transport step 2.
2712  * \param aji_client struct, and xml packet.
2713  * \return IKS_FILTER_EAT.
2714  */
2715 /* more of the same blob of code, too wonky for now*/
2716 static int aji_register_transport2(void *data, ikspak *pak)
2717 {
2718         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2719         int res = 0;
2720         struct aji_buddy *buddy = NULL;
2721
2722         iks *regiq = iks_new("iq");
2723         iks *regquery = iks_new("query");
2724         iks *reguser = iks_new("username");
2725         iks *regpass = iks_new("password");
2726
2727         if (client && regquery && reguser && regpass && regiq) {
2728                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2729                         ASTOBJ_RDLOCK(iterator);
2730                         if (iterator->btype == AJI_TRANS)
2731                                 buddy = iterator; ASTOBJ_UNLOCK(iterator);
2732                 });
2733                 iks_filter_remove_hook(client->f, aji_register_transport2);
2734                 iks_insert_attrib(regiq, "to", buddy->host);
2735                 iks_insert_attrib(regiq, "type", "set");
2736                 iks_insert_attrib(regiq, "id", client->mid);
2737                 ast_aji_increment_mid(client->mid);
2738                 iks_insert_attrib(regiq, "from", client->user);
2739                 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
2740                 iks_insert_cdata(reguser, buddy->user, 0);
2741                 iks_insert_cdata(regpass, buddy->pass, 0);
2742                 iks_insert_node(regiq, regquery);
2743                 iks_insert_node(regquery, reguser);
2744                 iks_insert_node(regquery, regpass);
2745                 res = ast_aji_send(client, regiq);
2746         } else
2747                 ast_log(LOG_ERROR, "Out of memory.\n");
2748         if (regiq)
2749                 iks_delete(regiq);
2750         if (regquery)
2751                 iks_delete(regquery);
2752         if (reguser)
2753                 iks_delete(reguser);
2754         if (regpass)
2755                 iks_delete(regpass);
2756         ASTOBJ_UNREF(client, aji_client_destroy);
2757         return IKS_FILTER_EAT;
2758 }
2759 #endif
2760
2761 /*!
2762  * \internal
2763  * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2764  * \param client the configured XMPP client we use to connect to a XMPP server
2765  * \return void.
2766  * \note The messages here should be configurable.
2767  */
2768 static void aji_pruneregister(struct aji_client *client)
2769 {
2770         int res = 0;
2771         iks *removeiq = iks_new("iq");
2772         iks *removequery = iks_new("query");
2773         iks *removeitem = iks_new("item");
2774         iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
2775         if (!client || !removeiq || !removequery || !removeitem || !send) {
2776                 ast_log(LOG_ERROR, "Out of memory.\n");
2777                 goto safeout;
2778         }
2779
2780         iks_insert_node(removeiq, removequery);
2781         iks_insert_node(removequery, removeitem);
2782         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2783                 ASTOBJ_RDLOCK(iterator);
2784                 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2785                  * be called at the same time */
2786                 if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) { /* If autoprune is set on jabber.conf */
2787                         res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
2788                                                                  "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
2789                                                                  " so I am no longer subscribing to your presence.\n"));
2790                         res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
2791                                                                  "GoodBye.  You are no longer in the Asterisk config file so I am removing"
2792                                                                  " your access to my presence.\n"));
2793                         iks_insert_attrib(removeiq, "from", client->jid->full); 
2794                         iks_insert_attrib(removeiq, "type", "set"); 
2795                         iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
2796                         iks_insert_attrib(removeitem, "jid", iterator->name);
2797                         iks_insert_attrib(removeitem, "subscription", "remove");
2798                         res = ast_aji_send(client, removeiq);
2799                 } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
2800                         res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name, 
2801                                                                  "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2802                         ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
2803                 }
2804                 ASTOBJ_UNLOCK(iterator);
2805         });
2806
2807  safeout:
2808         iks_delete(removeiq);
2809         iks_delete(removequery);
2810         iks_delete(removeitem);
2811         iks_delete(send);
2812         
2813         ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, aji_buddy_destroy);
2814 }
2815
2816 /*!
2817  * \internal
2818  * \brief filters the roster packet we get back from server.
2819  * \param data void
2820  * \param pak ikspak iksemel packet.
2821  * \return IKS_FILTER_EAT.
2822  */
2823 static int aji_filter_roster(void *data, ikspak *pak)
2824 {
2825         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2826         int flag = 0;
2827         iks *x = NULL;
2828         struct aji_buddy *buddy;
2829         
2830         client->state = AJI_CONNECTED;
2831         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2832                 ASTOBJ_RDLOCK(iterator);
2833                 x = iks_child(pak->query);
2834                 flag = 0;
2835                 while (x) {
2836                         if (!iks_strcmp(iks_name(x), "item")) {
2837                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
2838                                         flag = 1;
2839                                         ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
2840                                 }
2841                         }
2842                         x = iks_next(x);
2843                 }
2844                 if (!flag)
2845                         ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
2846                 iks_delete(x);
2847                 
2848                 ASTOBJ_UNLOCK(iterator);
2849         });
2850
2851         x = iks_child(pak->query);
2852         while (x) {
2853                 flag = 0;
2854                 if (iks_strcmp(iks_name(x), "item") == 0) {
2855                         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2856                                 ASTOBJ_RDLOCK(iterator);
2857                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
2858                                         flag = 1;
2859                                 ASTOBJ_UNLOCK(iterator);
2860                         });
2861
2862                         if (flag) {
2863                                 /* found buddy, don't create a new one */
2864                                 x = iks_next(x);
2865                                 continue;
2866                         }
2867                         
2868                         buddy = ast_calloc(1, sizeof(*buddy));
2869                         if (!buddy) {
2870                                 ast_log(LOG_WARNING, "Out of memory\n");
2871                                 return 0;
2872                         }
2873                         ASTOBJ_INIT(buddy);
2874                         ASTOBJ_WRLOCK(buddy);
2875                         ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
2876                         ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
2877                         if (ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
2878                                 ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
2879                                 ASTOBJ_MARK(buddy);
2880                         } else if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
2881                                 /* subscribe to buddy's presence only
2882                                    if we really need to */
2883                                 ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
2884                         }
2885                         ASTOBJ_UNLOCK(buddy);
2886                         if (buddy) {
2887                                 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
2888                                 ASTOBJ_UNREF(buddy, aji_buddy_destroy);
2889                         }
2890                 }
2891                 x = iks_next(x);
2892         }
2893
2894         iks_delete(x);
2895         aji_pruneregister(client);
2896
2897         ASTOBJ_UNREF(client, aji_client_destroy);
2898         return IKS_FILTER_EAT;
2899 }
2900
2901 /*!
2902  * \internal
2903  * \brief reconnect to jabber server
2904  * \param client the configured XMPP client we use to connect to a XMPP server
2905  * \return res.
2906 */
2907 static int aji_reconnect(struct aji_client *client)
2908 {
2909         int res = 0;
2910
2911         if (client->state)
2912                 client->state = AJI_DISCONNECTED;
2913         client->timeout=50;
2914         if (client->p)
2915                 iks_parser_reset(client->p);
2916         if (client->authorized)
2917                 client->authorized = 0;
2918
2919         res = aji_initialize(client);
2920
2921         return res;
2922 }
2923
2924 /*!
2925  * \internal
2926  * \brief Get the roster of jabber users
2927  * \param client the configured XMPP client we use to connect to a XMPP server
2928  * \return 1.
2929 */
2930 static int aji_get_roster(struct aji_client *client)
2931 {
2932         iks *roster = NULL;
2933         roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
2934
2935         if (roster) {
2936                 iks_insert_attrib(roster, "id", "roster");
2937                 aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
2938                 ast_aji_send(client, roster);
2939         }
2940
2941         iks_delete(roster);
2942         
2943         return 1;
2944 }
2945
2946 /*!
2947  * \internal
2948  * \brief connects as a client to jabber server.
2949  * \param data void
2950  * \param pak ikspak iksemel packet
2951  * \return res.
2952  */
2953 static int aji_client_connect(void *data, ikspak *pak)
2954 {
2955         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2956         int res = 0;
2957
2958         if (client) {
2959                 if (client->state == AJI_DISCONNECTED) {
2960                         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);
2961                         client->state = AJI_CONNECTING;
2962                         client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
2963                         iks_filter_remove_hook(client->f, aji_client_connect);
2964                         if (!client->component) /*client*/
2965                                 aji_get_roster(client);
2966                 }
2967         } else
2968                 ast_log(LOG_ERROR, "Out of memory.\n");
2969
2970         ASTOBJ_UNREF(client, aji_client_destroy);
2971         return res;
2972 }
2973
2974 /*!
2975  * \internal
2976  * \brief prepares client for connect.
2977  * \param client the configured XMPP client we use to connect to a XMPP server
2978  * \return 1.
2979  */
2980 static int aji_initialize(struct aji_client *client)
2981 {
2982         int connected = IKS_NET_NOCONN;
2983
2984 #ifdef HAVE_OPENSSL     
2985         /* reset stream flags */
2986         client->stream_flags = 0;
2987 #endif
2988         /* If it's a component, connect to user, otherwise, connect to server */
2989         connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
2990
2991         if (connected == IKS_NET_NOCONN) {
2992                 ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
2993                 return IKS_HOOK;
2994         } else  if (connected == IKS_NET_NODNS) {
2995                 ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to  %s\n", client->name, S_OR(client->serverhost, client->jid->server));
2996                 return IKS_HOOK;
2997         }
2998
2999         return IKS_OK;
3000 }
3001
3002 /*!
3003  * \brief disconnect from jabber server.
3004  * \param client the configured XMPP client we use to connect to a XMPP server
3005  * \return 1.
3006  */
3007 int ast_aji_disconnect(struct aji_client *client)
3008 {
3009         if (client) {
3010                 ast_verb(4, "JABBER: Disconnecting\n");
3011 #ifdef HAVE_OPENSSL
3012                 if (client->stream_flags & SECURE) {
3013                         SSL_shutdown(client->ssl_session);
3014                         SSL_CTX_free(client->ssl_context);
3015                         SSL_free(client->ssl_session);
3016                 }
3017 #endif
3018                 iks_disconnect(client->p);
3019                 iks_parser_delete(client->p);
3020                 ASTOBJ_UNREF(client, aji_client_destroy);
3021         }
3022
3023         return 1;
3024 }
3025
3026 /*!
3027  * \internal
3028  * \brief set presence of client.
3029  * \param client the configured XMPP client we use to connect to a XMPP server
3030  * \param to user send it to
3031  * \param from user it came from
3032  * \param level
3033  * \param desc
3034  * \return void.
3035  */
3036 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc)
3037 {
3038         int res = 0;
3039         iks *presence = iks_make_pres(level, desc);
3040         iks *cnode = iks_new("c");
3041         iks *priority = iks_new("priority");
3042         char priorityS[10];
3043
3044         if (presence && cnode && client && priority) {
3045                 if (to)
3046                         iks_insert_attrib(presence, "to", to);
3047                 if (from)
3048                         iks_insert_attrib(presence, "from", from);
3049                 snprintf(priorityS, sizeof(priorityS), "%d", client->priority);
3050                 iks_insert_cdata(priority, priorityS, strlen(priorityS));
3051                 iks_insert_node(presence, priority);
3052                 iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
3053                 iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
3054                 iks_insert_attrib(cnode, "ext", "voice-v1");
3055                 iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
3056                 iks_insert_node(presence, cnode);
3057                 res = ast_aji_send(client, presence);
3058         } else
3059                 ast_log(LOG_ERROR, "Out of memory.\n");
3060
3061         iks_delete(cnode);
3062         iks_delete(presence);
3063         iks_delete(priority);
3064 }
3065
3066 /*
3067 * \brief set the presence of the client in a groupchat context.
3068 * \param client the configured XMPP client we use to connect to a XMPP server
3069 * \param room the groupchat identifier in the from roomname@service
3070 * \param from user it came from
3071 * \param level the type of action, i.e. join or leave the chatroom
3072 * \param nick the nickname to use in the chatroom
3073 * \param desc a text that details the action to be taken
3074 * \return res.
3075 */
3076 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc)
3077 {
3078         int res = 0;
3079         iks *presence = NULL, *x = NULL;
3080         char from[AJI_MAX_JIDLEN];
3081         char roomid[AJI_MAX_JIDLEN];
3082         
3083         presenc