New CLI command 'gtalk show settings'.
[asterisk/asterisk.git] / channels / chan_gtalk.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Matt O'Gorman <mogorman@digium.com>
7  * Philippe Sultan <philippe.sultan@gmail.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \author Matt O'Gorman <mogorman@digium.com>
23  * \author Philippe Sultan <philippe.sultan@gmail.com>
24  *
25  * \brief Gtalk Channel Driver, until google/libjingle works with jingle spec
26  *
27  * \ingroup channel_drivers
28  *
29  * ********** General TODO:s
30  * \todo Support config reloading.
31  * \todo Fix native bridging.
32  */
33
34 /*** MODULEINFO
35         <depend>iksemel</depend>
36         <depend>res_jabber</depend>
37         <use>openssl</use>
38  ***/
39
40 #include "asterisk.h"
41
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include <sys/socket.h>
45 #include <fcntl.h>
46 #include <netdb.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <sys/signal.h>
50 #include <iksemel.h>
51 #include <pthread.h>
52 #include <ctype.h>
53
54 #include "asterisk/lock.h"
55 #include "asterisk/channel.h"
56 #include "asterisk/config.h"
57 #include "asterisk/module.h"
58 #include "asterisk/pbx.h"
59 #include "asterisk/sched.h"
60 #include "asterisk/io.h"
61 #include "asterisk/rtp_engine.h"
62 #include "asterisk/stun.h"
63 #include "asterisk/acl.h"
64 #include "asterisk/callerid.h"
65 #include "asterisk/file.h"
66 #include "asterisk/cli.h"
67 #include "asterisk/app.h"
68 #include "asterisk/musiconhold.h"
69 #include "asterisk/manager.h"
70 #include "asterisk/stringfields.h"
71 #include "asterisk/utils.h"
72 #include "asterisk/causes.h"
73 #include "asterisk/astobj.h"
74 #include "asterisk/abstract_jb.h"
75 #include "asterisk/jabber.h"
76 #include "asterisk/jingle.h"
77 #include "asterisk/features.h"
78
79 #define GOOGLE_CONFIG           "gtalk.conf"
80
81 /*! Global jitterbuffer configuration - by default, jb is disabled */
82 static struct ast_jb_conf default_jbconf =
83 {
84         .flags = 0,
85         .max_size = -1,
86         .resync_threshold = -1,
87         .impl = "",
88         .target_extra = -1,
89 };
90 static struct ast_jb_conf global_jbconf;
91
92 enum gtalk_protocol {
93         AJI_PROTOCOL_UDP = 1,
94         AJI_PROTOCOL_SSLTCP = 2,
95 };
96
97 enum gtalk_connect_type {
98         AJI_CONNECT_STUN = 1,
99         AJI_CONNECT_LOCAL = 2,
100         AJI_CONNECT_RELAY = 3,
101 };
102
103 struct gtalk_pvt {
104         ast_mutex_t lock;                /*!< Channel private lock */
105         time_t laststun;
106         struct gtalk *parent;            /*!< Parent client */
107         char sid[100];
108         char us[AJI_MAX_JIDLEN];
109         char them[AJI_MAX_JIDLEN];
110         char ring[10];                   /*!< Message ID of ring */
111         iksrule *ringrule;               /*!< Rule for matching RING request */
112         int initiator;                   /*!< If we're the initiator */
113         int alreadygone;
114         int capability;
115         struct ast_codec_pref prefs;
116         struct gtalk_candidate *theircandidates;
117         struct gtalk_candidate *ourcandidates;
118         char cid_num[80];                /*!< Caller ID num */
119         char cid_name[80];               /*!< Caller ID name */
120         char exten[80];                  /*!< Called extension */
121         struct ast_channel *owner;       /*!< Master Channel */
122         struct ast_rtp_instance *rtp;             /*!< RTP audio session */
123         struct ast_rtp_instance *vrtp;            /*!< RTP video session */
124         format_t jointcapability;             /*!< Supported capability at both ends (codecs ) */
125         format_t peercapability;
126         struct gtalk_pvt *next; /* Next entity */
127 };
128
129 struct gtalk_candidate {
130         char name[100];
131         enum gtalk_protocol protocol;
132         double preference;
133         char username[100];
134         char password[100];
135         enum gtalk_connect_type type;
136         char network[6];
137         int generation;
138         char ip[16];
139         int port;
140         int receipt;
141         struct gtalk_candidate *next;
142 };
143
144 struct gtalk {
145         ASTOBJ_COMPONENTS(struct gtalk);
146         struct aji_client *connection;
147         struct aji_buddy *buddy;
148         struct gtalk_pvt *p;
149         struct ast_codec_pref prefs;
150         int amaflags;                   /*!< AMA Flags */
151         char user[AJI_MAX_JIDLEN];
152         char context[AST_MAX_CONTEXT];
153         char parkinglot[AST_MAX_CONTEXT];       /*!<  Parkinglot */
154         char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
155         format_t capability;
156         ast_group_t callgroup;  /*!< Call group */
157         ast_group_t pickupgroup;        /*!< Pickup group */
158         int callingpres;                /*!< Calling presentation */
159         int allowguest;
160         char language[MAX_LANGUAGE];    /*!<  Default language for prompts */
161         char musicclass[MAX_MUSICCLASS];        /*!<  Music on Hold class */
162 };
163
164 struct gtalk_container {
165         ASTOBJ_CONTAINER_COMPONENTS(struct gtalk);
166 };
167
168 static const char desc[]                = "Gtalk Channel";
169 static const char DEFAULT_CONTEXT[]     = "default";
170 static const int DEFAULT_ALLOWGUEST     = 1;
171
172 static format_t global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263;
173
174 AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pvt's) */
175
176 /* Forward declarations */
177 static struct ast_channel *gtalk_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
178 /*static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration);*/
179 static int gtalk_sendtext(struct ast_channel *ast, const char *text);
180 static int gtalk_digit_begin(struct ast_channel *ast, char digit);
181 static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
182 static int gtalk_call(struct ast_channel *ast, char *dest, int timeout);
183 static int gtalk_hangup(struct ast_channel *ast);
184 static int gtalk_answer(struct ast_channel *ast);
185 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action);
186 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p);
187 static int gtalk_newcall(struct gtalk *client, ikspak *pak);
188 static struct ast_frame *gtalk_read(struct ast_channel *ast);
189 static int gtalk_write(struct ast_channel *ast, struct ast_frame *f);
190 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
191 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
192 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
193 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid);
194 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p);
195 /* static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); */
196 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
197 static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
198 static int gtalk_update_externip(void);
199
200 /*! \brief PBX interface structure for channel registration */
201 static const struct ast_channel_tech gtalk_tech = {
202         .type = "Gtalk",
203         .description = "Gtalk Channel Driver",
204         .capabilities = AST_FORMAT_AUDIO_MASK,
205         .requester = gtalk_request,
206         .send_text = gtalk_sendtext,
207         .send_digit_begin = gtalk_digit_begin,
208         .send_digit_end = gtalk_digit_end,
209         /* XXX TODO native bridging is causing odd problems with DTMF pass-through with
210          * the gtalk servers. Enable native bridging once the source of this problem has
211          * been identified.
212         .bridge = ast_rtp_instance_bridge, */
213         .call = gtalk_call,
214         .hangup = gtalk_hangup,
215         .answer = gtalk_answer,
216         .read = gtalk_read,
217         .write = gtalk_write,
218         .exception = gtalk_read,
219         .indicate = gtalk_indicate,
220         .fixup = gtalk_fixup,
221         .send_html = gtalk_sendhtml,
222         .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
223 };
224
225 static struct sockaddr_in bindaddr = { 0, };    /*!< The address we bind to */
226
227 static struct sched_context *sched;     /*!< The scheduling context */
228 static struct io_context *io;   /*!< The IO context */
229 static struct in_addr __ourip;
230
231 static struct ast_cli_entry gtalk_cli[] = {
232 /*      AST_CLI_DEFINE(gtalk_do_reload, "Reload GoogleTalk configuration"), XXX TODO reloads are not possible yet. */
233         AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"),
234         AST_CLI_DEFINE(gtalk_show_settings, "Show GoogleTalk global settings"),
235 };
236
237 static char externip[16];
238 static char global_context[AST_MAX_CONTEXT];
239 static char global_parkinglot[AST_MAX_CONTEXT];
240 static int global_allowguest;
241 static struct sockaddr_in stunaddr; /*!< the stun server we get the externip from */
242 static int global_stunaddr;
243
244 static struct gtalk_container gtalk_list;
245
246 static void gtalk_member_destroy(struct gtalk *obj)
247 {
248         ast_free(obj);
249 }
250
251 static struct gtalk *find_gtalk(char *name, char *connection)
252 {
253         struct gtalk *gtalk = NULL;
254         char *domain = NULL , *s = NULL;
255
256         if (strchr(connection, '@')) {
257                 s = ast_strdupa(connection);
258                 domain = strsep(&s, "@");
259                 ast_verbose("OOOOH domain = %s\n", domain);
260         }
261         gtalk = ASTOBJ_CONTAINER_FIND(&gtalk_list, name);
262         if (!gtalk && strchr(name, '@'))
263                 gtalk = ASTOBJ_CONTAINER_FIND_FULL(&gtalk_list, name, user,,, strcasecmp);
264
265         if (!gtalk) {
266                 /* guest call */
267                 ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
268                         ASTOBJ_RDLOCK(iterator);
269                         if (!strcasecmp(iterator->name, "guest")) {
270                                 gtalk = iterator;
271                         }
272                         ASTOBJ_UNLOCK(iterator);
273
274                         if (gtalk)
275                                 break;
276                 });
277
278         }
279         return gtalk;
280 }
281
282
283 static int add_codec_to_answer(const struct gtalk_pvt *p, int codec, iks *dcodecs)
284 {
285         int res = 0;
286         char *format = ast_getformatname(codec);
287
288         if (!strcasecmp("ulaw", format)) {
289                 iks *payload_eg711u, *payload_pcmu;
290                 payload_pcmu = iks_new("payload-type");
291                 payload_eg711u = iks_new("payload-type");
292
293                 if(!payload_eg711u || !payload_pcmu) {
294                         iks_delete(payload_pcmu);
295                         iks_delete(payload_eg711u);
296                         ast_log(LOG_WARNING,"Failed to allocate iks node");
297                         return -1;
298                 }
299                 iks_insert_attrib(payload_pcmu, "id", "0");
300                 iks_insert_attrib(payload_pcmu, "name", "PCMU");
301                 iks_insert_attrib(payload_pcmu, "clockrate","8000");
302                 iks_insert_attrib(payload_pcmu, "bitrate","64000");
303                 iks_insert_attrib(payload_eg711u, "id", "100");
304                 iks_insert_attrib(payload_eg711u, "name", "EG711U");
305                 iks_insert_attrib(payload_eg711u, "clockrate","8000");
306                 iks_insert_attrib(payload_eg711u, "bitrate","64000");
307                 iks_insert_node(dcodecs, payload_pcmu);
308                 iks_insert_node(dcodecs, payload_eg711u);
309                 res ++;
310         }
311         if (!strcasecmp("alaw", format)) {
312                 iks *payload_eg711a, *payload_pcma;
313                 payload_pcma = iks_new("payload-type");
314                 payload_eg711a = iks_new("payload-type");
315                 if(!payload_eg711a || !payload_pcma) {
316                         iks_delete(payload_eg711a);
317                         iks_delete(payload_pcma);
318                         ast_log(LOG_WARNING,"Failed to allocate iks node");
319                         return -1;
320                 }
321                 iks_insert_attrib(payload_pcma, "id", "8");
322                 iks_insert_attrib(payload_pcma, "name", "PCMA");
323                 iks_insert_attrib(payload_pcma, "clockrate","8000");
324                 iks_insert_attrib(payload_pcma, "bitrate","64000");
325                 payload_eg711a = iks_new("payload-type");
326                 iks_insert_attrib(payload_eg711a, "id", "101");
327                 iks_insert_attrib(payload_eg711a, "name", "EG711A");
328                 iks_insert_attrib(payload_eg711a, "clockrate","8000");
329                 iks_insert_attrib(payload_eg711a, "bitrate","64000");
330                 iks_insert_node(dcodecs, payload_pcma);
331                 iks_insert_node(dcodecs, payload_eg711a);
332                 res ++;
333         }
334         if (!strcasecmp("ilbc", format)) {
335                 iks *payload_ilbc = iks_new("payload-type");
336                 if(!payload_ilbc) {
337                         ast_log(LOG_WARNING,"Failed to allocate iks node");
338                         return -1;
339                 }
340                 iks_insert_attrib(payload_ilbc, "id", "97");
341                 iks_insert_attrib(payload_ilbc, "name", "iLBC");
342                 iks_insert_attrib(payload_ilbc, "clockrate","8000");
343                 iks_insert_attrib(payload_ilbc, "bitrate","13300");
344                 iks_insert_node(dcodecs, payload_ilbc);
345                 res ++;
346         }
347         if (!strcasecmp("g723", format)) {
348                 iks *payload_g723 = iks_new("payload-type");
349                 if(!payload_g723) {
350                         ast_log(LOG_WARNING,"Failed to allocate iks node");
351                         return -1;
352                 }
353                 iks_insert_attrib(payload_g723, "id", "4");
354                 iks_insert_attrib(payload_g723, "name", "G723");
355                 iks_insert_attrib(payload_g723, "clockrate","8000");
356                 iks_insert_attrib(payload_g723, "bitrate","6300");
357                 iks_insert_node(dcodecs, payload_g723);
358                 res ++;
359         }
360         if (!strcasecmp("speex", format)) {
361                 iks *payload_speex = iks_new("payload-type");
362                 if(!payload_speex) {
363                         ast_log(LOG_WARNING,"Failed to allocate iks node");
364                         return -1;
365                 }
366                 iks_insert_attrib(payload_speex, "id", "110");
367                 iks_insert_attrib(payload_speex, "name", "speex");
368                 iks_insert_attrib(payload_speex, "clockrate","8000");
369                 iks_insert_attrib(payload_speex, "bitrate","11000");
370                 iks_insert_node(dcodecs, payload_speex);
371                 res++;
372         }
373         if (!strcasecmp("gsm", format)) {
374                 iks *payload_gsm = iks_new("payload-type");
375                 if(!payload_gsm) {
376                         ast_log(LOG_WARNING,"Failed to allocate iks node");
377                         return -1;
378                 }
379                 iks_insert_attrib(payload_gsm, "id", "103");
380                 iks_insert_attrib(payload_gsm, "name", "gsm");
381                 iks_insert_node(dcodecs, payload_gsm);
382                 res++;
383         }
384
385         return res;
386 }
387
388 static int gtalk_invite(struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator)
389 {
390         struct gtalk *client = p->parent;
391         iks *iq, *gtalk, *dcodecs, *payload_telephone, *transport;
392         int x;
393         int pref_codec = 0;
394         int alreadysent = 0;
395         int codecs_num = 0;
396         char *lowerto = NULL;
397
398         iq = iks_new("iq");
399         gtalk = iks_new("session");
400         dcodecs = iks_new("description");
401         transport = iks_new("transport");
402         payload_telephone = iks_new("payload-type");
403         if (!(iq && gtalk && dcodecs && transport && payload_telephone)){
404                 iks_delete(iq);
405                 iks_delete(gtalk);
406                 iks_delete(dcodecs);
407                 iks_delete(transport);
408                 iks_delete(payload_telephone);
409
410                 ast_log(LOG_ERROR, "Could not allocate iksemel nodes\n");
411                 return 0;
412         }
413         iks_insert_attrib(dcodecs, "xmlns", GOOGLE_AUDIO_NS);
414         iks_insert_attrib(dcodecs, "xml:lang", "en");
415
416         for (x = 0; x < 64; x++) {
417                 if (!(pref_codec = ast_codec_pref_index(&client->prefs, x)))
418                         break;
419                 if (!(client->capability & pref_codec))
420                         continue;
421                 if (alreadysent & pref_codec)
422                         continue;
423                 codecs_num = add_codec_to_answer(p, pref_codec, dcodecs);
424                 alreadysent |= pref_codec;
425         }
426
427         if (codecs_num) {
428                 /* only propose DTMF within an audio session */
429                 iks_insert_attrib(payload_telephone, "id", "101");
430                 iks_insert_attrib(payload_telephone, "name", "telephone-event");
431                 iks_insert_attrib(payload_telephone, "clockrate", "8000");
432         }
433         iks_insert_attrib(transport,"xmlns",GOOGLE_TRANSPORT_NS);
434
435         iks_insert_attrib(iq, "type", "set");
436         iks_insert_attrib(iq, "to", to);
437         iks_insert_attrib(iq, "from", from);
438         iks_insert_attrib(iq, "id", client->connection->mid);
439         ast_aji_increment_mid(client->connection->mid);
440
441         iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
442         iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept");
443         /* put the initiator attribute to lower case if we receive the call
444          * otherwise GoogleTalk won't establish the session */
445         if (!initiator) {
446                 char c;
447                 char *t = lowerto = ast_strdupa(to);
448                 while (((c = *t) != '/') && (*t++ = tolower(c)));
449         }
450         iks_insert_attrib(gtalk, "initiator", initiator ? from : lowerto);
451         iks_insert_attrib(gtalk, "id", sid);
452         iks_insert_node(iq, gtalk);
453         iks_insert_node(gtalk, dcodecs);
454         iks_insert_node(dcodecs, payload_telephone);
455
456         ast_aji_send(client->connection, iq);
457
458         iks_delete(payload_telephone);
459         iks_delete(transport);
460         iks_delete(dcodecs);
461         iks_delete(gtalk);
462         iks_delete(iq);
463         return 1;
464 }
465
466 static int gtalk_ringing_ack(void *data, ikspak *pak)
467 {
468         struct gtalk_pvt *p = data;
469
470         if (p->ringrule)
471                 iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
472         p->ringrule = NULL;
473         if (p->owner)
474                 ast_queue_control(p->owner, AST_CONTROL_RINGING);
475         return IKS_FILTER_EAT;
476 }
477
478 static int gtalk_answer(struct ast_channel *ast)
479 {
480         struct gtalk_pvt *p = ast->tech_pvt;
481         int res = 0;
482
483         ast_debug(1, "Answer!\n");
484         ast_mutex_lock(&p->lock);
485         gtalk_invite(p, p->them, p->us,p->sid, 0);
486         manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
487                 ast->name, "GTALK", p->sid);
488         ast_mutex_unlock(&p->lock);
489         return res;
490 }
491
492 static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
493 {
494         struct gtalk_pvt *p = chan->tech_pvt;
495         enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;
496
497         if (!p)
498                 return res;
499
500         ast_mutex_lock(&p->lock);
501         if (p->rtp){
502                 ao2_ref(p->rtp, +1);
503                 *instance = p->rtp;
504                 res = AST_RTP_GLUE_RESULT_LOCAL;
505         }
506         ast_mutex_unlock(&p->lock);
507
508         return res;
509 }
510
511 static format_t gtalk_get_codec(struct ast_channel *chan)
512 {
513         struct gtalk_pvt *p = chan->tech_pvt;
514         return p->peercapability;
515 }
516
517 static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, format_t codecs, int nat_active)
518 {
519         struct gtalk_pvt *p;
520
521         p = chan->tech_pvt;
522         if (!p)
523                 return -1;
524         ast_mutex_lock(&p->lock);
525
526 /*      if (rtp)
527                 ast_rtp_get_peer(rtp, &p->redirip);
528         else
529                 memset(&p->redirip, 0, sizeof(p->redirip));
530         p->redircodecs = codecs; */
531
532         /* Reset lastrtprx timer */
533         ast_mutex_unlock(&p->lock);
534         return 0;
535 }
536
537 static struct ast_rtp_glue gtalk_rtp_glue = {
538         .type = "Gtalk",
539         .get_rtp_info = gtalk_get_rtp_peer,
540         .get_codec = gtalk_get_codec,
541         .update_peer = gtalk_set_rtp_peer,
542 };
543
544 static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2)
545 {
546         iks *response = NULL, *error = NULL, *reason = NULL;
547         int res = -1;
548
549         response = iks_new("iq");
550         if (response) {
551                 iks_insert_attrib(response, "type", "result");
552                 iks_insert_attrib(response, "from", from);
553                 iks_insert_attrib(response, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
554                 iks_insert_attrib(response, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
555                 if (reasonstr) {
556                         error = iks_new("error");
557                         if (error) {
558                                 iks_insert_attrib(error, "type", "cancel");
559                                 reason = iks_new(reasonstr);
560                                 if (reason)
561                                         iks_insert_node(error, reason);
562                                 iks_insert_node(response, error);
563                         }
564                 }
565                 ast_aji_send(client->connection, response);
566                 res = 0;
567         }
568
569         iks_delete(reason);
570         iks_delete(error);
571         iks_delete(response);
572
573         return res;
574 }
575
576 static int gtalk_is_answered(struct gtalk *client, ikspak *pak)
577 {
578         struct gtalk_pvt *tmp = NULL;
579         char *from;
580         iks *codec;
581         char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
582         int peernoncodeccapability;
583
584         ast_log(LOG_DEBUG, "The client is %s\n", client->name);
585
586         /* Make sure our new call does exist */
587         for (tmp = client->p; tmp; tmp = tmp->next) {
588                 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
589                         break;
590                 } else if (iks_find_with_attrib(pak->x, "ses:session", "id", tmp->sid)) {
591                         break;
592                 }
593         }
594
595         if (!tmp) {
596                 ast_log(LOG_WARNING, "Could not find session in iq\n");
597                 return -1;
598         }
599
600         /* codec points to the first <payload-type/> tag */
601         codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
602         while (codec) {
603                 char *codec_id = iks_find_attrib(codec, "id");
604                 char *codec_name = iks_find_attrib(codec, "name");
605                 if (!codec_id || !codec_name) {
606                         codec = iks_next_tag(codec);
607                         continue;
608                 }
609
610                 ast_rtp_codecs_payloads_set_m_type(
611                         ast_rtp_instance_get_codecs(tmp->rtp),
612                         tmp->rtp,
613                         atoi(codec_id));
614                 ast_rtp_codecs_payloads_set_rtpmap_type(
615                         ast_rtp_instance_get_codecs(tmp->rtp),
616                         tmp->rtp,
617                         atoi(codec_id),
618                         "audio",
619                         codec_name,
620                         0);
621                 codec = iks_next_tag(codec);
622         }
623
624         /* Now gather all of the codecs that we are asked for */
625         ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), &tmp->peercapability, &peernoncodeccapability);
626
627         /* at this point, we received an awser from the remote Gtalk client,
628            which allows us to compare capabilities */
629         tmp->jointcapability = tmp->capability & tmp->peercapability;
630         if (!tmp->jointcapability) {
631                 ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->capability),
632                         ast_getformatname_multiple(s2, BUFSIZ, tmp->peercapability),
633                         ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcapability));
634                 /* close session if capabilities don't match */
635                 ast_queue_hangup(tmp->owner);
636
637                 return -1;
638
639         }
640
641         from = iks_find_attrib(pak->x, "to");
642         if (!from) {
643                 from = client->connection->jid->full;
644         }
645
646         if (tmp->owner) {
647                 ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
648         }
649         gtalk_update_stun(tmp->parent, tmp);
650         gtalk_response(client, from, pak, NULL, NULL);
651         return 1;
652 }
653
654 static int gtalk_is_accepted(struct gtalk *client, ikspak *pak)
655 {
656         struct gtalk_pvt *tmp;
657         char *from;
658
659         ast_log(LOG_DEBUG, "The client is %s\n", client->name);
660         /* find corresponding call */
661         for (tmp = client->p; tmp; tmp = tmp->next) {
662                 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
663                         break;
664                 }
665         }
666
667         from = iks_find_attrib(pak->x, "to");
668         if (!from) {
669                 from = client->connection->jid->full;
670         }
671
672         if (tmp) {
673                 gtalk_update_stun(tmp->parent, tmp);
674         } else {
675                 ast_log(LOG_NOTICE, "Whoa, didn't find call during accept?!\n");
676         }
677
678         /* answer 'iq' packet to let the remote peer know that we're alive */
679         gtalk_response(client, from, pak, NULL, NULL);
680         return 1;
681 }
682
683 static int gtalk_handle_dtmf(struct gtalk *client, ikspak *pak)
684 {
685         struct gtalk_pvt *tmp;
686         iks *dtmfnode = NULL, *dtmfchild = NULL;
687         char *dtmf;
688         char *from;
689         /* Make sure our new call doesn't exist yet */
690         for (tmp = client->p; tmp; tmp = tmp->next) {
691                 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid))
692                         break;
693         }
694         from = iks_find_attrib(pak->x, "to");
695         if (!from) {
696                 from = client->connection->jid->full;
697         }
698
699         if (tmp) {
700                 if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) {
701                         gtalk_response(client, from, pak,
702                                         "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
703                                         "unsupported-dtmf-method xmlns='http://jabber.org/protocol/gtalk/info/dtmf#errors'");
704                         return -1;
705                 }
706                 if ((dtmfnode = iks_find(pak->x, "dtmf"))) {
707                         if((dtmf = iks_find_attrib(dtmfnode, "code"))) {
708                                 if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) {
709                                         struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
710                                         f.subclass.integer = dtmf[0];
711                                         ast_queue_frame(tmp->owner, &f);
712                                         ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
713                                 } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
714                                         struct ast_frame f = {AST_FRAME_DTMF_END, };
715                                         f.subclass.integer = dtmf[0];
716                                         ast_queue_frame(tmp->owner, &f);
717                                         ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
718                                 } else if(iks_find_attrib(pak->x, "dtmf")) { /* 250 millasecond default */
719                                         struct ast_frame f = {AST_FRAME_DTMF, };
720                                         f.subclass.integer = dtmf[0];
721                                         ast_queue_frame(tmp->owner, &f);
722                                         ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
723                                 }
724                         }
725                 } else if ((dtmfnode = iks_find_with_attrib(pak->x, "gtalk", "action", "session-info"))) {
726                         if((dtmfchild = iks_find(dtmfnode, "dtmf"))) {
727                                 if((dtmf = iks_find_attrib(dtmfchild, "code"))) {
728                                         if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) {
729                                                 struct ast_frame f = {AST_FRAME_DTMF_END, };
730                                                 f.subclass.integer = dtmf[0];
731                                                 ast_queue_frame(tmp->owner, &f);
732                                                 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
733                                         } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
734                                                 struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
735                                                 f.subclass.integer = dtmf[0];
736                                                 ast_queue_frame(tmp->owner, &f);
737                                                 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
738                                         }
739                                 }
740                         }
741                 }
742                 gtalk_response(client, from, pak, NULL, NULL);
743                 return 1;
744         } else {
745                 ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
746         }
747
748         gtalk_response(client, from, pak, NULL, NULL);
749         return 1;
750 }
751
752 static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak)
753 {
754         struct gtalk_pvt *tmp;
755         char *from;
756
757         ast_debug(1, "The client is %s\n", client->name);
758         /* Make sure our new call doesn't exist yet */
759         for (tmp = client->p; tmp; tmp = tmp->next) {
760                 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
761                         (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
762                         break;
763                 }
764         }
765         from = iks_find_attrib(pak->x, "to");
766         if (!from) {
767                 from = client->connection->jid->full;
768         }
769
770         if (tmp) {
771                 tmp->alreadygone = 1;
772                 if (tmp->owner) {
773                         ast_queue_hangup(tmp->owner);
774                 }
775         } else {
776                 ast_log(LOG_NOTICE, "Whoa, didn't find call during hangup!\n");
777         }
778         gtalk_response(client, from, pak, NULL, NULL);
779         return 1;
780 }
781
782 static int gtalk_get_local_ip(struct ast_sockaddr *ourip)
783 {
784         struct ast_sockaddr root;
785         struct ast_sockaddr bindaddr_tmp;
786         struct ast_sockaddr *addrs;
787         int addrs_cnt;
788
789         /* If bind address is not 0.0.0.0, then bindaddr is our local ip. */
790         ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
791         if (!ast_sockaddr_is_any(&bindaddr_tmp)) {
792                 ast_sockaddr_copy(ourip, &bindaddr_tmp);
793                 return 0;
794         }
795
796         /* If no bind address was provided, lets see what ip we would use to connect to google.com and use that.
797          * If you can't resolve google.com from your network, then this module is useless for you anyway. */
798         if ((addrs_cnt = ast_sockaddr_resolve(&addrs, "google.com", PARSE_PORT_FORBID, AF_INET)) > 0) {
799                 ast_sockaddr_copy(&root, &addrs[0]);
800                 ast_free(addrs);
801                 if (!ast_ouraddrfor(&root, ourip)) {
802                         return 0;
803                 }
804         }
805
806         /* As a last resort, use this function to find our local address. */
807         return ast_find_ourip(ourip, &bindaddr_tmp, AF_INET);
808 }
809
810 static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to)
811 {
812         struct gtalk_candidate *tmp;
813         struct aji_client *c = client->connection;
814         struct gtalk_candidate *ours1 = NULL, *ours2 = NULL;
815         struct sockaddr_in sin = { 0, };
816         struct ast_sockaddr sin_tmp;
817         struct ast_sockaddr us;
818         iks *iq, *gtalk, *candidate, *transport;
819         char user[17], pass[17], preference[5], port[7];
820         char *lowerfrom = NULL;
821
822         iq = iks_new("iq");
823         gtalk = iks_new("session");
824         candidate = iks_new("candidate");
825         transport = iks_new("transport");
826         if (!iq || !gtalk || !candidate || !transport) {
827                 ast_log(LOG_ERROR, "Memory allocation error\n");
828                 goto safeout;
829         }
830         ours1 = ast_calloc(1, sizeof(*ours1));
831         ours2 = ast_calloc(1, sizeof(*ours2));
832         if (!ours1 || !ours2)
833                 goto safeout;
834
835         iks_insert_attrib(transport, "xmlns",GOOGLE_TRANSPORT_NS);
836         iks_insert_node(iq, gtalk);
837         iks_insert_node(gtalk,candidate);
838         iks_insert_node(gtalk,transport);
839
840         for (; p; p = p->next) {
841                 if (!strcasecmp(p->sid, sid))
842                         break;
843         }
844
845         if (!p) {
846                 ast_log(LOG_NOTICE, "No matching gtalk session - SID %s!\n", sid);
847                 goto safeout;
848         }
849
850         ast_rtp_instance_get_local_address(p->rtp, &sin_tmp);
851         ast_sockaddr_to_sin(&sin_tmp, &sin);
852
853         gtalk_get_local_ip(&us);
854
855         if (!strcmp(ast_sockaddr_stringify_addr(&us), "127.0.0.1")) {
856                 ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute.");
857         }
858
859         /* Setup our gtalk candidates */
860         ast_copy_string(ours1->name, "rtp", sizeof(ours1->name));
861         ours1->port = ntohs(sin.sin_port);
862         ours1->preference = 1;
863         snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
864         snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
865         ast_copy_string(ours1->username, user, sizeof(ours1->username));
866         ast_copy_string(ours1->password, pass, sizeof(ours1->password));
867         ast_copy_string(ours1->ip, ast_sockaddr_stringify_addr(&us),
868                         sizeof(ours1->ip));
869         ours1->protocol = AJI_PROTOCOL_UDP;
870         ours1->type = AJI_CONNECT_LOCAL;
871         ours1->generation = 0;
872         p->ourcandidates = ours1;
873
874         /* XXX this is a blocking action.  We send a STUN request to the server
875          * and wait for the response.  If blocking here is a problem the STUN requests/responses
876          * for the externip may need to be done differently. */
877         gtalk_update_externip();
878         if (!ast_strlen_zero(externip)) {
879                 ast_copy_string(ours2->username, user, sizeof(ours2->username));
880                 ast_copy_string(ours2->password, pass, sizeof(ours2->password));
881                 ast_copy_string(ours2->ip, externip, sizeof(ours2->ip));
882                 ast_copy_string(ours2->name, "rtp", sizeof(ours1->name));
883                 ours2->port = ntohs(sin.sin_port);
884                 ours2->preference = 0.9;
885                 ours2->protocol = AJI_PROTOCOL_UDP;
886                 ours2->type = AJI_CONNECT_STUN;
887                 ours2->generation = 0;
888                 ours1->next = ours2;
889                 ours2 = NULL;
890         }
891         ours1 = NULL;
892
893         for (tmp = p->ourcandidates; tmp; tmp = tmp->next) {
894                 snprintf(port, sizeof(port), "%d", tmp->port);
895                 snprintf(preference, sizeof(preference), "%.2f", tmp->preference);
896                 iks_insert_attrib(iq, "from", to);
897                 iks_insert_attrib(iq, "to", from);
898                 iks_insert_attrib(iq, "type", "set");
899                 iks_insert_attrib(iq, "id", c->mid);
900                 ast_aji_increment_mid(c->mid);
901                 iks_insert_attrib(gtalk, "type", "candidates");
902                 iks_insert_attrib(gtalk, "id", sid);
903                 /* put the initiator attribute to lower case if we receive the call
904                  * otherwise GoogleTalk won't establish the session */
905                 if (!p->initiator) {
906                         char c;
907                         char *t = lowerfrom = ast_strdupa(from);
908                         while (((c = *t) != '/') && (*t++ = tolower(c)));
909                 }
910                 iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : lowerfrom);
911                 iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
912                 iks_insert_attrib(candidate, "name", tmp->name);
913                 iks_insert_attrib(candidate, "address", tmp->ip);
914                 iks_insert_attrib(candidate, "port", port);
915                 iks_insert_attrib(candidate, "username", tmp->username);
916                 iks_insert_attrib(candidate, "password", tmp->password);
917                 iks_insert_attrib(candidate, "preference", preference);
918                 if (tmp->protocol == AJI_PROTOCOL_UDP)
919                         iks_insert_attrib(candidate, "protocol", "udp");
920                 if (tmp->protocol == AJI_PROTOCOL_SSLTCP)
921                         iks_insert_attrib(candidate, "protocol", "ssltcp");
922                 if (tmp->type == AJI_CONNECT_STUN)
923                         iks_insert_attrib(candidate, "type", "stun");
924                 if (tmp->type == AJI_CONNECT_LOCAL)
925                         iks_insert_attrib(candidate, "type", "local");
926                 if (tmp->type == AJI_CONNECT_RELAY)
927                         iks_insert_attrib(candidate, "type", "relay");
928                 iks_insert_attrib(candidate, "network", "0");
929                 iks_insert_attrib(candidate, "generation", "0");
930                 ast_aji_send(c, iq);
931         }
932         p->laststun = 0;
933
934 safeout:
935         if (ours1)
936                 ast_free(ours1);
937         if (ours2)
938                 ast_free(ours2);
939         iks_delete(iq);
940         iks_delete(gtalk);
941         iks_delete(candidate);
942         iks_delete(transport);
943
944         return 1;
945 }
946
947 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid)
948 {
949         struct gtalk_pvt *tmp = NULL;
950         struct aji_resource *resources = NULL;
951         struct aji_buddy *buddy;
952         char idroster[200];
953         char *data, *exten = NULL;
954         struct ast_sockaddr bindaddr_tmp;
955
956         ast_debug(1, "The client is %s for alloc\n", client->name);
957         if (!sid && !strchr(them, '/')) {       /* I started call! */
958                 if (!strcasecmp(client->name, "guest")) {
959                         buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them);
960                         if (buddy) {
961                                 resources = buddy->resources;
962                         }
963                 } else if (client->buddy) {
964                         resources = client->buddy->resources;
965                 }
966
967                 while (resources) {
968                         if (resources->cap->jingle) {
969                                 break;
970                         }
971                         resources = resources->next;
972                 }
973                 if (resources) {
974                         snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource);
975                 } else if ((*them == '+') || (strstr(them, "@voice.google.com"))) {
976                         snprintf(idroster, sizeof(idroster), "%s/srvres", them);
977                 } else {
978                         ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n");
979                         return NULL;
980                 }
981         }
982         if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
983                 return NULL;
984         }
985
986         memcpy(&tmp->prefs, &client->prefs, sizeof(struct ast_codec_pref));
987
988         if (sid) {
989                 ast_copy_string(tmp->sid, sid, sizeof(tmp->sid));
990                 ast_copy_string(tmp->them, them, sizeof(tmp->them));
991                 ast_copy_string(tmp->us, us, sizeof(tmp->us));
992         } else {
993                 snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random());
994                 ast_copy_string(tmp->them, idroster, sizeof(tmp->them));
995                 ast_copy_string(tmp->us, us, sizeof(tmp->us));
996                 tmp->initiator = 1;
997         }
998         /* clear codecs */
999         bindaddr.sin_family = AF_INET;
1000         ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
1001         if (!(tmp->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL))) {
1002           ast_log(LOG_ERROR, "Failed to create a new RTP instance (possibly an invalid bindaddr?)\n");
1003           ast_free(tmp);
1004           return NULL;
1005         }
1006         ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1);
1007         ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_STUN, 1);
1008         ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_DTMF, 1);
1009         ast_rtp_instance_dtmf_mode_set(tmp->rtp, AST_RTP_DTMF_MODE_RFC2833);
1010         ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp);
1011
1012         /* add user configured codec capabilites */
1013         if (client->capability) {
1014                 tmp->capability = client->capability;
1015         } else if (global_capability) {
1016                 tmp->capability = global_capability;
1017         }
1018
1019         tmp->parent = client;
1020         if (!tmp->rtp) {
1021                 ast_log(LOG_WARNING, "Out of RTP sessions?\n");
1022                 ast_free(tmp);
1023                 return NULL;
1024         }
1025
1026         /* Set CALLERID(name) to the full JID of the remote peer */
1027         ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name));
1028
1029         if(strchr(tmp->us, '/')) {
1030                 data = ast_strdupa(tmp->us);
1031                 exten = strsep(&data, "/");
1032         } else {
1033                 exten = tmp->us;
1034         }
1035         ast_copy_string(tmp->exten,  exten, sizeof(tmp->exten));
1036         ast_mutex_init(&tmp->lock);
1037         ast_mutex_lock(&gtalklock);
1038         tmp->next = client->p;
1039         client->p = tmp;
1040         ast_mutex_unlock(&gtalklock);
1041         return tmp;
1042 }
1043
1044 /*! \brief Start new gtalk channel */
1045 static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid)
1046 {
1047         struct ast_channel *tmp;
1048         int fmt;
1049         int what;
1050         const char *n2;
1051
1052         if (title)
1053                 n2 = title;
1054         else
1055                 n2 = i->us;
1056         tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, linkedid, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff);
1057         if (!tmp) {
1058                 ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
1059                 return NULL;
1060         }
1061         tmp->tech = &gtalk_tech;
1062
1063         /* Select our native format based on codec preference until we receive
1064            something from another device to the contrary. */
1065         if (i->jointcapability)
1066                 what = i->jointcapability;
1067         else if (i->capability)
1068                 what = i->capability;
1069         else
1070                 what = global_capability;
1071
1072         /* Set Frame packetization */
1073         if (i->rtp) {
1074                 ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
1075         }
1076
1077         tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
1078         fmt = ast_best_codec(tmp->nativeformats);
1079
1080         if (i->rtp) {
1081                 ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
1082                 ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
1083         }
1084         if (i->vrtp) {
1085                 ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
1086                 ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
1087         }
1088         if (state == AST_STATE_RING)
1089                 tmp->rings = 1;
1090         tmp->adsicpe = AST_ADSI_UNAVAILABLE;
1091         tmp->writeformat = fmt;
1092         tmp->rawwriteformat = fmt;
1093         tmp->readformat = fmt;
1094         tmp->rawreadformat = fmt;
1095         tmp->tech_pvt = i;
1096
1097         tmp->callgroup = client->callgroup;
1098         tmp->pickupgroup = client->pickupgroup;
1099         tmp->caller.id.name.presentation = client->callingpres;
1100         tmp->caller.id.number.presentation = client->callingpres;
1101         if (!ast_strlen_zero(client->accountcode))
1102                 ast_string_field_set(tmp, accountcode, client->accountcode);
1103         if (client->amaflags)
1104                 tmp->amaflags = client->amaflags;
1105         if (!ast_strlen_zero(client->language))
1106                 ast_string_field_set(tmp, language, client->language);
1107         if (!ast_strlen_zero(client->musicclass))
1108                 ast_string_field_set(tmp, musicclass, client->musicclass);
1109         if (!ast_strlen_zero(client->parkinglot))
1110                 ast_string_field_set(tmp, parkinglot, client->parkinglot);
1111         i->owner = tmp;
1112         ast_module_ref(ast_module_info->self);
1113         ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
1114         ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
1115
1116         if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
1117                 tmp->dialed.number.str = ast_strdup(i->exten);
1118         }
1119         tmp->priority = 1;
1120         if (i->rtp)
1121                 ast_jb_configure(tmp, &global_jbconf);
1122         if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
1123                 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
1124                 tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
1125                 ast_hangup(tmp);
1126                 tmp = NULL;
1127         } else {
1128                 manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
1129                         "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
1130                         i->owner ? i->owner->name : "", "Gtalk", i->sid);
1131         }
1132         return tmp;
1133 }
1134
1135 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
1136 {
1137         iks *request, *session = NULL;
1138         int res = -1;
1139         char *lowerthem = NULL;
1140
1141         request = iks_new("iq");
1142         if (request) {
1143                 iks_insert_attrib(request, "type", "set");
1144                 iks_insert_attrib(request, "from", p->us);
1145                 iks_insert_attrib(request, "to", p->them);
1146                 iks_insert_attrib(request, "id", client->connection->mid);
1147                 ast_aji_increment_mid(client->connection->mid);
1148                 session = iks_new("session");
1149                 if (session) {
1150                         iks_insert_attrib(session, "type", action);
1151                         iks_insert_attrib(session, "id", p->sid);
1152                         /* put the initiator attribute to lower case if we receive the call
1153                          * otherwise GoogleTalk won't establish the session */
1154                         if (!p->initiator) {
1155                                 char c;
1156                                 char *t = lowerthem = ast_strdupa(p->them);
1157                                 while (((c = *t) != '/') && (*t++ = tolower(c)));
1158                         }
1159                         iks_insert_attrib(session, "initiator", p->initiator ? p->us : lowerthem);
1160                         iks_insert_attrib(session, "xmlns", GOOGLE_NS);
1161                         iks_insert_node(request, session);
1162                         ast_aji_send(client->connection, request);
1163                         res = 0;
1164                 }
1165         }
1166
1167         iks_delete(session);
1168         iks_delete(request);
1169
1170         return res;
1171 }
1172
1173 static void gtalk_free_candidates(struct gtalk_candidate *candidate)
1174 {
1175         struct gtalk_candidate *last;
1176         while (candidate) {
1177                 last = candidate;
1178                 candidate = candidate->next;
1179                 ast_free(last);
1180         }
1181 }
1182
1183 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
1184 {
1185         struct gtalk_pvt *cur, *prev = NULL;
1186         cur = client->p;
1187         while (cur) {
1188                 if (cur == p) {
1189                         if (prev)
1190                                 prev->next = p->next;
1191                         else
1192                                 client->p = p->next;
1193                         break;
1194                 }
1195                 prev = cur;
1196                 cur = cur->next;
1197         }
1198         if (p->ringrule)
1199                 iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
1200         if (p->owner)
1201                 ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
1202         if (p->rtp)
1203                 ast_rtp_instance_destroy(p->rtp);
1204         if (p->vrtp)
1205                 ast_rtp_instance_destroy(p->vrtp);
1206         gtalk_free_candidates(p->theircandidates);
1207         ast_free(p);
1208 }
1209
1210
1211 static int gtalk_newcall(struct gtalk *client, ikspak *pak)
1212 {
1213         struct gtalk_pvt *p, *tmp = client->p;
1214         struct ast_channel *chan;
1215         int res;
1216         iks *codec;
1217         char *from = NULL;
1218         char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
1219         int peernoncodeccapability;
1220         char *sid;
1221
1222         /* Make sure our new call doesn't exist yet */
1223         from = iks_find_attrib(pak->x,"to");
1224         if (!from) {
1225                 from = client->connection->jid->full;
1226         }
1227
1228         while (tmp) {
1229                 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
1230                         (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
1231                         ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
1232                         gtalk_response(client, from, pak, "out-of-order", NULL);
1233                         return -1;
1234                 }
1235                 tmp = tmp->next;
1236         }
1237
1238         if (!strcasecmp(client->name, "guest")){
1239                 /* the guest account is not tied to any configured XMPP client,
1240                    let's set it now */
1241                 client->connection = ast_aji_get_client(from);
1242                 if (!client->connection) {
1243                         ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
1244                         return -1;
1245                 }
1246         }
1247
1248         if (!(sid = iks_find_attrib(pak->query, "id"))) {
1249                 ast_log(LOG_WARNING, "Received Initiate without id attribute. Can not start call.\n");
1250                 return -1;
1251         }
1252
1253         p = gtalk_alloc(client, from, pak->from->full, sid);
1254         if (!p) {
1255                 ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
1256                 return -1;
1257         }
1258
1259         chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
1260         if (!chan) {
1261                 gtalk_free_pvt(client, p);
1262                 return -1;
1263         }
1264
1265         ast_mutex_lock(&p->lock);
1266         ast_copy_string(p->them, pak->from->full, sizeof(p->them));
1267         ast_copy_string(p->sid, sid, sizeof(p->sid));
1268
1269         /* codec points to the first <payload-type/> tag */
1270         codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
1271
1272         while (codec) {
1273                 char *codec_id = iks_find_attrib(codec, "id");
1274                 char *codec_name = iks_find_attrib(codec, "name");
1275                 if (!codec_id || !codec_name) {
1276                         codec = iks_next_tag(codec);
1277                         continue;
1278                 }
1279                 if (!strcmp(S_OR(iks_name(codec), ""), "vid:payload-type") && p->vrtp) {
1280                         ast_rtp_codecs_payloads_set_m_type(
1281                                 ast_rtp_instance_get_codecs(p->vrtp),
1282                                 p->vrtp,
1283                                 atoi(codec_id));
1284                         ast_rtp_codecs_payloads_set_rtpmap_type(
1285                                 ast_rtp_instance_get_codecs(p->vrtp),
1286                                 p->vrtp,
1287                                 atoi(codec_id),
1288                                 "video",
1289                                 codec_name,
1290                                 0);
1291                 } else {
1292                         ast_rtp_codecs_payloads_set_m_type(
1293                                 ast_rtp_instance_get_codecs(p->rtp),
1294                                 p->rtp,
1295                                 atoi(codec_id));
1296                         ast_rtp_codecs_payloads_set_rtpmap_type(
1297                                 ast_rtp_instance_get_codecs(p->rtp),
1298                                 p->rtp,
1299                                 atoi(codec_id),
1300                                 "audio",
1301                                 codec_name,
1302                                 0);
1303                 }
1304                 codec = iks_next_tag(codec);
1305         }
1306
1307         /* Now gather all of the codecs that we are asked for */
1308         ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability);
1309         p->jointcapability = p->capability & p->peercapability;
1310         ast_mutex_unlock(&p->lock);
1311
1312         ast_setstate(chan, AST_STATE_RING);
1313         if (!p->jointcapability) {
1314                 ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->capability),
1315                         ast_getformatname_multiple(s2, BUFSIZ, p->peercapability),
1316                         ast_getformatname_multiple(s3, BUFSIZ, p->jointcapability));
1317                 /* close session if capabilities don't match */
1318                 gtalk_action(client, p, "reject");
1319                 p->alreadygone = 1;
1320                 gtalk_hangup(chan);
1321                 ast_channel_release(chan);
1322                 return -1;
1323         }
1324
1325         res = ast_pbx_start(chan);
1326
1327         switch (res) {
1328         case AST_PBX_FAILED:
1329                 ast_log(LOG_WARNING, "Failed to start PBX :(\n");
1330                 gtalk_response(client, from, pak, "service-unavailable", NULL);
1331                 break;
1332         case AST_PBX_CALL_LIMIT:
1333                 ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
1334                 gtalk_response(client, from, pak, "service-unavailable", NULL);
1335                 break;
1336         case AST_PBX_SUCCESS:
1337                 gtalk_response(client, from, pak, NULL, NULL);
1338                 gtalk_create_candidates(client, p, p->sid, p->them, p->us);
1339                 /* nothing to do */
1340                 break;
1341         }
1342
1343         return 1;
1344 }
1345
1346 static int gtalk_update_externip(void)
1347 {
1348         int sock;
1349         char *newaddr;
1350         struct sockaddr_in answer = { 0, };
1351         struct sockaddr_in *dst;
1352         struct ast_sockaddr tmp_dst;
1353
1354         if (!stunaddr.sin_addr.s_addr) {
1355                 return -1;
1356         }
1357         dst = &stunaddr;
1358
1359         sock = socket(AF_INET, SOCK_DGRAM, 0);
1360         if (sock < 0) {
1361                 ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
1362                 return -1;
1363         }
1364
1365         ast_sockaddr_from_sin(&tmp_dst, dst);
1366         if (ast_connect(sock, &tmp_dst) != 0) {
1367                 ast_log(LOG_WARNING, "STUN Failed to connect to %s\n", ast_sockaddr_stringify(&tmp_dst));
1368                 close(sock);
1369                 return -1;
1370         }
1371
1372         if ((ast_stun_request(sock, &stunaddr, NULL, &answer))) {
1373                 close(sock);
1374                 return -1;
1375         }
1376
1377         newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
1378         memcpy(externip, newaddr, sizeof(externip));
1379
1380         close(sock);
1381         return 0;
1382
1383 }
1384
1385 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
1386 {
1387         struct gtalk_candidate *tmp;
1388         struct hostent *hp;
1389         struct ast_hostent ahp;
1390         struct sockaddr_in sin = { 0, };
1391         struct sockaddr_in aux = { 0, };
1392         struct ast_sockaddr sin_tmp;
1393         struct ast_sockaddr aux_tmp;
1394
1395         if (time(NULL) == p->laststun)
1396                 return 0;
1397
1398         tmp = p->theircandidates;
1399         p->laststun = time(NULL);
1400         while (tmp) {
1401                 char username[256];
1402
1403                 /* Find the IP address of the host */
1404                 if (!(hp = ast_gethostbyname(tmp->ip, &ahp))) {
1405                         ast_log(LOG_WARNING, "Could not get host by name for %s\n", tmp->ip);
1406                         tmp = tmp->next;
1407                         continue;
1408                 }
1409                 sin.sin_family = AF_INET;
1410                 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
1411                 sin.sin_port = htons(tmp->port);
1412                 snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);
1413
1414                 /* Find out the result of the STUN */
1415                 ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
1416                 ast_sockaddr_to_sin(&aux_tmp, &aux);
1417
1418                 /* If the STUN result is different from the IP of the hostname,
1419                  * lock on the stun IP of the hostname advertised by the
1420                  * remote client */
1421                 if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
1422                         ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
1423                 } else {
1424                         ast_sockaddr_from_sin(&sin_tmp, &sin);
1425                         ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
1426                 }
1427                 if (aux.sin_addr.s_addr) {
1428                         ast_debug(4, "Receiving RTP traffic from IP %s, matches with remote candidate's IP %s\n", ast_inet_ntoa(aux.sin_addr), tmp->ip);
1429                         ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
1430                 }
1431
1432                 tmp = tmp->next;
1433         }
1434         return 1;
1435 }
1436
1437 static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
1438 {
1439         struct gtalk_pvt *p = NULL, *tmp = NULL;
1440         struct aji_client *c = client->connection;
1441         struct gtalk_candidate *newcandidate = NULL;
1442         iks *traversenodes = NULL, *receipt = NULL;
1443         char *from;
1444
1445         from = iks_find_attrib(pak->x,"to");
1446         if (!from) {
1447                 from = c->jid->full;
1448         }
1449
1450         for (tmp = client->p; tmp; tmp = tmp->next) {
1451                 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
1452                         (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
1453                         p = tmp;
1454                         break;
1455                 }
1456         }
1457
1458         if (!p) {
1459                 return -1;
1460         }
1461         traversenodes = pak->query;
1462         while(traversenodes) {
1463                 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
1464                         traversenodes = iks_first_tag(traversenodes);
1465                         continue;
1466                 }
1467                 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
1468                         traversenodes = iks_child(traversenodes);
1469                         continue;
1470                 }
1471                 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
1472                         newcandidate = ast_calloc(1, sizeof(*newcandidate));
1473                         if (!newcandidate)
1474                                 return 0;
1475                         ast_copy_string(newcandidate->name,
1476                                 S_OR(iks_find_attrib(traversenodes, "name"), ""),
1477                                 sizeof(newcandidate->name));
1478                         ast_copy_string(newcandidate->ip,
1479                                 S_OR(iks_find_attrib(traversenodes, "address"), ""),
1480                                 sizeof(newcandidate->ip));
1481                         newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
1482                         ast_copy_string(newcandidate->username,
1483                                 S_OR(iks_find_attrib(traversenodes, "username"), ""),
1484                                 sizeof(newcandidate->username));
1485                         ast_copy_string(newcandidate->password,
1486                                 S_OR(iks_find_attrib(traversenodes, "password"), ""),
1487                                 sizeof(newcandidate->password));
1488                         newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
1489                         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
1490                                 newcandidate->protocol = AJI_PROTOCOL_UDP;
1491                         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
1492                                 newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
1493
1494                         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
1495                                 newcandidate->type = AJI_CONNECT_STUN;
1496                         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
1497                                 newcandidate->type = AJI_CONNECT_LOCAL;
1498                         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
1499                                 newcandidate->type = AJI_CONNECT_RELAY;
1500                         ast_copy_string(newcandidate->network,
1501                                 S_OR(iks_find_attrib(traversenodes, "network"), ""),
1502                                 sizeof(newcandidate->network));
1503                         newcandidate->generation = atoi(S_OR(iks_find_attrib(traversenodes, "generation"), "0"));
1504                         newcandidate->next = NULL;
1505
1506                         newcandidate->next = p->theircandidates;
1507                         p->theircandidates = newcandidate;
1508                         p->laststun = 0;
1509                         gtalk_update_stun(p->parent, p);
1510                         newcandidate = NULL;
1511                 }
1512                 traversenodes = iks_next_tag(traversenodes);
1513         }
1514
1515         receipt = iks_new("iq");
1516         iks_insert_attrib(receipt, "type", "result");
1517         iks_insert_attrib(receipt, "from", from);
1518         iks_insert_attrib(receipt, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
1519         iks_insert_attrib(receipt, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
1520         ast_aji_send(c, receipt);
1521
1522         iks_delete(receipt);
1523
1524         return 1;
1525 }
1526
1527 static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
1528 {
1529         struct ast_frame *f;
1530
1531         if (!p->rtp) {
1532                 return &ast_null_frame;
1533         }
1534         f = ast_rtp_instance_read(p->rtp, 0);
1535         gtalk_update_stun(p->parent, p);
1536         if (p->owner) {
1537                 /* We already hold the channel lock */
1538                 if (f->frametype == AST_FRAME_VOICE) {
1539                         if (f->subclass.codec != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
1540                                 ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
1541                                 p->owner->nativeformats =
1542                                         (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass.codec;
1543                                 ast_set_read_format(p->owner, p->owner->readformat);
1544                                 ast_set_write_format(p->owner, p->owner->writeformat);
1545                         }
1546                         /* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
1547                                 f = ast_dsp_process(p->owner, p->vad, f);
1548                                 if (option_debug && f && (f->frametype == AST_FRAME_DTMF))
1549                                         ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
1550                 } */
1551                 }
1552         }
1553         return f;
1554 }
1555
1556 static struct ast_frame *gtalk_read(struct ast_channel *ast)
1557 {
1558         struct ast_frame *fr;
1559         struct gtalk_pvt *p = ast->tech_pvt;
1560
1561         ast_mutex_lock(&p->lock);
1562         fr = gtalk_rtp_read(ast, p);
1563         ast_mutex_unlock(&p->lock);
1564         return fr;
1565 }
1566
1567 /*! \brief Send frame to media channel (rtp) */
1568 static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
1569 {
1570         struct gtalk_pvt *p = ast->tech_pvt;
1571         int res = 0;
1572         char buf[256];
1573
1574         switch (frame->frametype) {
1575         case AST_FRAME_VOICE:
1576                 if (!(frame->subclass.codec & ast->nativeformats)) {
1577                         ast_log(LOG_WARNING,
1578                                         "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
1579                                         ast_getformatname(frame->subclass.codec),
1580                                         ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
1581                                         ast_getformatname(ast->readformat),
1582                                         ast_getformatname(ast->writeformat));
1583                         return 0;
1584                 }
1585                 if (p) {
1586                         ast_mutex_lock(&p->lock);
1587                         if (p->rtp) {
1588                                 res = ast_rtp_instance_write(p->rtp, frame);
1589                         }
1590                         ast_mutex_unlock(&p->lock);
1591                 }
1592                 break;
1593         case AST_FRAME_VIDEO:
1594                 if (p) {
1595                         ast_mutex_lock(&p->lock);
1596                         if (p->vrtp) {
1597                                 res = ast_rtp_instance_write(p->vrtp, frame);
1598                         }
1599                         ast_mutex_unlock(&p->lock);
1600                 }
1601                 break;
1602         case AST_FRAME_IMAGE:
1603                 return 0;
1604                 break;
1605         default:
1606                 ast_log(LOG_WARNING, "Can't send %d type frames with Gtalk write\n",
1607                                 frame->frametype);
1608                 return 0;
1609         }
1610
1611         return res;
1612 }
1613
1614 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
1615 {
1616         struct gtalk_pvt *p = newchan->tech_pvt;
1617         ast_mutex_lock(&p->lock);
1618
1619         if ((p->owner != oldchan)) {
1620                 ast_mutex_unlock(&p->lock);
1621                 return -1;
1622         }
1623         if (p->owner == oldchan)
1624                 p->owner = newchan;
1625         ast_mutex_unlock(&p->lock);
1626         return 0;
1627 }
1628
1629 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
1630 {
1631         int res = 0;
1632
1633         switch (condition) {
1634         case AST_CONTROL_HOLD:
1635                 ast_moh_start(ast, data, NULL);
1636                 break;
1637         case AST_CONTROL_UNHOLD:
1638                 ast_moh_stop(ast);
1639                 break;
1640         default:
1641                 ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
1642                 res = -1;
1643         }
1644
1645         return res;
1646 }
1647
1648 static int gtalk_sendtext(struct ast_channel *chan, const char *text)
1649 {
1650         int res = 0;
1651         struct aji_client *client = NULL;
1652         struct gtalk_pvt *p = chan->tech_pvt;
1653
1654         if (!p->parent) {
1655                 ast_log(LOG_ERROR, "Parent channel not found\n");
1656                 return -1;
1657         }
1658         if (!p->parent->connection) {
1659                 ast_log(LOG_ERROR, "XMPP client not found\n");
1660                 return -1;
1661         }
1662         client = p->parent->connection;
1663         res = ast_aji_send_chat(client, p->them, text);
1664         return res;
1665 }
1666
1667 static int gtalk_digit_begin(struct ast_channel *chan, char digit)
1668 {
1669         struct gtalk_pvt *p = chan->tech_pvt;
1670         int res = 0;
1671
1672         ast_mutex_lock(&p->lock);
1673         if (p->rtp) {
1674                 ast_rtp_instance_dtmf_begin(p->rtp, digit);
1675         } else {
1676                 res = -1;
1677         }
1678         ast_mutex_unlock(&p->lock);
1679
1680         return res;
1681 }
1682
1683 static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
1684 {
1685         struct gtalk_pvt *p = chan->tech_pvt;
1686         int res = 0;
1687
1688         ast_mutex_lock(&p->lock);
1689         if (p->rtp) {
1690                 ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
1691         } else {
1692                 res = -1;
1693         }
1694         ast_mutex_unlock(&p->lock);
1695
1696         return res;
1697 }
1698
1699 /* This function is of not in use at the moment, but I am choosing to leave this
1700  * within the code base as a reference to how DTMF is possible through
1701  * jingle signaling.  However, google currently does DTMF through the RTP. */
1702 #if 0
1703 static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
1704 {
1705         struct gtalk_pvt *p = ast->tech_pvt;
1706         struct gtalk *client = p->parent;
1707         iks *iq, *gtalk, *dtmf;
1708         char buffer[2] = {digit, '\0'};
1709         char *lowerthem = NULL;
1710         iq = iks_new("iq");
1711         gtalk = iks_new("gtalk");
1712         dtmf = iks_new("dtmf");
1713         if(!iq || !gtalk || !dtmf) {
1714                 iks_delete(iq);
1715                 iks_delete(gtalk);
1716                 iks_delete(dtmf);
1717                 ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
1718                 return -1;
1719         }
1720
1721         iks_insert_attrib(iq, "type", "set");
1722         iks_insert_attrib(iq, "to", p->them);
1723         iks_insert_attrib(iq, "from", p->us);
1724         iks_insert_attrib(iq, "id", client->connection->mid);
1725         ast_aji_increment_mid(client->connection->mid);
1726         iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
1727         iks_insert_attrib(gtalk, "action", "session-info");
1728         // put the initiator attribute to lower case if we receive the call
1729         // otherwise GoogleTalk won't establish the session
1730         if (!p->initiator) {
1731                 char c;
1732                 char *t = lowerthem = ast_strdupa(p->them);
1733                 while (((c = *t) != '/') && (*t++ = tolower(c)));
1734         }
1735         iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: lowerthem);
1736         iks_insert_attrib(gtalk, "sid", p->sid);
1737         iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
1738         iks_insert_attrib(dtmf, "code", buffer);
1739         iks_insert_node(iq, gtalk);
1740         iks_insert_node(gtalk, dtmf);
1741
1742         ast_mutex_lock(&p->lock);
1743         if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
1744                 iks_insert_attrib(dtmf, "action", "button-down");
1745         } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
1746                 iks_insert_attrib(dtmf, "action", "button-up");
1747         }
1748         ast_aji_send(client->connection, iq);
1749
1750         iks_delete(iq);
1751         iks_delete(gtalk);
1752         iks_delete(dtmf);
1753         ast_mutex_unlock(&p->lock);
1754         return 0;
1755 }
1756 #endif
1757
1758 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
1759 {
1760         ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
1761
1762         return -1;
1763 }
1764
1765 /*!\brief Initiate new call, part of PBX interface
1766  * dest is the dial string */
1767 static int gtalk_call(struct ast_channel *ast, char *dest, int timeout)
1768 {
1769         struct gtalk_pvt *p = ast->tech_pvt;
1770
1771         if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
1772                 ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name);
1773                 return -1;
1774         }
1775
1776         ast_setstate(ast, AST_STATE_RING);
1777         if (!p->ringrule) {
1778                 ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
1779                 p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
1780                                                         IKS_RULE_ID, p->ring, IKS_RULE_DONE);
1781         } else {
1782                 ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
1783         }
1784
1785         gtalk_invite(p, p->them, p->us, p->sid, 1);
1786         gtalk_create_candidates(p->parent, p, p->sid, p->them, p->us);
1787
1788         return 0;
1789 }
1790
1791 /*! \brief Hangup a call through the gtalk proxy channel */
1792 static int gtalk_hangup(struct ast_channel *ast)
1793 {
1794         struct gtalk_pvt *p = ast->tech_pvt;
1795         struct gtalk *client;
1796
1797         ast_mutex_lock(&p->lock);
1798         client = p->parent;
1799         p->owner = NULL;
1800         ast->tech_pvt = NULL;
1801         if (!p->alreadygone) {
1802                 gtalk_action(client, p, "terminate");
1803         }
1804         ast_mutex_unlock(&p->lock);
1805
1806         gtalk_free_pvt(client, p);
1807         ast_module_unref(ast_module_info->self);
1808
1809         return 0;
1810 }
1811
1812 /*!\brief Part of PBX interface */
1813 static struct ast_channel *gtalk_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
1814 {
1815         struct gtalk_pvt *p = NULL;
1816         struct gtalk *client = NULL;
1817         char *sender = NULL, *to = NULL, *s = NULL;
1818         struct ast_channel *chan = NULL;
1819
1820         if (data) {
1821                 s = ast_strdupa(data);
1822                 if (s) {
1823                         sender = strsep(&s, "/");
1824                         if (sender && (sender[0] != '\0')) {
1825                                 to = strsep(&s, "/");
1826                         }
1827                         if (!to) {
1828                                 ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
1829                                 return NULL;
1830                         }
1831                 }
1832         }
1833
1834         client = find_gtalk(to, sender);
1835         if (!client) {
1836                 ast_log(LOG_WARNING, "Could not find recipient.\n");
1837                 return NULL;
1838         }
1839         if (!strcasecmp(client->name, "guest")){
1840                 /* the guest account is not tied to any configured XMPP client,
1841                    let's set it now */
1842                 client->connection = ast_aji_get_client(sender);
1843                 if (!client->connection) {
1844                         ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
1845                         ASTOBJ_UNREF(client, gtalk_member_destroy);
1846                         return NULL;
1847                 }
1848         }
1849
1850         ASTOBJ_WRLOCK(client);
1851         p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
1852         if (p) {
1853                 chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
1854         }
1855         ASTOBJ_UNLOCK(client);
1856         return chan;
1857 }
1858
1859 /*! \brief CLI command "gtalk show channels" */
1860 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1861 {
1862 #define FORMAT  "%-30.30s  %-30.30s  %-15.15s  %-5.5s %-5.5s \n"
1863         struct gtalk_pvt *p;
1864         struct ast_channel *chan;
1865         int numchans = 0;
1866         char them[AJI_MAX_JIDLEN];
1867         char *jid = NULL;
1868         char *resource = NULL;
1869
1870         switch (cmd) {
1871         case CLI_INIT:
1872                 e->command = "gtalk show channels";
1873                 e->usage =
1874                         "Usage: gtalk show channels\n"
1875                         "       Shows current state of the Gtalk channels.\n";
1876                 return NULL;
1877         case CLI_GENERATE:
1878                 return NULL;
1879         }
1880
1881         if (a->argc != 3)
1882                 return CLI_SHOWUSAGE;
1883
1884         ast_mutex_lock(&gtalklock);
1885         ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
1886         ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
1887                 ASTOBJ_WRLOCK(iterator);
1888                 p = iterator->p;
1889                 while(p) {
1890                         chan = p->owner;
1891                         ast_copy_string(them, p->them, sizeof(them));
1892                         jid = them;
1893                         resource = strchr(them, '/');
1894                         if (!resource)
1895                                 resource = "None";
1896                         else {
1897                                 *resource = '\0';
1898                                 resource ++;
1899                         }
1900                         if (chan)
1901                                 ast_cli(a->fd, FORMAT,
1902                                         chan->name,
1903                                         jid,
1904                                         resource,
1905                                         ast_getformatname(chan->readformat),
1906                                         ast_getformatname(chan->writeformat)
1907                                         );
1908                         else
1909                                 ast_log(LOG_WARNING, "No available channel\n");
1910                         numchans ++;
1911                         p = p->next;
1912                 }
1913                 ASTOBJ_UNLOCK(iterator);
1914         });
1915
1916         ast_mutex_unlock(&gtalklock);
1917
1918         ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
1919         return CLI_SUCCESS;
1920 #undef FORMAT
1921 }
1922
1923 /*! \brief List global settings for the GoogleTalk channel */
1924 static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1925 {
1926         char codec_buf[BUFSIZ];
1927         switch (cmd) {
1928         case CLI_INIT:
1929                 e->command = "gtalk show settings";
1930                 e->usage =
1931                         "Usage: gtalk show settings\n"
1932                         "       Provides detailed list of the configuration on the GoogleTalk channel.\n";
1933                 return NULL;
1934         case CLI_GENERATE:
1935                 return NULL;
1936         }
1937
1938         if (a->argc != 3) {
1939                 return CLI_SHOWUSAGE;
1940         }
1941
1942 #define FORMAT "  %-25.20s  %-15.30s\n"
1943
1944         ast_cli(a->fd, "\nGlobal Settings:\n");
1945         ast_cli(a->fd, "----------------\n");
1946         ast_cli(a->fd, FORMAT, "UDP Bindaddress:", ast_inet_ntoa(bindaddr.sin_addr));
1947         ast_cli(a->fd, FORMAT, "Stun Address:", global_stunaddr != 0 ? ast_inet_ntoa(stunaddr.sin_addr) : "Disabled");
1948         ast_cli(a->fd, FORMAT, "External IP:", S_OR(externip, "Disabled"));
1949         ast_cli(a->fd, FORMAT, "Context:", global_context);
1950         ast_cli(a->fd, FORMAT, "Codecs:", ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, global_capability));
1951         ast_cli(a->fd, FORMAT, "Parking Lot:", global_parkinglot);
1952         ast_cli(a->fd, FORMAT, "Allow Guest:", AST_CLI_YESNO(global_allowguest));
1953         ast_cli(a->fd, "\n----\n");
1954
1955         return CLI_SUCCESS;
1956 #undef FORMAT
1957 }
1958
1959 /*! \brief CLI command "gtalk reload"
1960  *  \todo XXX TODO make this work. */
1961 #if 0
1962 static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1963 {
1964         switch (cmd) {
1965         case CLI_INIT:
1966                 e->command = "gtalk reload";
1967                 e->usage =
1968                         "Usage: gtalk reload\n"
1969                         "       Reload gtalk channel driver.\n";
1970                 return NULL;
1971         case CLI_GENERATE:
1972                 return NULL;
1973         }
1974
1975         ast_verbose("IT DOES WORK!\n");
1976         return CLI_SUCCESS;
1977 }
1978 #endif
1979
1980 static int gtalk_parser(void *data, ikspak *pak)
1981 {
1982         struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
1983         int res;
1984
1985         if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
1986                 ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
1987         }
1988
1989         if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
1990                 ast_log(LOG_NOTICE, "No attribute \"type\" found.  Ignoring message.\n");
1991         } else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
1992                 /* New call */
1993                 gtalk_newcall(client, pak);
1994         } else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
1995                 ast_debug(3, "About to add candidate!\n");
1996                 res = gtalk_add_candidate(client, pak);
1997                 if (!res) {
1998                         ast_log(LOG_WARNING, "Could not add any candidate\n");
1999                 } else {
2000                         ast_debug(3, "Candidate Added!\n");
2001                 }
2002         } else if (!strcmp(iks_find_attrib(pak->query, "type"), "accept")) {
2003                 gtalk_is_answered(client, pak);
2004         } else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
2005                 gtalk_is_accepted(client, pak);
2006         } else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
2007                 gtalk_handle_dtmf(client, pak);
2008         } else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
2009                 gtalk_hangup_farend(client, pak);
2010         } else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
2011                 gtalk_hangup_farend(client, pak);
2012         }
2013         ASTOBJ_UNREF(client, gtalk_member_destroy);
2014         return IKS_FILTER_EAT;
2015 }
2016
2017 static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
2018                                                                 struct ast_codec_pref prefs, char *context,
2019                                                                 struct gtalk *member)
2020 {
2021         struct aji_client *client;
2022
2023         if (!member)
2024                 ast_log(LOG_WARNING, "Out of memory.\n");
2025
2026         ast_copy_string(member->name, label, sizeof(member->name));
2027         ast_copy_string(member->user, label, sizeof(member->user));
2028         ast_copy_string(member->context, context, sizeof(member->context));
2029         member->allowguest = allowguest;
2030         member->prefs = prefs;
2031         while (var) {
2032                 if (!strcasecmp(var->name, "username"))
2033                         ast_copy_string(member->user, var->value, sizeof(member->user));
2034                 else if (!strcasecmp(var->name, "disallow"))
2035                         ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0);
2036                 else if (!strcasecmp(var->name, "allow"))
2037                         ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
2038                 else if (!strcasecmp(var->name, "context"))
2039                         ast_copy_string(member->context, var->value, sizeof(member->context));
2040                 else if (!strcasecmp(var->name, "parkinglot"))
2041                         ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
2042                 else if (!strcasecmp(var->name, "connection")) {
2043                         if ((client = ast_aji_get_client(var->value))) {
2044                                 member->connection = client;
2045                                 iks_filter_add_rule(client->f, gtalk_parser, member,
2046                                                     IKS_RULE_TYPE, IKS_PAK_IQ,
2047                                                     IKS_RULE_FROM_PARTIAL, member->user,
2048                                                     IKS_RULE_NS, GOOGLE_NS,
2049                                                     IKS_RULE_DONE);
2050                         } else {
2051                                 ast_log(LOG_ERROR, "connection referenced not found!\n");
2052                                 return 0;
2053                         }
2054                 }
2055                 var = var->next;
2056         }
2057         if (member->connection && member->user)
2058                 member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
2059         else {
2060                 ast_log(LOG_ERROR, "No Connection or Username!\n");
2061         }
2062         return 1;
2063 }
2064
2065 static int gtalk_load_config(void)
2066 {
2067         char *cat = NULL;
2068         struct ast_config *cfg = NULL;
2069         struct ast_variable *var;
2070         struct gtalk *member;
2071         struct ast_codec_pref prefs;
2072         struct aji_client_container *clients;
2073         struct gtalk_candidate *global_candidates = NULL;
2074         struct hostent *hp;
2075         struct ast_hostent ahp;
2076         struct ast_flags config_flags = { 0 };
2077
2078         cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
2079         if (!cfg) {
2080                 return 0;
2081         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2082                 ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", GOOGLE_CONFIG);
2083                 return 0;
2084         }
2085
2086         /* Copy the default jb config over global_jbconf */
2087         memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
2088
2089         /* set defaults */
2090         memset(&stunaddr, 0, sizeof(stunaddr));
2091         global_stunaddr = 0;
2092         global_allowguest = DEFAULT_ALLOWGUEST;
2093         ast_copy_string(global_context, DEFAULT_CONTEXT, sizeof(global_context));
2094         ast_copy_string(global_parkinglot, DEFAULT_PARKINGLOT, sizeof(global_parkinglot));
2095
2096         cat = ast_category_browse(cfg, NULL);
2097         for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
2098                 /* handle jb conf */
2099                 if (!ast_jb_read_conf(&global_jbconf, var->name, var->value)) {
2100                         continue;
2101                 }
2102
2103                 if (!strcasecmp(var->name, "allowguest")) {
2104                         global_allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
2105                 } else if (!strcasecmp(var->name, "disallow")) {
2106                         ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0);
2107                 } else if (!strcasecmp(var->name, "allow")) {
2108                         ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
2109                 } else if (!strcasecmp(var->name, "context")) {
2110                         ast_copy_string(global_context, var->value, sizeof(global_context));
2111                 } else if (!strcasecmp(var->name, "externip")) {
2112                         ast_copy_string(externip, var->value, sizeof(externip));
2113                 } else if (!strcasecmp(var->name, "parkinglot")) {
2114                         ast_copy_string(global_parkinglot, var->value, sizeof(global_parkinglot));
2115                 } else if (!strcasecmp(var->name, "bindaddr")) {
2116                         if (!(hp = ast_gethostbyname(var->value, &ahp))) {
2117                                 ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
2118                         } else {
2119                                 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
2120                         }
2121                 } else if (!strcasecmp(var->name, "stunaddr")) {
2122                         stunaddr.sin_port = htons(STANDARD_STUN_PORT);
2123                         global_stunaddr = 1;
2124                         if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
2125                                 ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
2126                         }
2127                 }
2128         }
2129         while (cat) {
2130                 if (strcasecmp(cat, "general")) {
2131                         var = ast_variable_browse(cfg, cat);
2132                         member = ast_calloc(1, sizeof(*member));
2133                         ASTOBJ_INIT(member);
2134                         ASTOBJ_WRLOCK(member);
2135                         if (!strcasecmp(cat, "guest")) {
2136                                 ast_copy_string(member->name, "guest", sizeof(member->name));
2137                                 ast_copy_string(member->user, "guest", sizeof(member->user));
2138                                 ast_copy_string(member->context, global_context, sizeof(member->context));
2139                                 ast_copy_string(member->parkinglot, global_parkinglot, sizeof(member->parkinglot));
2140                                 member->allowguest = global_allowguest;
2141                                 member->prefs = prefs;
2142                                 while (var) {
2143                                         if (!strcasecmp(var->name, "disallow")) {
2144                                                 ast_parse_allow_disallow(&member->prefs, &member->capability,
2145                                                                                                  var->value, 0);
2146                                         } else if (!strcasecmp(var->name, "allow")) {
2147                                                 ast_parse_allow_disallow(&member->prefs, &member->capability,
2148                                                                                                  var->value, 1);
2149                                         } else if (!strcasecmp(var->name, "context")) {
2150                                                 ast_copy_string(member->context, var->value,
2151                                                                                 sizeof(member->context));
2152                                         } else if (!strcasecmp(var->name, "parkinglot")) {
2153                                                 ast_copy_string(member->parkinglot, var->value,
2154                                                                                 sizeof(member->parkinglot));
2155                                         }
2156                                         var = var->next;
2157                                 }
2158                                 ASTOBJ_UNLOCK(member);
2159                                 clients = ast_aji_get_clients();
2160                                 if (clients) {
2161                                         ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
2162                                                 ASTOBJ_WRLOCK(iterator);
2163                                                 ASTOBJ_WRLOCK(member);
2164                                                 member->connection = NULL;
2165                                                 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
2166                                                 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "http://jabber.org/protocol/gtalk", IKS_RULE_DONE);
2167                                                 ASTOBJ_UNLOCK(member);
2168                                                 ASTOBJ_UNLOCK(iterator);
2169                                         });
2170                                         ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
2171                                         ASTOBJ_UNREF(member, gtalk_member_destroy);
2172                                 } else {
2173                                         ASTOBJ_UNLOCK(member);
2174                                         ASTOBJ_UNREF(member, gtalk_member_destroy);
2175                                 }
2176                         } else {
2177                                 ASTOBJ_UNLOCK(member);
2178                                 if (gtalk_create_member(cat, var, global_allowguest, prefs, global_context, member)) {
2179                                         ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
2180                                 }
2181                                 ASTOBJ_UNREF(member, gtalk_member_destroy);
2182                         }
2183                 }
2184                 cat = ast_category_browse(cfg, cat);
2185         }
2186
2187         gtalk_update_externip();
2188         gtalk_free_candidates(global_candidates);
2189         return 1;
2190 }
2191
2192 /*! \brief Load module into PBX, register channel */
2193 static int load_module(void)
2194 {
2195         struct ast_sockaddr bindaddr_tmp;
2196         struct ast_sockaddr ourip_tmp;
2197
2198         char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
2199         free(jabber_loaded);
2200         if (!jabber_loaded) {
2201                 /* If embedded, check for a different module name */
2202                 jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0);
2203                 free(jabber_loaded);
2204                 if (!jabber_loaded) {
2205                         ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n");
2206                         return AST_MODULE_LOAD_DECLINE;
2207                 }
2208         }
2209
2210         ASTOBJ_CONTAINER_INIT(&gtalk_list);
2211         if (!gtalk_load_config()) {
2212                 ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
2213                 return 0;
2214         }
2215
2216         sched = sched_context_create();
2217         if (!sched) {
2218                 ast_log(LOG_WARNING, "Unable to create schedule context\n");
2219         }
2220
2221         io = io_context_create();
2222         if (!io) {
2223                 ast_log(LOG_WARNING, "Unable to create I/O context\n");
2224         }
2225
2226         ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
2227         if (gtalk_get_local_ip(&ourip_tmp)) {
2228                 ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
2229                 return 0;
2230         }
2231         __ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));
2232
2233         ast_rtp_glue_register(&gtalk_rtp_glue);
2234         ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
2235
2236         /* Make sure we can register our channel type */
2237         if (ast_channel_register(&gtalk_tech)) {
2238                 ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
2239                 return -1;
2240         }
2241         return 0;
2242 }
2243
2244 /*! \brief Reload module
2245  *  \todo XXX TODO make this work. */
2246 #if 0
2247 static int reload(void)
2248 {
2249         return 0;
2250 }
2251 #endif
2252 /*! \brief Unload the gtalk channel from Asterisk */
2253 static int unload_module(void)
2254 {
2255         struct gtalk_pvt *privates = NULL;
2256         ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
2257         /* First, take us out of the channel loop */
2258         ast_channel_unregister(&gtalk_tech);
2259         ast_rtp_glue_unregister(&gtalk_rtp_glue);
2260
2261         if (!ast_mutex_lock(&gtalklock)) {
2262                 /* Hangup all interfaces if they have an owner */
2263                 ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
2264                         ASTOBJ_WRLOCK(iterator);
2265                         privates = iterator->p;
2266                         while(privates) {
2267                                 if (privates->owner)
2268                                         ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
2269                                 privates = privates->next;
2270                         }
2271                         iterator->p = NULL;
2272                         ASTOBJ_UNLOCK(iterator);
2273                 });
2274                 ast_mutex_unlock(&gtalklock);
2275         } else {
2276                 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
2277                 return -1;
2278         }
2279         ASTOBJ_CONTAINER_DESTROYALL(&gtalk_list, gtalk_member_destroy);
2280         ASTOBJ_CONTAINER_DESTROY(&gtalk_list);
2281         return 0;
2282 }
2283
2284 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gtalk Channel Driver",
2285                 .load = load_module,
2286                 .unload = unload_module,
2287                 /* .reload = reload, */
2288                 .load_pri = AST_MODPRI_CHANNEL_DRIVER,
2289                 );