Sat Jul 12 2014 17:18:27

Asterisk developer's documentation


chan_gtalk.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Matt O'Gorman <mogorman@digium.com>
00007  * Philippe Sultan <philippe.sultan@gmail.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \author Matt O'Gorman <mogorman@digium.com>
00023  * \author Philippe Sultan <philippe.sultan@gmail.com>
00024  *
00025  * \brief Gtalk Channel Driver, until google/libjingle works with jingle spec
00026  *
00027  * \ingroup channel_drivers
00028  *
00029  * ********** General TODO:s
00030  * \todo Support config reloading.
00031  * \todo Fix native bridging.
00032  */
00033 
00034 /*** MODULEINFO
00035         <defaultenabled>no</defaultenabled>
00036    <depend>iksemel</depend>
00037    <depend>res_jabber</depend>
00038    <use type="external">openssl</use>
00039    <support_level>deprecated</support_level>
00040    <replacement>chan_motif</replacement>
00041  ***/
00042 
00043 #include "asterisk.h"
00044 
00045 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413587 $")
00046 
00047 #include <sys/socket.h>
00048 #include <fcntl.h>
00049 #include <netdb.h>
00050 #include <netinet/in.h>
00051 #include <arpa/inet.h>
00052 #include <sys/signal.h>
00053 #include <iksemel.h>
00054 #include <pthread.h>
00055 #include <ctype.h>
00056 
00057 #include "asterisk/lock.h"
00058 #include "asterisk/channel.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/module.h"
00061 #include "asterisk/pbx.h"
00062 #include "asterisk/sched.h"
00063 #include "asterisk/io.h"
00064 #include "asterisk/rtp_engine.h"
00065 #include "asterisk/stun.h"
00066 #include "asterisk/acl.h"
00067 #include "asterisk/callerid.h"
00068 #include "asterisk/file.h"
00069 #include "asterisk/cli.h"
00070 #include "asterisk/app.h"
00071 #include "asterisk/musiconhold.h"
00072 #include "asterisk/manager.h"
00073 #include "asterisk/stringfields.h"
00074 #include "asterisk/utils.h"
00075 #include "asterisk/causes.h"
00076 #include "asterisk/astobj.h"
00077 #include "asterisk/abstract_jb.h"
00078 #include "asterisk/jabber.h"
00079 #include "asterisk/jingle.h"
00080 #include "asterisk/features.h"
00081 
00082 #define GOOGLE_CONFIG      "gtalk.conf"
00083 
00084 /*! Global jitterbuffer configuration - by default, jb is disabled */
00085 static struct ast_jb_conf default_jbconf =
00086 {
00087    .flags = 0,
00088    .max_size = -1,
00089    .resync_threshold = -1,
00090    .impl = "",
00091    .target_extra = -1,
00092 };
00093 static struct ast_jb_conf global_jbconf;
00094 
00095 enum gtalk_protocol {
00096    AJI_PROTOCOL_UDP = 1,
00097    AJI_PROTOCOL_SSLTCP = 2,
00098 };
00099 
00100 enum gtalk_connect_type {
00101    AJI_CONNECT_STUN = 1,
00102    AJI_CONNECT_LOCAL = 2,
00103    AJI_CONNECT_RELAY = 3,
00104 };
00105 
00106 struct gtalk_pvt {
00107    ast_mutex_t lock;                /*!< Channel private lock */
00108    time_t laststun;
00109    struct gtalk *parent;            /*!< Parent client */
00110    char sid[100];
00111    char us[AJI_MAX_JIDLEN];
00112    char them[AJI_MAX_JIDLEN];
00113    char ring[10];                   /*!< Message ID of ring */
00114    iksrule *ringrule;               /*!< Rule for matching RING request */
00115    int initiator;                   /*!< If we're the initiator */
00116    int alreadygone;
00117    struct ast_codec_pref prefs;
00118    struct gtalk_candidate *theircandidates;
00119    struct gtalk_candidate *ourcandidates;
00120    char cid_num[80];                /*!< Caller ID num */
00121    char cid_name[80];               /*!< Caller ID name */
00122    char exten[80];                  /*!< Called extension */
00123    struct ast_channel *owner;       /*!< Master Channel */
00124    struct ast_rtp_instance *rtp;             /*!< RTP audio session */
00125    struct ast_rtp_instance *vrtp;            /*!< RTP video session */
00126    struct ast_format_cap *cap;
00127    struct ast_format_cap *jointcap;             /*!< Supported capability at both ends (codecs ) */
00128    struct ast_format_cap *peercap;
00129    struct gtalk_pvt *next; /* Next entity */
00130 };
00131 
00132 struct gtalk_candidate {
00133    char name[100];
00134    enum gtalk_protocol protocol;
00135    double preference;
00136    char username[100];
00137    char password[100];
00138    enum gtalk_connect_type type;
00139    char network[6];
00140    int generation;
00141    char ip[16];
00142    int port;
00143    int receipt;
00144    struct gtalk_candidate *next;
00145 };
00146 
00147 struct gtalk {
00148    ASTOBJ_COMPONENTS(struct gtalk);
00149    struct aji_client *connection;
00150    struct aji_buddy *buddy;
00151    struct gtalk_pvt *p;
00152    struct ast_codec_pref prefs;
00153    int amaflags;        /*!< AMA Flags */
00154    char user[AJI_MAX_JIDLEN];
00155    char context[AST_MAX_CONTEXT];
00156    char parkinglot[AST_MAX_CONTEXT];   /*!<  Parkinglot */
00157    char accountcode[AST_MAX_ACCOUNT_CODE];   /*!< Account code */
00158    struct ast_format_cap *cap;
00159    ast_group_t callgroup;  /*!< Call group */
00160    ast_group_t pickupgroup;   /*!< Pickup group */
00161    int callingpres;     /*!< Calling presentation */
00162    int allowguest;
00163    char language[MAX_LANGUAGE];  /*!<  Default language for prompts */
00164    char musicclass[MAX_MUSICCLASS]; /*!<  Music on Hold class */
00165 };
00166 
00167 struct gtalk_container {
00168    ASTOBJ_CONTAINER_COMPONENTS(struct gtalk);
00169 };
00170 
00171 static const char desc[]      = "Gtalk Channel";
00172 static const char DEFAULT_CONTEXT[] = "default";
00173 static const int DEFAULT_ALLOWGUEST = 1;
00174 
00175 static struct ast_format_cap *global_capability;
00176 
00177 AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pvt's) */
00178 
00179 /* Forward declarations */
00180 static struct ast_channel *gtalk_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
00181 /*static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration);*/
00182 static int gtalk_sendtext(struct ast_channel *ast, const char *text);
00183 static int gtalk_digit_begin(struct ast_channel *ast, char digit);
00184 static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00185 static int gtalk_call(struct ast_channel *ast, const char *dest, int timeout);
00186 static int gtalk_hangup(struct ast_channel *ast);
00187 static int gtalk_answer(struct ast_channel *ast);
00188 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action);
00189 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p);
00190 static int gtalk_newcall(struct gtalk *client, ikspak *pak);
00191 static struct ast_frame *gtalk_read(struct ast_channel *ast);
00192 static int gtalk_write(struct ast_channel *ast, struct ast_frame *f);
00193 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00194 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00195 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00196 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid);
00197 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p);
00198 /* static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); */
00199 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00200 static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00201 static int gtalk_update_externip(void);
00202 static int gtalk_parser(void *data, ikspak *pak);
00203 static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to);
00204 
00205 /*! \brief PBX interface structure for channel registration */
00206 static struct ast_channel_tech gtalk_tech = {
00207    .type = "Gtalk",
00208    .description = "Gtalk Channel Driver",
00209    .requester = gtalk_request,
00210    .send_text = gtalk_sendtext,
00211    .send_digit_begin = gtalk_digit_begin,
00212    .send_digit_end = gtalk_digit_end,
00213    /* XXX TODO native bridging is causing odd problems with DTMF pass-through with
00214     * the gtalk servers. Enable native bridging once the source of this problem has
00215     * been identified.
00216    .bridge = ast_rtp_instance_bridge, */
00217    .call = gtalk_call,
00218    .hangup = gtalk_hangup,
00219    .answer = gtalk_answer,
00220    .read = gtalk_read,
00221    .write = gtalk_write,
00222    .exception = gtalk_read,
00223    .indicate = gtalk_indicate,
00224    .fixup = gtalk_fixup,
00225    .send_html = gtalk_sendhtml,
00226    .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
00227 };
00228 
00229 static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */
00230 
00231 static struct ast_sched_context *sched;   /*!< The scheduling context */
00232 static struct io_context *io; /*!< The IO context */
00233 static struct in_addr __ourip;
00234 
00235 static struct ast_cli_entry gtalk_cli[] = {
00236 /* AST_CLI_DEFINE(gtalk_do_reload, "Reload GoogleTalk configuration"), XXX TODO reloads are not possible yet. */
00237    AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"),
00238    AST_CLI_DEFINE(gtalk_show_settings, "Show GoogleTalk global settings"),
00239 };
00240 
00241 static char externip[16];
00242 static char global_context[AST_MAX_CONTEXT];
00243 static char global_parkinglot[AST_MAX_CONTEXT];
00244 static int global_allowguest;
00245 static struct sockaddr_in stunaddr; /*!< the stun server we get the externip from */
00246 static int global_stunaddr;
00247 
00248 static struct gtalk_container gtalk_list;
00249 
00250 static void gtalk_member_destroy(struct gtalk *obj)
00251 {
00252    obj->cap = ast_format_cap_destroy(obj->cap);
00253    if (obj->connection) {
00254       ASTOBJ_UNREF(obj->connection, ast_aji_client_destroy);
00255    }
00256    if (obj->buddy) {
00257       ASTOBJ_UNREF(obj->buddy, ast_aji_buddy_destroy);
00258    }
00259    ast_free(obj);
00260 }
00261 
00262 /* XXX This could be a source of reference leaks given that the CONTAINER_FIND
00263  * macros bump the refcount while the traversal does not. */
00264 static struct gtalk *find_gtalk(char *name, char *connection)
00265 {
00266    struct gtalk *gtalk = NULL;
00267    char *domain = NULL , *s = NULL;
00268 
00269    if (strchr(connection, '@')) {
00270       s = ast_strdupa(connection);
00271       domain = strsep(&s, "@");
00272       ast_verbose("OOOOH domain = %s\n", domain);
00273    }
00274    gtalk = ASTOBJ_CONTAINER_FIND(&gtalk_list, name);
00275    if (!gtalk && strchr(name, '@'))
00276       gtalk = ASTOBJ_CONTAINER_FIND_FULL(&gtalk_list, name, user,,, strcasecmp);
00277 
00278    if (!gtalk) {
00279       /* guest call */
00280       ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
00281          ASTOBJ_RDLOCK(iterator);
00282          if (!strcasecmp(iterator->name, "guest")) {
00283             gtalk = iterator;
00284          }
00285          ASTOBJ_UNLOCK(iterator);
00286 
00287          if (gtalk)
00288             break;
00289       });
00290 
00291    }
00292    return gtalk;
00293 }
00294 
00295 
00296 static int add_codec_to_answer(const struct gtalk_pvt *p, struct ast_format *codec, iks *dcodecs)
00297 {
00298    int res = 0;
00299    const char *format = ast_getformatname(codec);
00300 
00301    if (!strcasecmp("ulaw", format)) {
00302       iks *payload_eg711u, *payload_pcmu;
00303       payload_pcmu = iks_new("payload-type");
00304       payload_eg711u = iks_new("payload-type");
00305 
00306       if(!payload_eg711u || !payload_pcmu) {
00307          iks_delete(payload_pcmu);
00308          iks_delete(payload_eg711u);
00309          ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00310          return -1;
00311       }
00312       iks_insert_attrib(payload_pcmu, "id", "0");
00313       iks_insert_attrib(payload_pcmu, "name", "PCMU");
00314       iks_insert_attrib(payload_pcmu, "clockrate","8000");
00315       iks_insert_attrib(payload_pcmu, "bitrate","64000");
00316       iks_insert_attrib(payload_eg711u, "id", "100");
00317       iks_insert_attrib(payload_eg711u, "name", "EG711U");
00318       iks_insert_attrib(payload_eg711u, "clockrate","8000");
00319       iks_insert_attrib(payload_eg711u, "bitrate","64000");
00320       iks_insert_node(dcodecs, payload_pcmu);
00321       iks_insert_node(dcodecs, payload_eg711u);
00322       res ++;
00323    }
00324    if (!strcasecmp("alaw", format)) {
00325       iks *payload_eg711a, *payload_pcma;
00326       payload_pcma = iks_new("payload-type");
00327       payload_eg711a = iks_new("payload-type");
00328       if(!payload_eg711a || !payload_pcma) {
00329          iks_delete(payload_eg711a);
00330          iks_delete(payload_pcma);
00331          ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00332          return -1;
00333       }
00334       iks_insert_attrib(payload_pcma, "id", "8");
00335       iks_insert_attrib(payload_pcma, "name", "PCMA");
00336       iks_insert_attrib(payload_pcma, "clockrate","8000");
00337       iks_insert_attrib(payload_pcma, "bitrate","64000");
00338       payload_eg711a = iks_new("payload-type");
00339       iks_insert_attrib(payload_eg711a, "id", "101");
00340       iks_insert_attrib(payload_eg711a, "name", "EG711A");
00341       iks_insert_attrib(payload_eg711a, "clockrate","8000");
00342       iks_insert_attrib(payload_eg711a, "bitrate","64000");
00343       iks_insert_node(dcodecs, payload_pcma);
00344       iks_insert_node(dcodecs, payload_eg711a);
00345       res ++;
00346    }
00347    if (!strcasecmp("ilbc", format)) {
00348       iks *payload_ilbc = iks_new("payload-type");
00349       if(!payload_ilbc) {
00350          ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00351          return -1;
00352       }
00353       iks_insert_attrib(payload_ilbc, "id", "97");
00354       iks_insert_attrib(payload_ilbc, "name", "iLBC");
00355       iks_insert_attrib(payload_ilbc, "clockrate","8000");
00356       iks_insert_attrib(payload_ilbc, "bitrate","13300");
00357       iks_insert_node(dcodecs, payload_ilbc);
00358       res ++;
00359    }
00360    if (!strcasecmp("g723", format)) {
00361       iks *payload_g723 = iks_new("payload-type");
00362       if(!payload_g723) {
00363          ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00364          return -1;
00365       }
00366       iks_insert_attrib(payload_g723, "id", "4");
00367       iks_insert_attrib(payload_g723, "name", "G723");
00368       iks_insert_attrib(payload_g723, "clockrate","8000");
00369       iks_insert_attrib(payload_g723, "bitrate","6300");
00370       iks_insert_node(dcodecs, payload_g723);
00371       res ++;
00372    }
00373    if (!strcasecmp("speex", format)) {
00374       iks *payload_speex = iks_new("payload-type");
00375       if(!payload_speex) {
00376          ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00377          return -1;
00378       }
00379       iks_insert_attrib(payload_speex, "id", "110");
00380       iks_insert_attrib(payload_speex, "name", "speex");
00381       iks_insert_attrib(payload_speex, "clockrate","8000");
00382       iks_insert_attrib(payload_speex, "bitrate","11000");
00383       iks_insert_node(dcodecs, payload_speex);
00384       res++;
00385    }
00386    if (!strcasecmp("gsm", format)) {
00387       iks *payload_gsm = iks_new("payload-type");
00388       if(!payload_gsm) {
00389          ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00390          return -1;
00391       }
00392       iks_insert_attrib(payload_gsm, "id", "103");
00393       iks_insert_attrib(payload_gsm, "name", "gsm");
00394       iks_insert_node(dcodecs, payload_gsm);
00395       res++;
00396    }
00397 
00398    return res;
00399 }
00400 
00401 static int gtalk_invite(struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator)
00402 {
00403    struct gtalk *client = p->parent;
00404    iks *iq, *gtalk, *dcodecs, *payload_telephone, *transport;
00405    int x;
00406    struct ast_format_cap *alreadysent;
00407    int codecs_num = 0;
00408    char *lowerto = NULL;
00409    struct ast_format tmpfmt;
00410 
00411    iq = iks_new("iq");
00412    gtalk = iks_new("session");
00413    dcodecs = iks_new("description");
00414    transport = iks_new("transport");
00415    payload_telephone = iks_new("payload-type");
00416    if (!(iq && gtalk && dcodecs && transport && payload_telephone)) {
00417       iks_delete(iq);
00418       iks_delete(gtalk);
00419       iks_delete(dcodecs);
00420       iks_delete(transport);
00421       iks_delete(payload_telephone);
00422 
00423       ast_log(LOG_ERROR, "Could not allocate iksemel nodes\n");
00424       return 0;
00425    }
00426    iks_insert_attrib(dcodecs, "xmlns", GOOGLE_AUDIO_NS);
00427    iks_insert_attrib(dcodecs, "xml:lang", "en");
00428 
00429    if (!(alreadysent = ast_format_cap_alloc_nolock())) {
00430       return 0;
00431    }
00432    for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
00433       if (!(ast_codec_pref_index(&client->prefs, x, &tmpfmt))) {
00434          break;
00435       }
00436       if (!(ast_format_cap_iscompatible(client->cap, &tmpfmt))) {
00437          continue;
00438       }
00439       if (ast_format_cap_iscompatible(alreadysent, &tmpfmt)) {
00440          continue;
00441       }
00442       codecs_num = add_codec_to_answer(p, &tmpfmt, dcodecs);
00443       ast_format_cap_add(alreadysent, &tmpfmt);
00444    }
00445    alreadysent = ast_format_cap_destroy(alreadysent);
00446 
00447    if (codecs_num) {
00448       /* only propose DTMF within an audio session */
00449       iks_insert_attrib(payload_telephone, "id", "101");
00450       iks_insert_attrib(payload_telephone, "name", "telephone-event");
00451       iks_insert_attrib(payload_telephone, "clockrate", "8000");
00452    }
00453    iks_insert_attrib(transport,"xmlns",GOOGLE_TRANSPORT_NS);
00454 
00455    iks_insert_attrib(iq, "type", "set");
00456    iks_insert_attrib(iq, "to", to);
00457    iks_insert_attrib(iq, "from", from);
00458    iks_insert_attrib(iq, "id", client->connection->mid);
00459    ast_aji_increment_mid(client->connection->mid);
00460 
00461    iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
00462    iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept");
00463    /* put the initiator attribute to lower case if we receive the call
00464     * otherwise GoogleTalk won't establish the session */
00465    if (!initiator) {
00466            char c;
00467            char *t = lowerto = ast_strdupa(to);
00468       while (((c = *t) != '/') && (*t++ = tolower(c)));
00469    }
00470    iks_insert_attrib(gtalk, "initiator", initiator ? from : lowerto);
00471    iks_insert_attrib(gtalk, "id", sid);
00472    iks_insert_node(iq, gtalk);
00473    iks_insert_node(gtalk, dcodecs);
00474    iks_insert_node(dcodecs, payload_telephone);
00475 
00476    ast_aji_send(client->connection, iq);
00477 
00478    iks_delete(payload_telephone);
00479    iks_delete(transport);
00480    iks_delete(dcodecs);
00481    iks_delete(gtalk);
00482    iks_delete(iq);
00483    return 1;
00484 }
00485 
00486 static int gtalk_ringing_ack(void *data, ikspak *pak)
00487 {
00488    struct gtalk_pvt *p = data;
00489    struct ast_channel *owner;
00490 
00491    ast_mutex_lock(&p->lock);
00492 
00493    if (p->ringrule) {
00494       iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
00495    }
00496    p->ringrule = NULL;
00497 
00498    /* this may be a redirect */
00499    if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
00500       char *name = NULL;
00501       char *redirect = NULL;
00502       iks *traversenodes = NULL;
00503       traversenodes = pak->query;
00504       while (traversenodes) {
00505          if (!(name = iks_name(traversenodes))) {
00506             break;
00507          }
00508          if (!strcasecmp(name, "error") &&
00509             ((redirect = iks_find_cdata(traversenodes, "redirect")) ||
00510               (redirect = iks_find_cdata(traversenodes, "sta:redirect"))) &&
00511             (redirect = strstr(redirect, "xmpp:"))) {
00512             redirect += 5;
00513             ast_debug(1, "redirect %s\n", redirect);
00514             ast_copy_string(p->them, redirect, sizeof(p->them));
00515 
00516             gtalk_invite(p, p->them, p->us, p->sid, 1);
00517             break;
00518          }
00519          traversenodes = iks_next_tag(traversenodes);
00520       }
00521    }
00522    gtalk_create_candidates(p->parent, p, p->sid, p->them, p->us);
00523    owner = p->owner;
00524    ast_mutex_unlock(&p->lock);
00525 
00526    if (owner) {
00527       ast_queue_control(owner, AST_CONTROL_RINGING);
00528    }
00529 
00530    return IKS_FILTER_EAT;
00531 }
00532 
00533 static int gtalk_answer(struct ast_channel *ast)
00534 {
00535    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
00536    int res = 0;
00537 
00538    ast_debug(1, "Answer!\n");
00539    ast_mutex_lock(&p->lock);
00540    gtalk_invite(p, p->them, p->us,p->sid, 0);
00541    manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
00542       ast_channel_name(ast), "GTALK", p->sid);
00543    ast_mutex_unlock(&p->lock);
00544    return res;
00545 }
00546 
00547 static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
00548 {
00549    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
00550    enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;
00551 
00552    if (!p)
00553       return res;
00554 
00555    ast_mutex_lock(&p->lock);
00556    if (p->rtp){
00557       ao2_ref(p->rtp, +1);
00558       *instance = p->rtp;
00559       res = AST_RTP_GLUE_RESULT_LOCAL;
00560    }
00561    ast_mutex_unlock(&p->lock);
00562 
00563    return res;
00564 }
00565 
00566 static void gtalk_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
00567 {
00568    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
00569    ast_mutex_lock(&p->lock);
00570    ast_format_cap_copy(result, p->peercap);
00571    ast_mutex_unlock(&p->lock);
00572 }
00573 
00574 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, const struct ast_format_cap *cap, int nat_active)
00575 {
00576    struct gtalk_pvt *p;
00577 
00578    p = ast_channel_tech_pvt(chan);
00579    if (!p)
00580       return -1;
00581    ast_mutex_lock(&p->lock);
00582 
00583 /* if (rtp)
00584       ast_rtp_get_peer(rtp, &p->redirip);
00585    else
00586       memset(&p->redirip, 0, sizeof(p->redirip));
00587    p->redircodecs = codecs; */
00588 
00589    /* Reset lastrtprx timer */
00590    ast_mutex_unlock(&p->lock);
00591    return 0;
00592 }
00593 
00594 static struct ast_rtp_glue gtalk_rtp_glue = {
00595    .type = "Gtalk",
00596    .get_rtp_info = gtalk_get_rtp_peer,
00597    .get_codec = gtalk_get_codec,
00598    .update_peer = gtalk_set_rtp_peer,
00599 };
00600 
00601 static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2)
00602 {
00603    iks *response = NULL, *error = NULL, *reason = NULL;
00604    int res = -1;
00605 
00606    response = iks_new("iq");
00607    if (response) {
00608       iks_insert_attrib(response, "type", "result");
00609       iks_insert_attrib(response, "from", from);
00610       iks_insert_attrib(response, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
00611       iks_insert_attrib(response, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
00612       if (reasonstr) {
00613          error = iks_new("error");
00614          if (error) {
00615             iks_insert_attrib(error, "type", "cancel");
00616             reason = iks_new(reasonstr);
00617             if (reason)
00618                iks_insert_node(error, reason);
00619             iks_insert_node(response, error);
00620          }
00621       }
00622       ast_aji_send(client->connection, response);
00623       res = 0;
00624    }
00625 
00626    iks_delete(reason);
00627    iks_delete(error);
00628    iks_delete(response);
00629 
00630    return res;
00631 }
00632 
00633 static int gtalk_is_answered(struct gtalk *client, ikspak *pak)
00634 {
00635    struct gtalk_pvt *tmp = NULL;
00636    char *from;
00637    iks *codec;
00638    char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
00639    int peernoncodeccapability;
00640 
00641    ast_debug(1, "The client is %s\n", client->name);
00642 
00643    /* Make sure our new call does exist */
00644    for (tmp = client->p; tmp; tmp = tmp->next) {
00645       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
00646          break;
00647       } else if (iks_find_with_attrib(pak->x, "ses:session", "id", tmp->sid)) {
00648          break;
00649       }
00650    }
00651 
00652    if (!tmp) {
00653       ast_log(LOG_WARNING, "Could not find session in iq\n");
00654       return -1;
00655    }
00656 
00657    /* codec points to the first <payload-type/> tag */
00658    codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
00659    while (codec) {
00660       char *codec_id = iks_find_attrib(codec, "id");
00661       char *codec_name = iks_find_attrib(codec, "name");
00662       if (!codec_id || !codec_name) {
00663          codec = iks_next_tag(codec);
00664          continue;
00665       }
00666 
00667       ast_rtp_codecs_payloads_set_m_type(
00668          ast_rtp_instance_get_codecs(tmp->rtp),
00669          tmp->rtp,
00670          atoi(codec_id));
00671       ast_rtp_codecs_payloads_set_rtpmap_type(
00672          ast_rtp_instance_get_codecs(tmp->rtp),
00673          tmp->rtp,
00674          atoi(codec_id),
00675          "audio",
00676          codec_name,
00677          0);
00678       codec = iks_next_tag(codec);
00679    }
00680 
00681    /* Now gather all of the codecs that we are asked for */
00682    ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), tmp->peercap, &peernoncodeccapability);
00683 
00684    /* at this point, we received an answer from the remote Gtalk client,
00685       which allows us to compare capabilities */
00686    ast_format_cap_joint_copy(tmp->cap, tmp->peercap, tmp->jointcap);
00687    if (ast_format_cap_is_empty(tmp->jointcap)) {
00688       ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->cap),
00689          ast_getformatname_multiple(s2, BUFSIZ, tmp->peercap),
00690          ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcap));
00691       /* close session if capabilities don't match */
00692       ast_queue_hangup(tmp->owner);
00693 
00694       return -1;
00695 
00696    }
00697 
00698    from = iks_find_attrib(pak->x, "to");
00699    if (!from) {
00700       from = client->connection->jid->full;
00701    }
00702 
00703    if (tmp->owner) {
00704       ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
00705    }
00706    gtalk_update_stun(tmp->parent, tmp);
00707    gtalk_response(client, from, pak, NULL, NULL);
00708    return 1;
00709 }
00710 
00711 static int gtalk_is_accepted(struct gtalk *client, ikspak *pak)
00712 {
00713    struct gtalk_pvt *tmp;
00714    char *from;
00715 
00716    ast_debug(1, "The client is %s\n", client->name);
00717    /* find corresponding call */
00718    for (tmp = client->p; tmp; tmp = tmp->next) {
00719       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
00720          break;
00721       }
00722    }
00723 
00724    from = iks_find_attrib(pak->x, "to");
00725    if (!from) {
00726       from = client->connection->jid->full;
00727    }
00728 
00729    if (tmp) {
00730       gtalk_update_stun(tmp->parent, tmp);
00731    } else {
00732       ast_log(LOG_NOTICE, "Whoa, didn't find call during accept?!\n");
00733    }
00734 
00735    /* answer 'iq' packet to let the remote peer know that we're alive */
00736    gtalk_response(client, from, pak, NULL, NULL);
00737    return 1;
00738 }
00739 
00740 static int gtalk_handle_dtmf(struct gtalk *client, ikspak *pak)
00741 {
00742    struct gtalk_pvt *tmp;
00743    iks *dtmfnode = NULL, *dtmfchild = NULL;
00744    char *dtmf;
00745    char *from;
00746    /* Make sure our new call doesn't exist yet */
00747    for (tmp = client->p; tmp; tmp = tmp->next) {
00748       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid))
00749          break;
00750    }
00751    from = iks_find_attrib(pak->x, "to");
00752    if (!from) {
00753       from = client->connection->jid->full;
00754    }
00755 
00756    if (tmp) {
00757       if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) {
00758          gtalk_response(client, from, pak,
00759                "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
00760                "unsupported-dtmf-method xmlns='http://jabber.org/protocol/gtalk/info/dtmf#errors'");
00761          return -1;
00762       }
00763       if ((dtmfnode = iks_find(pak->x, "dtmf"))) {
00764          if((dtmf = iks_find_attrib(dtmfnode, "code"))) {
00765             if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) {
00766                struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
00767                f.subclass.integer = dtmf[0];
00768                ast_queue_frame(tmp->owner, &f);
00769                ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00770             } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
00771                struct ast_frame f = {AST_FRAME_DTMF_END, };
00772                f.subclass.integer = dtmf[0];
00773                ast_queue_frame(tmp->owner, &f);
00774                ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00775             } else if(iks_find_attrib(pak->x, "dtmf")) { /* 250 millasecond default */
00776                struct ast_frame f = {AST_FRAME_DTMF, };
00777                f.subclass.integer = dtmf[0];
00778                ast_queue_frame(tmp->owner, &f);
00779                ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00780             }
00781          }
00782       } else if ((dtmfnode = iks_find_with_attrib(pak->x, "gtalk", "action", "session-info"))) {
00783          if((dtmfchild = iks_find(dtmfnode, "dtmf"))) {
00784             if((dtmf = iks_find_attrib(dtmfchild, "code"))) {
00785                if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) {
00786                   struct ast_frame f = {AST_FRAME_DTMF_END, };
00787                   f.subclass.integer = dtmf[0];
00788                   ast_queue_frame(tmp->owner, &f);
00789                   ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00790                } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
00791                   struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
00792                   f.subclass.integer = dtmf[0];
00793                   ast_queue_frame(tmp->owner, &f);
00794                   ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00795                }
00796             }
00797          }
00798       }
00799       gtalk_response(client, from, pak, NULL, NULL);
00800       return 1;
00801    } else {
00802       ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
00803    }
00804 
00805    gtalk_response(client, from, pak, NULL, NULL);
00806    return 1;
00807 }
00808 
00809 static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak)
00810 {
00811    struct gtalk_pvt *tmp;
00812    char *from;
00813 
00814    ast_debug(1, "The client is %s\n", client->name);
00815    /* Make sure our new call doesn't exist yet */
00816    for (tmp = client->p; tmp; tmp = tmp->next) {
00817       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
00818          (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
00819          break;
00820       }
00821    }
00822    from = iks_find_attrib(pak->x, "to");
00823    if (!from) {
00824       from = client->connection->jid->full;
00825    }
00826 
00827    if (tmp) {
00828       tmp->alreadygone = 1;
00829       if (tmp->owner) {
00830          ast_queue_hangup(tmp->owner);
00831       }
00832    } else {
00833       ast_log(LOG_NOTICE, "Whoa, didn't find call during hangup!\n");
00834    }
00835    gtalk_response(client, from, pak, NULL, NULL);
00836    return 1;
00837 }
00838 
00839 static int gtalk_get_local_ip(struct ast_sockaddr *ourip)
00840 {
00841    struct ast_sockaddr root;
00842    struct ast_sockaddr bindaddr_tmp;
00843    struct ast_sockaddr *addrs;
00844 
00845    /* If bind address is not 0.0.0.0, then bindaddr is our local ip. */
00846    ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
00847    if (!ast_sockaddr_is_any(&bindaddr_tmp)) {
00848       ast_sockaddr_copy(ourip, &bindaddr_tmp);
00849       return 0;
00850    }
00851 
00852    /* If no bind address was provided, lets see what ip we would use to connect to google.com and use that.
00853     * If you can't resolve google.com from your network, then this module is useless for you anyway. */
00854    if (ast_sockaddr_resolve(&addrs, "google.com", PARSE_PORT_FORBID, AF_INET) > 0) {
00855       ast_sockaddr_copy(&root, &addrs[0]);
00856       ast_free(addrs);
00857       if (!ast_ouraddrfor(&root, ourip)) {
00858          return 0;
00859       }
00860    }
00861 
00862    /* As a last resort, use this function to find our local address. */
00863    return ast_find_ourip(ourip, &bindaddr_tmp, AF_INET);
00864 }
00865 
00866 static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to)
00867 {
00868    struct gtalk_candidate *tmp;
00869    struct aji_client *c = client->connection;
00870    struct gtalk_candidate *ours1 = NULL, *ours2 = NULL;
00871    struct sockaddr_in sin = { 0, };
00872    struct ast_sockaddr sin_tmp;
00873    struct ast_sockaddr us;
00874    iks *iq, *gtalk, *candidate, *transport;
00875    char user[17], pass[17], preference[5], port[7];
00876    char *lowerfrom = NULL;
00877 
00878    iq = iks_new("iq");
00879    gtalk = iks_new("session");
00880    candidate = iks_new("candidate");
00881    transport = iks_new("transport");
00882    if (!iq || !gtalk || !candidate || !transport) {
00883       ast_log(LOG_ERROR, "Memory allocation error\n");
00884       goto safeout;
00885    }
00886    ours1 = ast_calloc(1, sizeof(*ours1));
00887    ours2 = ast_calloc(1, sizeof(*ours2));
00888    if (!ours1 || !ours2)
00889       goto safeout;
00890 
00891    iks_insert_attrib(transport, "xmlns",GOOGLE_TRANSPORT_NS);
00892    iks_insert_node(iq, gtalk);
00893    iks_insert_node(gtalk,candidate);
00894    iks_insert_node(gtalk,transport);
00895 
00896    for (; p; p = p->next) {
00897       if (!strcasecmp(p->sid, sid))
00898          break;
00899    }
00900 
00901    if (!p) {
00902       ast_log(LOG_NOTICE, "No matching gtalk session - SID %s!\n", sid);
00903       goto safeout;
00904    }
00905 
00906    ast_rtp_instance_get_local_address(p->rtp, &sin_tmp);
00907    ast_sockaddr_to_sin(&sin_tmp, &sin);
00908 
00909    gtalk_get_local_ip(&us);
00910 
00911    if (!strcmp(ast_sockaddr_stringify_addr(&us), "127.0.0.1")) {
00912       ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute.\n");
00913    }
00914 
00915    /* Setup our gtalk candidates */
00916    ast_copy_string(ours1->name, "rtp", sizeof(ours1->name));
00917    ours1->port = ntohs(sin.sin_port);
00918    ours1->preference = 1;
00919    snprintf(user, sizeof(user), "%08lx%08lx", (long unsigned)ast_random(), (long unsigned)ast_random());
00920    snprintf(pass, sizeof(pass), "%08lx%08lx", (long unsigned)ast_random(), (long unsigned)ast_random());
00921    ast_copy_string(ours1->username, user, sizeof(ours1->username));
00922    ast_copy_string(ours1->password, pass, sizeof(ours1->password));
00923    ast_copy_string(ours1->ip, ast_sockaddr_stringify_addr(&us),
00924          sizeof(ours1->ip));
00925    ours1->protocol = AJI_PROTOCOL_UDP;
00926    ours1->type = AJI_CONNECT_LOCAL;
00927    ours1->generation = 0;
00928    p->ourcandidates = ours1;
00929 
00930    /* XXX this is a blocking action.  We send a STUN request to the server
00931     * and wait for the response.  If blocking here is a problem the STUN requests/responses
00932     * for the externip may need to be done differently. */
00933    gtalk_update_externip();
00934    if (!ast_strlen_zero(externip)) {
00935       ast_copy_string(ours2->username, user, sizeof(ours2->username));
00936       ast_copy_string(ours2->password, pass, sizeof(ours2->password));
00937       ast_copy_string(ours2->ip, externip, sizeof(ours2->ip));
00938       ast_copy_string(ours2->name, "rtp", sizeof(ours1->name));
00939       ours2->port = ntohs(sin.sin_port);
00940       ours2->preference = 0.9;
00941       ours2->protocol = AJI_PROTOCOL_UDP;
00942       ours2->type = AJI_CONNECT_STUN;
00943       ours2->generation = 0;
00944       ours1->next = ours2;
00945       ours2 = NULL;
00946    }
00947    ours1 = NULL;
00948 
00949    for (tmp = p->ourcandidates; tmp; tmp = tmp->next) {
00950       snprintf(port, sizeof(port), "%d", tmp->port);
00951       snprintf(preference, sizeof(preference), "%.2f", tmp->preference);
00952       iks_insert_attrib(iq, "from", to);
00953       iks_insert_attrib(iq, "to", from);
00954       iks_insert_attrib(iq, "type", "set");
00955       iks_insert_attrib(iq, "id", c->mid);
00956       ast_aji_increment_mid(c->mid);
00957       iks_insert_attrib(gtalk, "type", "candidates");
00958       iks_insert_attrib(gtalk, "id", sid);
00959       /* put the initiator attribute to lower case if we receive the call
00960        * otherwise GoogleTalk won't establish the session */
00961       if (!p->initiator) {
00962          char cur;
00963          char *t = lowerfrom = ast_strdupa(from);
00964          while (((cur = *t) != '/') && (*t++ = tolower(cur)));
00965       }
00966       iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : lowerfrom);
00967       iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
00968       iks_insert_attrib(candidate, "name", tmp->name);
00969       iks_insert_attrib(candidate, "address", tmp->ip);
00970       iks_insert_attrib(candidate, "port", port);
00971       iks_insert_attrib(candidate, "username", tmp->username);
00972       iks_insert_attrib(candidate, "password", tmp->password);
00973       iks_insert_attrib(candidate, "preference", preference);
00974       if (tmp->protocol == AJI_PROTOCOL_UDP)
00975          iks_insert_attrib(candidate, "protocol", "udp");
00976       if (tmp->protocol == AJI_PROTOCOL_SSLTCP)
00977          iks_insert_attrib(candidate, "protocol", "ssltcp");
00978       if (tmp->type == AJI_CONNECT_STUN)
00979          iks_insert_attrib(candidate, "type", "stun");
00980       if (tmp->type == AJI_CONNECT_LOCAL)
00981          iks_insert_attrib(candidate, "type", "local");
00982       if (tmp->type == AJI_CONNECT_RELAY)
00983          iks_insert_attrib(candidate, "type", "relay");
00984       iks_insert_attrib(candidate, "network", "0");
00985       iks_insert_attrib(candidate, "generation", "0");
00986       ast_aji_send(c, iq);
00987    }
00988    p->laststun = 0;
00989 
00990 safeout:
00991    if (ours1)
00992       ast_free(ours1);
00993    if (ours2)
00994       ast_free(ours2);
00995    iks_delete(iq);
00996    iks_delete(gtalk);
00997    iks_delete(candidate);
00998    iks_delete(transport);
00999 
01000    return 1;
01001 }
01002 
01003 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid)
01004 {
01005    struct gtalk_pvt *tmp = NULL;
01006    struct aji_resource *resources = NULL;
01007    struct aji_buddy *buddy = NULL;
01008    char idroster[200] = "";
01009    char *data, *exten = NULL;
01010    struct ast_sockaddr bindaddr_tmp;
01011 
01012    ast_debug(1, "The client is %s for alloc\n", client->name);
01013    if (!sid && !strchr(them, '/')) {   /* I started call! */
01014       if (!strcasecmp(client->name, "guest")) {
01015          buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them);
01016          if (buddy) {
01017             resources = buddy->resources;
01018          }
01019       } else if (client->buddy) {
01020          resources = client->buddy->resources;
01021       }
01022 
01023       while (resources) {
01024          if (resources->cap->jingle) {
01025             break;
01026          }
01027          resources = resources->next;
01028       }
01029       if (resources) {
01030          snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource);
01031       } else if ((*them == '+') || (strstr(them, "@voice.google.com"))) {
01032          snprintf(idroster, sizeof(idroster), "%s", them);
01033       } else {
01034          ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n");
01035          if (buddy) {
01036             ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
01037          }
01038          return NULL;
01039       }
01040       if (buddy) {
01041          ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
01042       }
01043    }
01044    if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
01045       return NULL;
01046    }
01047    tmp->cap = ast_format_cap_alloc_nolock();
01048    tmp->jointcap = ast_format_cap_alloc_nolock();
01049    tmp->peercap = ast_format_cap_alloc_nolock();
01050    if (!tmp->jointcap || !tmp->peercap || !tmp->cap) {
01051       tmp->cap = ast_format_cap_destroy(tmp->cap);
01052       tmp->jointcap = ast_format_cap_destroy(tmp->jointcap);
01053       tmp->peercap = ast_format_cap_destroy(tmp->peercap);
01054       ast_free(tmp);
01055       return NULL;
01056    }
01057 
01058    memcpy(&tmp->prefs, &client->prefs, sizeof(struct ast_codec_pref));
01059 
01060    if (sid) {
01061       ast_copy_string(tmp->sid, sid, sizeof(tmp->sid));
01062       ast_copy_string(tmp->them, them, sizeof(tmp->them));
01063       ast_copy_string(tmp->us, us, sizeof(tmp->us));
01064    } else {
01065       snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", (long unsigned)ast_random(), (long unsigned)ast_random());
01066       ast_copy_string(tmp->them, idroster, sizeof(tmp->them));
01067       ast_copy_string(tmp->us, us, sizeof(tmp->us));
01068       tmp->initiator = 1;
01069    }
01070    /* clear codecs */
01071    bindaddr.sin_family = AF_INET;
01072    ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
01073    if (!(tmp->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL))) {
01074      ast_log(LOG_ERROR, "Failed to create a new RTP instance (possibly an invalid bindaddr?)\n");
01075      ast_free(tmp);
01076      return NULL;
01077    }
01078    ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1);
01079    ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_STUN, 1);
01080    ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_DTMF, 1);
01081    ast_rtp_instance_dtmf_mode_set(tmp->rtp, AST_RTP_DTMF_MODE_RFC2833);
01082    ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp);
01083 
01084    /* add user configured codec capabilites */
01085    if (!(ast_format_cap_is_empty(client->cap))) {
01086       ast_format_cap_copy(tmp->cap, client->cap);
01087    } else if (!(ast_format_cap_is_empty(global_capability))) {
01088       ast_format_cap_copy(tmp->cap, global_capability);
01089    }
01090 
01091    tmp->parent = client;
01092    if (!tmp->rtp) {
01093       ast_log(LOG_WARNING, "Out of RTP sessions?\n");
01094       ast_free(tmp);
01095       return NULL;
01096    }
01097 
01098    /* Set CALLERID(name) to the full JID of the remote peer */
01099    ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name));
01100 
01101    if(strchr(tmp->us, '/')) {
01102       data = ast_strdupa(tmp->us);
01103       exten = strsep(&data, "/");
01104    } else {
01105       exten = tmp->us;
01106    }
01107    ast_copy_string(tmp->exten,  exten, sizeof(tmp->exten));
01108    ast_mutex_init(&tmp->lock);
01109    ast_mutex_lock(&gtalklock);
01110    tmp->next = client->p;
01111    client->p = tmp;
01112    ast_mutex_unlock(&gtalklock);
01113    return tmp;
01114 }
01115 
01116 /*! \brief Start new gtalk channel */
01117 static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid)
01118 {
01119    struct ast_channel *tmp;
01120    const char *n2;
01121    struct ast_format_cap *what; /* used as SHALLOW COPY DO NOT DESTROY */
01122    struct ast_format tmpfmt;
01123 
01124    if (title)
01125       n2 = title;
01126    else
01127       n2 = i->us;
01128    tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, linkedid,
01129       client->accountcode, i->exten, client->context, client->amaflags,
01130       "Gtalk/%s-%04lx", n2, (long unsigned)(ast_random() & 0xffff));
01131    if (!tmp) {
01132       ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
01133       return NULL;
01134    }
01135    ast_channel_tech_set(tmp, &gtalk_tech);
01136 
01137    /* Select our native format based on codec preference until we receive
01138       something from another device to the contrary. */
01139    if (!(ast_format_cap_is_empty(i->jointcap))) {
01140       what = i->jointcap;
01141    } else if (i->cap) {
01142       what = i->cap;
01143    } else {
01144       what = global_capability;
01145    }
01146 
01147    /* Set Frame packetization */
01148    if (i->rtp) {
01149       ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
01150    }
01151 
01152    ast_codec_choose(&i->prefs, what, 1, &tmpfmt);
01153    ast_format_cap_add(ast_channel_nativeformats(tmp), &tmpfmt);
01154 
01155    ast_format_cap_iter_start(i->jointcap);
01156    while (!(ast_format_cap_iter_next(i->jointcap, &tmpfmt))) {
01157       if (AST_FORMAT_GET_TYPE(tmpfmt.id) == AST_FORMAT_TYPE_VIDEO) {
01158          ast_format_cap_add(ast_channel_nativeformats(tmp), &tmpfmt);
01159       }
01160    }
01161    ast_format_cap_iter_end(i->jointcap);
01162 
01163    if (i->rtp) {
01164       ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
01165       ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
01166    }
01167    if (i->vrtp) {
01168       ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
01169       ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
01170    }
01171    if (state == AST_STATE_RING)
01172       ast_channel_rings_set(tmp, 1);
01173    ast_channel_adsicpe_set(tmp, AST_ADSI_UNAVAILABLE);
01174 
01175    ast_best_codec(ast_channel_nativeformats(tmp), &tmpfmt);
01176    ast_format_copy(ast_channel_writeformat(tmp), &tmpfmt);
01177    ast_format_copy(ast_channel_rawwriteformat(tmp), &tmpfmt);
01178    ast_format_copy(ast_channel_readformat(tmp), &tmpfmt);
01179    ast_format_copy(ast_channel_rawreadformat(tmp), &tmpfmt);
01180    ast_channel_tech_pvt_set(tmp, i);
01181 
01182    ast_channel_callgroup_set(tmp, client->callgroup);
01183    ast_channel_pickupgroup_set(tmp, client->pickupgroup);
01184    ast_channel_caller(tmp)->id.name.presentation = client->callingpres;
01185    ast_channel_caller(tmp)->id.number.presentation = client->callingpres;
01186    if (!ast_strlen_zero(client->accountcode))
01187       ast_channel_accountcode_set(tmp, client->accountcode);
01188    if (client->amaflags)
01189       ast_channel_amaflags_set(tmp, client->amaflags);
01190    if (!ast_strlen_zero(client->language))
01191       ast_channel_language_set(tmp, client->language);
01192    if (!ast_strlen_zero(client->musicclass))
01193       ast_channel_musicclass_set(tmp, client->musicclass);
01194    if (!ast_strlen_zero(client->parkinglot))
01195       ast_channel_parkinglot_set(tmp, client->parkinglot);
01196    i->owner = tmp;
01197    ast_module_ref(ast_module_info->self);
01198    ast_channel_context_set(tmp, client->context);
01199    ast_channel_exten_set(tmp, i->exten);
01200 
01201    if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
01202       ast_channel_dialed(tmp)->number.str = ast_strdup(i->exten);
01203    }
01204    ast_channel_priority_set(tmp, 1);
01205    if (i->rtp)
01206       ast_jb_configure(tmp, &global_jbconf);
01207    if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
01208       ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(tmp));
01209       ast_channel_hangupcause_set(tmp, AST_CAUSE_SWITCH_CONGESTION);
01210       ast_hangup(tmp);
01211       tmp = NULL;
01212    } else {
01213       manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
01214          "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
01215          i->owner ? ast_channel_name(i->owner) : "", "Gtalk", i->sid);
01216    }
01217    return tmp;
01218 }
01219 
01220 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
01221 {
01222    iks *request, *session = NULL;
01223    int res = -1;
01224    char *lowerthem = NULL;
01225 
01226    request = iks_new("iq");
01227    if (request) {
01228       iks_insert_attrib(request, "type", "set");
01229       iks_insert_attrib(request, "from", p->us);
01230       iks_insert_attrib(request, "to", p->them);
01231       iks_insert_attrib(request, "id", client->connection->mid);
01232       ast_aji_increment_mid(client->connection->mid);
01233       session = iks_new("session");
01234       if (session) {
01235          iks_insert_attrib(session, "type", action);
01236          iks_insert_attrib(session, "id", p->sid);
01237          /* put the initiator attribute to lower case if we receive the call
01238           * otherwise GoogleTalk won't establish the session */
01239          if (!p->initiator) {
01240                  char c;
01241             char *t = lowerthem = ast_strdupa(p->them);
01242             while (((c = *t) != '/') && (*t++ = tolower(c)));
01243          }
01244          iks_insert_attrib(session, "initiator", p->initiator ? p->us : lowerthem);
01245          iks_insert_attrib(session, "xmlns", GOOGLE_NS);
01246          iks_insert_node(request, session);
01247          ast_aji_send(client->connection, request);
01248          res = 0;
01249       }
01250    }
01251 
01252    iks_delete(session);
01253    iks_delete(request);
01254 
01255    return res;
01256 }
01257 
01258 static void gtalk_free_candidates(struct gtalk_candidate *candidate)
01259 {
01260    struct gtalk_candidate *last;
01261    while (candidate) {
01262       last = candidate;
01263       candidate = candidate->next;
01264       ast_free(last);
01265    }
01266 }
01267 
01268 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
01269 {
01270    struct gtalk_pvt *cur, *prev = NULL;
01271    cur = client->p;
01272    while (cur) {
01273       if (cur == p) {
01274          if (prev)
01275             prev->next = p->next;
01276          else
01277             client->p = p->next;
01278          break;
01279       }
01280       prev = cur;
01281       cur = cur->next;
01282    }
01283    if (p->ringrule)
01284       iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
01285    if (p->owner)
01286       ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
01287    if (p->rtp)
01288       ast_rtp_instance_destroy(p->rtp);
01289    if (p->vrtp)
01290       ast_rtp_instance_destroy(p->vrtp);
01291    gtalk_free_candidates(p->theircandidates);
01292    p->cap = ast_format_cap_destroy(p->cap);
01293    p->jointcap = ast_format_cap_destroy(p->jointcap);
01294    p->peercap = ast_format_cap_destroy(p->peercap);
01295    ast_free(p);
01296 }
01297 
01298 
01299 static int gtalk_newcall(struct gtalk *client, ikspak *pak)
01300 {
01301    struct gtalk_pvt *p, *tmp = client->p;
01302    struct ast_channel *chan;
01303    int res;
01304    iks *codec;
01305    char *from = NULL;
01306    char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
01307    int peernoncodeccapability;
01308    char *sid;
01309 
01310    /* Make sure our new call doesn't exist yet */
01311    from = iks_find_attrib(pak->x,"to");
01312    if (!from) {
01313       from = client->connection->jid->full;
01314    }
01315 
01316    while (tmp) {
01317       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01318          (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01319          ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
01320          gtalk_response(client, from, pak, "out-of-order", NULL);
01321          return -1;
01322       }
01323       tmp = tmp->next;
01324    }
01325 
01326    if (!strcasecmp(client->name, "guest")){
01327       /* the guest account is not tied to any configured XMPP client,
01328          let's set it now */
01329       if (client->connection) {
01330          ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01331       }
01332       client->connection = ast_aji_get_client(from);
01333       if (!client->connection) {
01334          ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
01335          return -1;
01336       }
01337    }
01338 
01339    if (!(sid = iks_find_attrib(pak->query, "id"))) {
01340       ast_log(LOG_WARNING, "Received Initiate without id attribute. Can not start call.\n");
01341       return -1;
01342    }
01343 
01344    p = gtalk_alloc(client, from, pak->from->full, sid);
01345    if (!p) {
01346       ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
01347       return -1;
01348    }
01349 
01350    chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
01351    if (!chan) {
01352       gtalk_free_pvt(client, p);
01353       return -1;
01354    }
01355 
01356    ast_mutex_lock(&p->lock);
01357    ast_copy_string(p->them, pak->from->full, sizeof(p->them));
01358    ast_copy_string(p->sid, sid, sizeof(p->sid));
01359 
01360    /* codec points to the first <payload-type/> tag */
01361    codec = iks_first_tag(iks_first_tag(pak->query));
01362 
01363    while (codec) {
01364       char *codec_id = iks_find_attrib(codec, "id");
01365       char *codec_name = iks_find_attrib(codec, "name");
01366       if (!codec_id || !codec_name) {
01367          codec = iks_next_tag(codec);
01368          continue;
01369       }
01370       if (!strcmp(S_OR(iks_name(codec), ""), "vid:payload-type") && p->vrtp) {
01371          ast_rtp_codecs_payloads_set_m_type(
01372             ast_rtp_instance_get_codecs(p->vrtp),
01373             p->vrtp,
01374             atoi(codec_id));
01375          ast_rtp_codecs_payloads_set_rtpmap_type(
01376             ast_rtp_instance_get_codecs(p->vrtp),
01377             p->vrtp,
01378             atoi(codec_id),
01379             "video",
01380             codec_name,
01381             0);
01382       } else {
01383          ast_rtp_codecs_payloads_set_m_type(
01384             ast_rtp_instance_get_codecs(p->rtp),
01385             p->rtp,
01386             atoi(codec_id));
01387          ast_rtp_codecs_payloads_set_rtpmap_type(
01388             ast_rtp_instance_get_codecs(p->rtp),
01389             p->rtp,
01390             atoi(codec_id),
01391             "audio",
01392             codec_name,
01393             0);
01394       }
01395       codec = iks_next_tag(codec);
01396    }
01397 
01398    /* Now gather all of the codecs that we are asked for */
01399    ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), p->peercap, &peernoncodeccapability);
01400    ast_format_cap_joint_copy(p->cap, p->peercap, p->jointcap);
01401    ast_mutex_unlock(&p->lock);
01402 
01403    ast_setstate(chan, AST_STATE_RING);
01404    if (ast_format_cap_is_empty(p->jointcap)) {
01405       ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->cap),
01406          ast_getformatname_multiple(s2, BUFSIZ, p->peercap),
01407          ast_getformatname_multiple(s3, BUFSIZ, p->jointcap));
01408       /* close session if capabilities don't match */
01409       gtalk_action(client, p, "reject");
01410       p->alreadygone = 1;
01411       gtalk_hangup(chan);
01412       ast_channel_release(chan);
01413       return -1;
01414    }
01415 
01416    res = ast_pbx_start(chan);
01417 
01418    switch (res) {
01419    case AST_PBX_FAILED:
01420       ast_log(LOG_WARNING, "Failed to start PBX :(\n");
01421       gtalk_response(client, from, pak, "service-unavailable", NULL);
01422       break;
01423    case AST_PBX_CALL_LIMIT:
01424       ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
01425       gtalk_response(client, from, pak, "service-unavailable", NULL);
01426       break;
01427    case AST_PBX_SUCCESS:
01428       gtalk_response(client, from, pak, NULL, NULL);
01429       gtalk_create_candidates(client, p, p->sid, p->them, p->us);
01430       /* nothing to do */
01431       break;
01432    }
01433 
01434    return 1;
01435 }
01436 
01437 static int gtalk_update_externip(void)
01438 {
01439    int sock;
01440    char *newaddr;
01441    struct sockaddr_in answer = { 0, };
01442    struct sockaddr_in *dst;
01443    struct ast_sockaddr tmp_dst;
01444 
01445    if (!stunaddr.sin_addr.s_addr) {
01446       return -1;
01447    }
01448    dst = &stunaddr;
01449 
01450    sock = socket(AF_INET, SOCK_DGRAM, 0);
01451    if (sock < 0) {
01452       ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
01453       return -1;
01454    }
01455 
01456    ast_sockaddr_from_sin(&tmp_dst, dst);
01457    if (ast_connect(sock, &tmp_dst) != 0) {
01458       ast_log(LOG_WARNING, "STUN Failed to connect to %s\n", ast_sockaddr_stringify(&tmp_dst));
01459       close(sock);
01460       return -1;
01461    }
01462 
01463    if ((ast_stun_request(sock, &stunaddr, NULL, &answer))) {
01464       close(sock);
01465       return -1;
01466    }
01467 
01468    newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
01469    memcpy(externip, newaddr, sizeof(externip));
01470 
01471    close(sock);
01472    return 0;
01473 
01474 }
01475 
01476 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
01477 {
01478    struct gtalk_candidate *tmp;
01479    struct hostent *hp;
01480    struct ast_hostent ahp;
01481    struct sockaddr_in sin = { 0, };
01482    struct sockaddr_in aux = { 0, };
01483    struct ast_sockaddr sin_tmp;
01484    struct ast_sockaddr aux_tmp;
01485 
01486    if (time(NULL) == p->laststun)
01487       return 0;
01488 
01489    tmp = p->theircandidates;
01490    p->laststun = time(NULL);
01491    while (tmp) {
01492       char username[256];
01493 
01494       /* Find the IP address of the host */
01495       if (!(hp = ast_gethostbyname(tmp->ip, &ahp))) {
01496          ast_log(LOG_WARNING, "Could not get host by name for %s\n", tmp->ip);
01497          tmp = tmp->next;
01498          continue;
01499       }
01500       sin.sin_family = AF_INET;
01501       memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
01502       sin.sin_port = htons(tmp->port);
01503       snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);
01504 
01505       /* Find out the result of the STUN */
01506       ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
01507       ast_sockaddr_to_sin(&aux_tmp, &aux);
01508 
01509       /* If the STUN result is different from the IP of the hostname,
01510        * lock on the stun IP of the hostname advertised by the
01511        * remote client */
01512       if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
01513          ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
01514       } else {
01515          ast_sockaddr_from_sin(&sin_tmp, &sin);
01516          ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
01517       }
01518       if (aux.sin_addr.s_addr) {
01519          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);
01520          ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
01521       }
01522 
01523       tmp = tmp->next;
01524    }
01525    return 1;
01526 }
01527 
01528 static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
01529 {
01530    struct gtalk_pvt *p = NULL, *tmp = NULL;
01531    struct aji_client *c = client->connection;
01532    struct gtalk_candidate *newcandidate = NULL;
01533    iks *traversenodes = NULL, *receipt = NULL;
01534    char *from;
01535 
01536    from = iks_find_attrib(pak->x,"to");
01537    if (!from) {
01538       from = c->jid->full;
01539    }
01540 
01541    for (tmp = client->p; tmp; tmp = tmp->next) {
01542       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01543          (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01544          p = tmp;
01545          break;
01546       }
01547    }
01548 
01549    if (!p) {
01550       return -1;
01551    }
01552    traversenodes = pak->query;
01553    while(traversenodes) {
01554       if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
01555          traversenodes = iks_first_tag(traversenodes);
01556          continue;
01557       }
01558       if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
01559          traversenodes = iks_child(traversenodes);
01560          continue;
01561       }
01562       if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
01563          newcandidate = ast_calloc(1, sizeof(*newcandidate));
01564          if (!newcandidate)
01565             return 0;
01566          ast_copy_string(newcandidate->name,
01567             S_OR(iks_find_attrib(traversenodes, "name"), ""),
01568             sizeof(newcandidate->name));
01569          ast_copy_string(newcandidate->ip,
01570             S_OR(iks_find_attrib(traversenodes, "address"), ""),
01571             sizeof(newcandidate->ip));
01572          newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
01573          ast_copy_string(newcandidate->username,
01574             S_OR(iks_find_attrib(traversenodes, "username"), ""),
01575             sizeof(newcandidate->username));
01576          ast_copy_string(newcandidate->password,
01577             S_OR(iks_find_attrib(traversenodes, "password"), ""),
01578             sizeof(newcandidate->password));
01579          newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
01580          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
01581             newcandidate->protocol = AJI_PROTOCOL_UDP;
01582          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
01583             newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
01584 
01585          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
01586             newcandidate->type = AJI_CONNECT_STUN;
01587          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
01588             newcandidate->type = AJI_CONNECT_LOCAL;
01589          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
01590             newcandidate->type = AJI_CONNECT_RELAY;
01591          ast_copy_string(newcandidate->network,
01592             S_OR(iks_find_attrib(traversenodes, "network"), ""),
01593             sizeof(newcandidate->network));
01594          newcandidate->generation = atoi(S_OR(iks_find_attrib(traversenodes, "generation"), "0"));
01595          newcandidate->next = NULL;
01596 
01597          newcandidate->next = p->theircandidates;
01598          p->theircandidates = newcandidate;
01599          p->laststun = 0;
01600          gtalk_update_stun(p->parent, p);
01601          newcandidate = NULL;
01602       }
01603       traversenodes = iks_next_tag(traversenodes);
01604    }
01605 
01606    receipt = iks_new("iq");
01607    iks_insert_attrib(receipt, "type", "result");
01608    iks_insert_attrib(receipt, "from", from);
01609    iks_insert_attrib(receipt, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
01610    iks_insert_attrib(receipt, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
01611    ast_aji_send(c, receipt);
01612 
01613    iks_delete(receipt);
01614 
01615    return 1;
01616 }
01617 
01618 static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
01619 {
01620    struct ast_frame *f;
01621 
01622    if (!p->rtp) {
01623       return &ast_null_frame;
01624    }
01625    f = ast_rtp_instance_read(p->rtp, 0);
01626    gtalk_update_stun(p->parent, p);
01627    if (p->owner) {
01628       /* We already hold the channel lock */
01629       if (f->frametype == AST_FRAME_VOICE) {
01630          if (!ast_format_cap_iscompatible(ast_channel_nativeformats(p->owner), &f->subclass.format)) {
01631             ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
01632             ast_format_cap_remove_bytype(ast_channel_nativeformats(p->owner), AST_FORMAT_TYPE_AUDIO);
01633             ast_format_cap_add(ast_channel_nativeformats(p->owner), &f->subclass.format);
01634             ast_set_read_format(p->owner, ast_channel_readformat(p->owner));
01635             ast_set_write_format(p->owner, ast_channel_writeformat(p->owner));
01636          }
01637          /* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
01638             f = ast_dsp_process(p->owner, p->vad, f);
01639             if (option_debug && f && (f->frametype == AST_FRAME_DTMF))
01640                ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
01641            } */
01642       }
01643    }
01644    return f;
01645 }
01646 
01647 static struct ast_frame *gtalk_read(struct ast_channel *ast)
01648 {
01649    struct ast_frame *fr;
01650    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01651 
01652    ast_mutex_lock(&p->lock);
01653    fr = gtalk_rtp_read(ast, p);
01654    ast_mutex_unlock(&p->lock);
01655    return fr;
01656 }
01657 
01658 /*! \brief Send frame to media channel (rtp) */
01659 static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
01660 {
01661    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01662    int res = 0;
01663    char buf[256];
01664 
01665    switch (frame->frametype) {
01666    case AST_FRAME_VOICE:
01667       if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format))) {
01668          ast_log(LOG_WARNING,
01669                "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
01670                ast_getformatname(&frame->subclass.format),
01671                ast_getformatname_multiple(buf, sizeof(buf), ast_channel_nativeformats(ast)),
01672                ast_getformatname(ast_channel_readformat(ast)),
01673                ast_getformatname(ast_channel_writeformat(ast)));
01674          return 0;
01675       }
01676       if (p) {
01677          ast_mutex_lock(&p->lock);
01678          if (p->rtp) {
01679             res = ast_rtp_instance_write(p->rtp, frame);
01680          }
01681          ast_mutex_unlock(&p->lock);
01682       }
01683       break;
01684    case AST_FRAME_VIDEO:
01685       if (p) {
01686          ast_mutex_lock(&p->lock);
01687          if (p->vrtp) {
01688             res = ast_rtp_instance_write(p->vrtp, frame);
01689          }
01690          ast_mutex_unlock(&p->lock);
01691       }
01692       break;
01693    case AST_FRAME_IMAGE:
01694       return 0;
01695       break;
01696    default:
01697       ast_log(LOG_WARNING, "Can't send %u type frames with Gtalk write\n",
01698             frame->frametype);
01699       return 0;
01700    }
01701 
01702    return res;
01703 }
01704 
01705 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
01706 {
01707    struct gtalk_pvt *p = ast_channel_tech_pvt(newchan);
01708    ast_mutex_lock(&p->lock);
01709 
01710    if ((p->owner != oldchan)) {
01711       ast_mutex_unlock(&p->lock);
01712       return -1;
01713    }
01714    if (p->owner == oldchan)
01715       p->owner = newchan;
01716    ast_mutex_unlock(&p->lock);
01717    return 0;
01718 }
01719 
01720 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
01721 {
01722    int res = 0;
01723 
01724    switch (condition) {
01725    case AST_CONTROL_HOLD:
01726       ast_moh_start(ast, data, NULL);
01727       break;
01728    case AST_CONTROL_UNHOLD:
01729       ast_moh_stop(ast);
01730       break;
01731    default:
01732       ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
01733       /* fallthrough */
01734    case AST_CONTROL_PVT_CAUSE_CODE:
01735       res = -1;
01736    }
01737 
01738    return res;
01739 }
01740 
01741 static int gtalk_sendtext(struct ast_channel *chan, const char *text)
01742 {
01743    int res = 0;
01744    struct aji_client *client = NULL;
01745    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
01746 
01747    if (!p->parent) {
01748       ast_log(LOG_ERROR, "Parent channel not found\n");
01749       return -1;
01750    }
01751    if (!p->parent->connection) {
01752       ast_log(LOG_ERROR, "XMPP client not found\n");
01753       return -1;
01754    }
01755    client = p->parent->connection;
01756    res = ast_aji_send_chat(client, p->them, text);
01757    return res;
01758 }
01759 
01760 static int gtalk_digit_begin(struct ast_channel *chan, char digit)
01761 {
01762    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
01763    int res = 0;
01764 
01765    ast_mutex_lock(&p->lock);
01766    if (p->rtp) {
01767       ast_rtp_instance_dtmf_begin(p->rtp, digit);
01768    } else {
01769       res = -1;
01770    }
01771    ast_mutex_unlock(&p->lock);
01772 
01773    return res;
01774 }
01775 
01776 static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
01777 {
01778    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
01779    int res = 0;
01780 
01781    ast_mutex_lock(&p->lock);
01782    if (p->rtp) {
01783       ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
01784    } else {
01785       res = -1;
01786    }
01787    ast_mutex_unlock(&p->lock);
01788 
01789    return res;
01790 }
01791 
01792 /* This function is of not in use at the moment, but I am choosing to leave this
01793  * within the code base as a reference to how DTMF is possible through
01794  * jingle signaling.  However, google currently does DTMF through the RTP. */
01795 #if 0
01796 static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
01797 {
01798    struct gtalk_pvt *p = ast->tech_pvt;
01799    struct gtalk *client = p->parent;
01800    iks *iq, *gtalk, *dtmf;
01801    char buffer[2] = {digit, '\0'};
01802    char *lowerthem = NULL;
01803    iq = iks_new("iq");
01804    gtalk = iks_new("gtalk");
01805    dtmf = iks_new("dtmf");
01806    if(!iq || !gtalk || !dtmf) {
01807       iks_delete(iq);
01808       iks_delete(gtalk);
01809       iks_delete(dtmf);
01810       ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
01811       return -1;
01812    }
01813 
01814    iks_insert_attrib(iq, "type", "set");
01815    iks_insert_attrib(iq, "to", p->them);
01816    iks_insert_attrib(iq, "from", p->us);
01817    iks_insert_attrib(iq, "id", client->connection->mid);
01818    ast_aji_increment_mid(client->connection->mid);
01819    iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
01820    iks_insert_attrib(gtalk, "action", "session-info");
01821    // put the initiator attribute to lower case if we receive the call
01822    // otherwise GoogleTalk won't establish the session
01823    if (!p->initiator) {
01824            char c;
01825            char *t = lowerthem = ast_strdupa(p->them);
01826            while (((c = *t) != '/') && (*t++ = tolower(c)));
01827    }
01828    iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: lowerthem);
01829    iks_insert_attrib(gtalk, "sid", p->sid);
01830    iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
01831    iks_insert_attrib(dtmf, "code", buffer);
01832    iks_insert_node(iq, gtalk);
01833    iks_insert_node(gtalk, dtmf);
01834 
01835    ast_mutex_lock(&p->lock);
01836    if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
01837       iks_insert_attrib(dtmf, "action", "button-down");
01838    } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
01839       iks_insert_attrib(dtmf, "action", "button-up");
01840    }
01841    ast_aji_send(client->connection, iq);
01842 
01843    iks_delete(iq);
01844    iks_delete(gtalk);
01845    iks_delete(dtmf);
01846    ast_mutex_unlock(&p->lock);
01847    return 0;
01848 }
01849 #endif
01850 
01851 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
01852 {
01853    ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
01854 
01855    return -1;
01856 }
01857 
01858 /*!\brief Initiate new call, part of PBX interface
01859  * dest is the dial string */
01860 static int gtalk_call(struct ast_channel *ast, const char *dest, int timeout)
01861 {
01862    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01863 
01864    if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
01865       ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
01866       return -1;
01867    }
01868 
01869    ast_setstate(ast, AST_STATE_RING);
01870    if (!p->ringrule) {
01871       ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
01872       p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
01873                      IKS_RULE_ID, p->ring, IKS_RULE_DONE);
01874    } else {
01875       ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
01876    }
01877 
01878    gtalk_invite(p, p->them, p->us, p->sid, 1);
01879 
01880    return 0;
01881 }
01882 
01883 /*! \brief Hangup a call through the gtalk proxy channel */
01884 static int gtalk_hangup(struct ast_channel *ast)
01885 {
01886    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01887    struct gtalk *client;
01888 
01889    ast_mutex_lock(&p->lock);
01890    client = p->parent;
01891    p->owner = NULL;
01892    ast_channel_tech_pvt_set(ast, NULL);
01893    if (!p->alreadygone) {
01894       gtalk_action(client, p, "terminate");
01895    }
01896    ast_mutex_unlock(&p->lock);
01897 
01898    gtalk_free_pvt(client, p);
01899    ast_module_unref(ast_module_info->self);
01900 
01901    return 0;
01902 }
01903 
01904 /*!\brief Part of PBX interface */
01905 static struct ast_channel *gtalk_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
01906 {
01907    struct gtalk_pvt *p = NULL;
01908    struct gtalk *client = NULL;
01909    char *sender = NULL, *to = NULL, *s = NULL;
01910    struct ast_channel *chan = NULL;
01911 
01912    if (data) {
01913       s = ast_strdupa(data);
01914       sender = strsep(&s, "/");
01915       if (sender && (sender[0] != '\0')) {
01916          to = strsep(&s, "/");
01917       }
01918       if (!to) {
01919          ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", data);
01920          return NULL;
01921       }
01922       if (!to) {
01923          ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
01924          return NULL;
01925       }
01926    }
01927 
01928    client = find_gtalk(to, sender);
01929    if (!client) {
01930       ast_log(LOG_WARNING, "Could not find recipient.\n");
01931       return NULL;
01932    }
01933    if (!strcasecmp(client->name, "guest")){
01934       /* the guest account is not tied to any configured XMPP client,
01935          let's set it now */
01936       if (client->connection) {
01937          ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01938       }
01939       client->connection = ast_aji_get_client(sender);
01940       if (!client->connection) {
01941          ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
01942          ASTOBJ_UNREF(client, gtalk_member_destroy);
01943          return NULL;
01944       }
01945    }
01946 
01947    ASTOBJ_WRLOCK(client);
01948    p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
01949    if (p) {
01950       chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? ast_channel_linkedid(requestor) : NULL);
01951    }
01952    ASTOBJ_UNLOCK(client);
01953    return chan;
01954 }
01955 
01956 /*! \brief CLI command "gtalk show channels" */
01957 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01958 {
01959 #define FORMAT  "%-30.30s  %-30.30s  %-15.15s  %-5.5s %-5.5s \n"
01960    struct gtalk_pvt *p;
01961    struct ast_channel *chan;
01962    int numchans = 0;
01963    char them[AJI_MAX_JIDLEN];
01964    char *jid = NULL;
01965    char *resource = NULL;
01966 
01967    switch (cmd) {
01968    case CLI_INIT:
01969       e->command = "gtalk show channels";
01970       e->usage =
01971          "Usage: gtalk show channels\n"
01972          "       Shows current state of the Gtalk channels.\n";
01973       return NULL;
01974    case CLI_GENERATE:
01975       return NULL;
01976    }
01977 
01978    if (a->argc != 3)
01979       return CLI_SHOWUSAGE;
01980 
01981    ast_mutex_lock(&gtalklock);
01982    ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
01983    ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
01984       ASTOBJ_WRLOCK(iterator);
01985       p = iterator->p;
01986       while(p) {
01987          chan = p->owner;
01988          ast_copy_string(them, p->them, sizeof(them));
01989          jid = them;
01990          resource = strchr(them, '/');
01991          if (!resource)
01992             resource = "None";
01993          else {
01994             *resource = '\0';
01995             resource ++;
01996          }
01997          if (chan)
01998             ast_cli(a->fd, FORMAT,
01999                ast_channel_name(chan),
02000                jid,
02001                resource,
02002                ast_getformatname(ast_channel_readformat(chan)),
02003                ast_getformatname(ast_channel_writeformat(chan))
02004                );
02005          else
02006             ast_log(LOG_WARNING, "No available channel\n");
02007          numchans ++;
02008          p = p->next;
02009       }
02010       ASTOBJ_UNLOCK(iterator);
02011    });
02012 
02013    ast_mutex_unlock(&gtalklock);
02014 
02015    ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
02016    return CLI_SUCCESS;
02017 #undef FORMAT
02018 }
02019 
02020 /*! \brief List global settings for the GoogleTalk channel */
02021 static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02022 {
02023    char codec_buf[BUFSIZ];
02024    switch (cmd) {
02025    case CLI_INIT:
02026       e->command = "gtalk show settings";
02027       e->usage =
02028          "Usage: gtalk show settings\n"
02029          "       Provides detailed list of the configuration on the GoogleTalk channel.\n";
02030       return NULL;
02031    case CLI_GENERATE:
02032       return NULL;
02033    }
02034 
02035    if (a->argc != 3) {
02036       return CLI_SHOWUSAGE;
02037    }
02038 
02039 #define FORMAT "  %-25.20s  %-15.30s\n"
02040 
02041    ast_cli(a->fd, "\nGlobal Settings:\n");
02042    ast_cli(a->fd, "----------------\n");
02043    ast_cli(a->fd, FORMAT, "UDP Bindaddress:", ast_inet_ntoa(bindaddr.sin_addr));
02044    ast_cli(a->fd, FORMAT, "Stun Address:", global_stunaddr != 0 ? ast_inet_ntoa(stunaddr.sin_addr) : "Disabled");
02045    ast_cli(a->fd, FORMAT, "External IP:", S_OR(externip, "Disabled"));
02046    ast_cli(a->fd, FORMAT, "Context:", global_context);
02047    ast_cli(a->fd, FORMAT, "Codecs:", ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, global_capability));
02048    ast_cli(a->fd, FORMAT, "Parking Lot:", global_parkinglot);
02049    ast_cli(a->fd, FORMAT, "Allow Guest:", AST_CLI_YESNO(global_allowguest));
02050    ast_cli(a->fd, "\n----\n");
02051 
02052    return CLI_SUCCESS;
02053 #undef FORMAT
02054 }
02055 
02056 /*! \brief CLI command "gtalk reload"
02057  *  \todo XXX TODO make this work. */
02058 #if 0
02059 static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02060 {
02061    switch (cmd) {
02062    case CLI_INIT:
02063       e->command = "gtalk reload";
02064       e->usage =
02065          "Usage: gtalk reload\n"
02066          "       Reload gtalk channel driver.\n";
02067       return NULL;
02068    case CLI_GENERATE:
02069       return NULL;
02070    }
02071 
02072    ast_verbose("IT DOES WORK!\n");
02073    return CLI_SUCCESS;
02074 }
02075 #endif
02076 
02077 static int gtalk_parser(void *data, ikspak *pak)
02078 {
02079    struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
02080    int res;
02081    iks *tmp;
02082 
02083    if (!strcasecmp(iks_name(pak->query), "jin:jingle") && (tmp = iks_next(pak->query)) && !strcasecmp(iks_name(tmp), "ses:session")) {
02084       ast_debug(1, "New method detected. Skipping jingle offer and using old gtalk method.\n");
02085       pak->query = tmp;
02086    }
02087 
02088    if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
02089       ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
02090    }
02091 
02092    if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
02093       ast_log(LOG_NOTICE, "No attribute \"type\" found.  Ignoring message.\n");
02094    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
02095       /* New call */
02096       gtalk_newcall(client, pak);
02097    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
02098       ast_debug(3, "About to add candidate!\n");
02099       res = gtalk_add_candidate(client, pak);
02100       if (!res) {
02101          ast_log(LOG_WARNING, "Could not add any candidate\n");
02102       } else {
02103          ast_debug(3, "Candidate Added!\n");
02104       }
02105    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "accept")) {
02106       gtalk_is_answered(client, pak);
02107    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
02108       gtalk_is_accepted(client, pak);
02109    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
02110       gtalk_handle_dtmf(client, pak);
02111    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
02112       gtalk_hangup_farend(client, pak);
02113    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
02114       gtalk_hangup_farend(client, pak);
02115    }
02116    ASTOBJ_UNREF(client, gtalk_member_destroy);
02117    return IKS_FILTER_EAT;
02118 }
02119 
02120 static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
02121                         struct ast_codec_pref prefs, char *context,
02122                         struct gtalk *member)
02123 {
02124    struct aji_client *client;
02125 
02126    if (!member)
02127       ast_log(LOG_WARNING, "Out of memory.\n");
02128 
02129    ast_copy_string(member->name, label, sizeof(member->name));
02130    ast_copy_string(member->user, label, sizeof(member->user));
02131    ast_copy_string(member->context, context, sizeof(member->context));
02132    member->allowguest = allowguest;
02133    member->prefs = prefs;
02134    while (var) {
02135       if (!strcasecmp(var->name, "username"))
02136          ast_copy_string(member->user, var->value, sizeof(member->user));
02137       else if (!strcasecmp(var->name, "disallow"))
02138          ast_parse_allow_disallow(&member->prefs, member->cap, var->value, 0);
02139       else if (!strcasecmp(var->name, "allow"))
02140          ast_parse_allow_disallow(&member->prefs, member->cap, var->value, 1);
02141       else if (!strcasecmp(var->name, "context"))
02142          ast_copy_string(member->context, var->value, sizeof(member->context));
02143       else if (!strcasecmp(var->name, "parkinglot"))
02144          ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
02145       else if (!strcasecmp(var->name, "connection")) {
02146          if ((client = ast_aji_get_client(var->value))) {
02147             member->connection = client;
02148             iks_filter_add_rule(client->f, gtalk_parser, member,
02149                       IKS_RULE_TYPE, IKS_PAK_IQ,
02150                       IKS_RULE_FROM_PARTIAL, member->user,
02151                       IKS_RULE_NS, GOOGLE_NS,
02152                       IKS_RULE_DONE);
02153          } else {
02154             ast_log(LOG_ERROR, "connection referenced not found!\n");
02155             return 0;
02156          }
02157       }
02158       var = var->next;
02159    }
02160    if (member->connection && member->user)
02161       member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
02162    else {
02163       ast_log(LOG_ERROR, "No Connection or Username!\n");
02164    }
02165    return 1;
02166 }
02167 
02168 static int gtalk_load_config(void)
02169 {
02170    char *cat = NULL;
02171    struct ast_config *cfg = NULL;
02172    struct ast_variable *var;
02173    struct gtalk *member;
02174    struct ast_codec_pref prefs;
02175    struct aji_client_container *clients;
02176    struct gtalk_candidate *global_candidates = NULL;
02177    struct hostent *hp;
02178    struct ast_hostent ahp;
02179    struct ast_flags config_flags = { 0 };
02180 
02181    cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
02182    if (!cfg) {
02183       return 0;
02184    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02185       ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", GOOGLE_CONFIG);
02186       return 0;
02187    }
02188 
02189    /* Copy the default jb config over global_jbconf */
02190    memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
02191 
02192    /* set defaults */
02193    memset(&prefs, 0, sizeof(prefs));
02194    memset(&stunaddr, 0, sizeof(stunaddr));
02195    global_stunaddr = 0;
02196    global_allowguest = DEFAULT_ALLOWGUEST;
02197    ast_copy_string(global_context, DEFAULT_CONTEXT, sizeof(global_context));
02198    ast_copy_string(global_parkinglot, DEFAULT_PARKINGLOT, sizeof(global_parkinglot));
02199 
02200    cat = ast_category_browse(cfg, NULL);
02201    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02202       /* handle jb conf */
02203       if (!ast_jb_read_conf(&global_jbconf, var->name, var->value)) {
02204          continue;
02205       }
02206 
02207       if (!strcasecmp(var->name, "allowguest")) {
02208          global_allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
02209       } else if (!strcasecmp(var->name, "disallow")) {
02210          ast_parse_allow_disallow(&prefs, global_capability, var->value, 0);
02211       } else if (!strcasecmp(var->name, "allow")) {
02212          ast_parse_allow_disallow(&prefs, global_capability, var->value, 1);
02213       } else if (!strcasecmp(var->name, "context")) {
02214          ast_copy_string(global_context, var->value, sizeof(global_context));
02215       } else if (!strcasecmp(var->name, "externip")) {
02216          ast_copy_string(externip, var->value, sizeof(externip));
02217       } else if (!strcasecmp(var->name, "parkinglot")) {
02218          ast_copy_string(global_parkinglot, var->value, sizeof(global_parkinglot));
02219       } else if (!strcasecmp(var->name, "bindaddr")) {
02220          if (!(hp = ast_gethostbyname(var->value, &ahp))) {
02221             ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
02222          } else {
02223             memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
02224          }
02225       } else if (!strcasecmp(var->name, "stunaddr")) {
02226          stunaddr.sin_port = htons(STANDARD_STUN_PORT);
02227          global_stunaddr = 1;
02228          if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
02229             ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
02230          }
02231       }
02232    }
02233    while (cat) {
02234       if (strcasecmp(cat, "general")) {
02235          var = ast_variable_browse(cfg, cat);
02236          member = ast_calloc(1, sizeof(*member));
02237          ASTOBJ_INIT(member);
02238          ASTOBJ_WRLOCK(member);
02239          member->cap = ast_format_cap_alloc_nolock();
02240          if (!strcasecmp(cat, "guest")) {
02241             ast_copy_string(member->name, "guest", sizeof(member->name));
02242             ast_copy_string(member->user, "guest", sizeof(member->user));
02243             ast_copy_string(member->context, global_context, sizeof(member->context));
02244             ast_copy_string(member->parkinglot, global_parkinglot, sizeof(member->parkinglot));
02245             member->allowguest = global_allowguest;
02246             member->prefs = prefs;
02247             while (var) {
02248                if (!strcasecmp(var->name, "disallow")) {
02249                   ast_parse_allow_disallow(&member->prefs, member->cap,
02250                                      var->value, 0);
02251                } else if (!strcasecmp(var->name, "allow")) {
02252                   ast_parse_allow_disallow(&member->prefs, member->cap,
02253                                      var->value, 1);
02254                } else if (!strcasecmp(var->name, "context")) {
02255                   ast_copy_string(member->context, var->value,
02256                               sizeof(member->context));
02257                } else if (!strcasecmp(var->name, "parkinglot")) {
02258                   ast_copy_string(member->parkinglot, var->value,
02259                               sizeof(member->parkinglot));
02260                }
02261                var = var->next;
02262             }
02263             ASTOBJ_UNLOCK(member);
02264             clients = ast_aji_get_clients();
02265             if (clients) {
02266                ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
02267                   ASTOBJ_WRLOCK(iterator);
02268                   ASTOBJ_WRLOCK(member);
02269                   if (member->connection) {
02270                      ASTOBJ_UNREF(member->connection, ast_aji_client_destroy);
02271                   }
02272                   member->connection = NULL;
02273                   iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
02274                   iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_JINGLE_NS, IKS_RULE_DONE);
02275                   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);
02276                   ASTOBJ_UNLOCK(member);
02277                   ASTOBJ_UNLOCK(iterator);
02278                });
02279                ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
02280                ASTOBJ_UNREF(member, gtalk_member_destroy);
02281             } else {
02282                ASTOBJ_UNLOCK(member);
02283                ASTOBJ_UNREF(member, gtalk_member_destroy);
02284             }
02285          } else {
02286             ASTOBJ_UNLOCK(member);
02287             if (gtalk_create_member(cat, var, global_allowguest, prefs, global_context, member)) {
02288                ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
02289             }
02290             ASTOBJ_UNREF(member, gtalk_member_destroy);
02291          }
02292       }
02293       cat = ast_category_browse(cfg, cat);
02294    }
02295 
02296    ast_config_destroy(cfg);
02297    gtalk_update_externip();
02298    gtalk_free_candidates(global_candidates);
02299    return 1;
02300 }
02301 
02302 /*! \brief Load module into PBX, register channel */
02303 static int load_module(void)
02304 {
02305    struct ast_sockaddr bindaddr_tmp;
02306    struct ast_sockaddr ourip_tmp;
02307    char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
02308    struct ast_format tmpfmt;
02309 
02310    if (!(gtalk_tech.capabilities = ast_format_cap_alloc())) {
02311       return AST_MODULE_LOAD_DECLINE;
02312    }
02313    if (!(global_capability = ast_format_cap_alloc())) {
02314       return AST_MODULE_LOAD_DECLINE;
02315    }
02316 
02317    ast_format_cap_add_all_by_type(gtalk_tech.capabilities, AST_FORMAT_TYPE_AUDIO);
02318    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0));
02319    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0));
02320    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0));
02321    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_H263, 0));
02322 
02323    free(jabber_loaded);
02324    if (!jabber_loaded) {
02325       /* If embedded, check for a different module name */
02326       jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0);
02327       free(jabber_loaded);
02328       if (!jabber_loaded) {
02329          ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n");
02330          return AST_MODULE_LOAD_DECLINE;
02331       }
02332    }
02333 
02334    ASTOBJ_CONTAINER_INIT(&gtalk_list);
02335    if (!gtalk_load_config()) {
02336       ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
02337       return 0;
02338    }
02339 
02340    sched = ast_sched_context_create();
02341    if (!sched) {
02342       ast_log(LOG_WARNING, "Unable to create schedule context\n");
02343    }
02344 
02345    io = io_context_create();
02346    if (!io) {
02347       ast_log(LOG_WARNING, "Unable to create I/O context\n");
02348    }
02349 
02350    ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
02351    if (gtalk_get_local_ip(&ourip_tmp)) {
02352       ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
02353       return 0;
02354    }
02355    __ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));
02356 
02357    ast_rtp_glue_register(&gtalk_rtp_glue);
02358    ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02359 
02360    /* Make sure we can register our channel type */
02361    if (ast_channel_register(&gtalk_tech)) {
02362       ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
02363       return -1;
02364    }
02365    return 0;
02366 }
02367 
02368 /*! \brief Reload module
02369  *  \todo XXX TODO make this work. */
02370 #if 0
02371 static int reload(void)
02372 {
02373    return 0;
02374 }
02375 #endif
02376 /*! \brief Unload the gtalk channel from Asterisk */
02377 static int unload_module(void)
02378 {
02379    struct gtalk_pvt *privates = NULL;
02380    ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02381    /* First, take us out of the channel loop */
02382    ast_channel_unregister(&gtalk_tech);
02383    ast_rtp_glue_unregister(&gtalk_rtp_glue);
02384 
02385    if (!ast_mutex_lock(&gtalklock)) {
02386       /* Hangup all interfaces if they have an owner */
02387       ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
02388          ASTOBJ_WRLOCK(iterator);
02389          privates = iterator->p;
02390          while(privates) {
02391             if (privates->owner)
02392                ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
02393             privates = privates->next;
02394          }
02395          iterator->p = NULL;
02396          ASTOBJ_UNLOCK(iterator);
02397       });
02398       ast_mutex_unlock(&gtalklock);
02399    } else {
02400       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
02401       return -1;
02402    }
02403    ASTOBJ_CONTAINER_DESTROYALL(&gtalk_list, gtalk_member_destroy);
02404    ASTOBJ_CONTAINER_DESTROY(&gtalk_list);
02405    global_capability = ast_format_cap_destroy(global_capability);
02406    gtalk_tech.capabilities = ast_format_cap_destroy(gtalk_tech.capabilities);
02407    return 0;
02408 }
02409 
02410 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gtalk Channel Driver",
02411       .load = load_module,
02412       .unload = unload_module,
02413       /* .reload = reload, */
02414       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02415       );