Sat Apr 26 2014 22:01:30

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: 372795 $")
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", ast_random(), ast_random());
00920    snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), 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", ast_random(), 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, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff);
01129    if (!tmp) {
01130       ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
01131       return NULL;
01132    }
01133    ast_channel_tech_set(tmp, &gtalk_tech);
01134 
01135    /* Select our native format based on codec preference until we receive
01136       something from another device to the contrary. */
01137    if (!(ast_format_cap_is_empty(i->jointcap))) {
01138       what = i->jointcap;
01139    } else if (i->cap) {
01140       what = i->cap;
01141    } else {
01142       what = global_capability;
01143    }
01144 
01145    /* Set Frame packetization */
01146    if (i->rtp) {
01147       ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
01148    }
01149 
01150    ast_codec_choose(&i->prefs, what, 1, &tmpfmt);
01151    ast_format_cap_add(ast_channel_nativeformats(tmp), &tmpfmt);
01152 
01153    ast_format_cap_iter_start(i->jointcap);
01154    while (!(ast_format_cap_iter_next(i->jointcap, &tmpfmt))) {
01155       if (AST_FORMAT_GET_TYPE(tmpfmt.id) == AST_FORMAT_TYPE_VIDEO) {
01156          ast_format_cap_add(ast_channel_nativeformats(tmp), &tmpfmt);
01157       }
01158    }
01159    ast_format_cap_iter_end(i->jointcap);
01160 
01161    if (i->rtp) {
01162       ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
01163       ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
01164    }
01165    if (i->vrtp) {
01166       ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
01167       ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
01168    }
01169    if (state == AST_STATE_RING)
01170       ast_channel_rings_set(tmp, 1);
01171    ast_channel_adsicpe_set(tmp, AST_ADSI_UNAVAILABLE);
01172 
01173    ast_best_codec(ast_channel_nativeformats(tmp), &tmpfmt);
01174    ast_format_copy(ast_channel_writeformat(tmp), &tmpfmt);
01175    ast_format_copy(ast_channel_rawwriteformat(tmp), &tmpfmt);
01176    ast_format_copy(ast_channel_readformat(tmp), &tmpfmt);
01177    ast_format_copy(ast_channel_rawreadformat(tmp), &tmpfmt);
01178    ast_channel_tech_pvt_set(tmp, i);
01179 
01180    ast_channel_callgroup_set(tmp, client->callgroup);
01181    ast_channel_pickupgroup_set(tmp, client->pickupgroup);
01182    ast_channel_caller(tmp)->id.name.presentation = client->callingpres;
01183    ast_channel_caller(tmp)->id.number.presentation = client->callingpres;
01184    if (!ast_strlen_zero(client->accountcode))
01185       ast_channel_accountcode_set(tmp, client->accountcode);
01186    if (client->amaflags)
01187       ast_channel_amaflags_set(tmp, client->amaflags);
01188    if (!ast_strlen_zero(client->language))
01189       ast_channel_language_set(tmp, client->language);
01190    if (!ast_strlen_zero(client->musicclass))
01191       ast_channel_musicclass_set(tmp, client->musicclass);
01192    if (!ast_strlen_zero(client->parkinglot))
01193       ast_channel_parkinglot_set(tmp, client->parkinglot);
01194    i->owner = tmp;
01195    ast_module_ref(ast_module_info->self);
01196    ast_channel_context_set(tmp, client->context);
01197    ast_channel_exten_set(tmp, i->exten);
01198 
01199    if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
01200       ast_channel_dialed(tmp)->number.str = ast_strdup(i->exten);
01201    }
01202    ast_channel_priority_set(tmp, 1);
01203    if (i->rtp)
01204       ast_jb_configure(tmp, &global_jbconf);
01205    if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
01206       ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(tmp));
01207       ast_channel_hangupcause_set(tmp, AST_CAUSE_SWITCH_CONGESTION);
01208       ast_hangup(tmp);
01209       tmp = NULL;
01210    } else {
01211       manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
01212          "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
01213          i->owner ? ast_channel_name(i->owner) : "", "Gtalk", i->sid);
01214    }
01215    return tmp;
01216 }
01217 
01218 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
01219 {
01220    iks *request, *session = NULL;
01221    int res = -1;
01222    char *lowerthem = NULL;
01223 
01224    request = iks_new("iq");
01225    if (request) {
01226       iks_insert_attrib(request, "type", "set");
01227       iks_insert_attrib(request, "from", p->us);
01228       iks_insert_attrib(request, "to", p->them);
01229       iks_insert_attrib(request, "id", client->connection->mid);
01230       ast_aji_increment_mid(client->connection->mid);
01231       session = iks_new("session");
01232       if (session) {
01233          iks_insert_attrib(session, "type", action);
01234          iks_insert_attrib(session, "id", p->sid);
01235          /* put the initiator attribute to lower case if we receive the call
01236           * otherwise GoogleTalk won't establish the session */
01237          if (!p->initiator) {
01238                  char c;
01239             char *t = lowerthem = ast_strdupa(p->them);
01240             while (((c = *t) != '/') && (*t++ = tolower(c)));
01241          }
01242          iks_insert_attrib(session, "initiator", p->initiator ? p->us : lowerthem);
01243          iks_insert_attrib(session, "xmlns", GOOGLE_NS);
01244          iks_insert_node(request, session);
01245          ast_aji_send(client->connection, request);
01246          res = 0;
01247       }
01248    }
01249 
01250    iks_delete(session);
01251    iks_delete(request);
01252 
01253    return res;
01254 }
01255 
01256 static void gtalk_free_candidates(struct gtalk_candidate *candidate)
01257 {
01258    struct gtalk_candidate *last;
01259    while (candidate) {
01260       last = candidate;
01261       candidate = candidate->next;
01262       ast_free(last);
01263    }
01264 }
01265 
01266 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
01267 {
01268    struct gtalk_pvt *cur, *prev = NULL;
01269    cur = client->p;
01270    while (cur) {
01271       if (cur == p) {
01272          if (prev)
01273             prev->next = p->next;
01274          else
01275             client->p = p->next;
01276          break;
01277       }
01278       prev = cur;
01279       cur = cur->next;
01280    }
01281    if (p->ringrule)
01282       iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
01283    if (p->owner)
01284       ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
01285    if (p->rtp)
01286       ast_rtp_instance_destroy(p->rtp);
01287    if (p->vrtp)
01288       ast_rtp_instance_destroy(p->vrtp);
01289    gtalk_free_candidates(p->theircandidates);
01290    p->cap = ast_format_cap_destroy(p->cap);
01291    p->jointcap = ast_format_cap_destroy(p->jointcap);
01292    p->peercap = ast_format_cap_destroy(p->peercap);
01293    ast_free(p);
01294 }
01295 
01296 
01297 static int gtalk_newcall(struct gtalk *client, ikspak *pak)
01298 {
01299    struct gtalk_pvt *p, *tmp = client->p;
01300    struct ast_channel *chan;
01301    int res;
01302    iks *codec;
01303    char *from = NULL;
01304    char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
01305    int peernoncodeccapability;
01306    char *sid;
01307 
01308    /* Make sure our new call doesn't exist yet */
01309    from = iks_find_attrib(pak->x,"to");
01310    if (!from) {
01311       from = client->connection->jid->full;
01312    }
01313 
01314    while (tmp) {
01315       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01316          (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01317          ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
01318          gtalk_response(client, from, pak, "out-of-order", NULL);
01319          return -1;
01320       }
01321       tmp = tmp->next;
01322    }
01323 
01324    if (!strcasecmp(client->name, "guest")){
01325       /* the guest account is not tied to any configured XMPP client,
01326          let's set it now */
01327       if (client->connection) {
01328          ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01329       }
01330       client->connection = ast_aji_get_client(from);
01331       if (!client->connection) {
01332          ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
01333          return -1;
01334       }
01335    }
01336 
01337    if (!(sid = iks_find_attrib(pak->query, "id"))) {
01338       ast_log(LOG_WARNING, "Received Initiate without id attribute. Can not start call.\n");
01339       return -1;
01340    }
01341 
01342    p = gtalk_alloc(client, from, pak->from->full, sid);
01343    if (!p) {
01344       ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
01345       return -1;
01346    }
01347 
01348    chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
01349    if (!chan) {
01350       gtalk_free_pvt(client, p);
01351       return -1;
01352    }
01353 
01354    ast_mutex_lock(&p->lock);
01355    ast_copy_string(p->them, pak->from->full, sizeof(p->them));
01356    ast_copy_string(p->sid, sid, sizeof(p->sid));
01357 
01358    /* codec points to the first <payload-type/> tag */
01359    codec = iks_first_tag(iks_first_tag(pak->query));
01360 
01361    while (codec) {
01362       char *codec_id = iks_find_attrib(codec, "id");
01363       char *codec_name = iks_find_attrib(codec, "name");
01364       if (!codec_id || !codec_name) {
01365          codec = iks_next_tag(codec);
01366          continue;
01367       }
01368       if (!strcmp(S_OR(iks_name(codec), ""), "vid:payload-type") && p->vrtp) {
01369          ast_rtp_codecs_payloads_set_m_type(
01370             ast_rtp_instance_get_codecs(p->vrtp),
01371             p->vrtp,
01372             atoi(codec_id));
01373          ast_rtp_codecs_payloads_set_rtpmap_type(
01374             ast_rtp_instance_get_codecs(p->vrtp),
01375             p->vrtp,
01376             atoi(codec_id),
01377             "video",
01378             codec_name,
01379             0);
01380       } else {
01381          ast_rtp_codecs_payloads_set_m_type(
01382             ast_rtp_instance_get_codecs(p->rtp),
01383             p->rtp,
01384             atoi(codec_id));
01385          ast_rtp_codecs_payloads_set_rtpmap_type(
01386             ast_rtp_instance_get_codecs(p->rtp),
01387             p->rtp,
01388             atoi(codec_id),
01389             "audio",
01390             codec_name,
01391             0);
01392       }
01393       codec = iks_next_tag(codec);
01394    }
01395 
01396    /* Now gather all of the codecs that we are asked for */
01397    ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), p->peercap, &peernoncodeccapability);
01398    ast_format_cap_joint_copy(p->cap, p->peercap, p->jointcap);
01399    ast_mutex_unlock(&p->lock);
01400 
01401    ast_setstate(chan, AST_STATE_RING);
01402    if (ast_format_cap_is_empty(p->jointcap)) {
01403       ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->cap),
01404          ast_getformatname_multiple(s2, BUFSIZ, p->peercap),
01405          ast_getformatname_multiple(s3, BUFSIZ, p->jointcap));
01406       /* close session if capabilities don't match */
01407       gtalk_action(client, p, "reject");
01408       p->alreadygone = 1;
01409       gtalk_hangup(chan);
01410       ast_channel_release(chan);
01411       return -1;
01412    }
01413 
01414    res = ast_pbx_start(chan);
01415 
01416    switch (res) {
01417    case AST_PBX_FAILED:
01418       ast_log(LOG_WARNING, "Failed to start PBX :(\n");
01419       gtalk_response(client, from, pak, "service-unavailable", NULL);
01420       break;
01421    case AST_PBX_CALL_LIMIT:
01422       ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
01423       gtalk_response(client, from, pak, "service-unavailable", NULL);
01424       break;
01425    case AST_PBX_SUCCESS:
01426       gtalk_response(client, from, pak, NULL, NULL);
01427       gtalk_create_candidates(client, p, p->sid, p->them, p->us);
01428       /* nothing to do */
01429       break;
01430    }
01431 
01432    return 1;
01433 }
01434 
01435 static int gtalk_update_externip(void)
01436 {
01437    int sock;
01438    char *newaddr;
01439    struct sockaddr_in answer = { 0, };
01440    struct sockaddr_in *dst;
01441    struct ast_sockaddr tmp_dst;
01442 
01443    if (!stunaddr.sin_addr.s_addr) {
01444       return -1;
01445    }
01446    dst = &stunaddr;
01447 
01448    sock = socket(AF_INET, SOCK_DGRAM, 0);
01449    if (sock < 0) {
01450       ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
01451       return -1;
01452    }
01453 
01454    ast_sockaddr_from_sin(&tmp_dst, dst);
01455    if (ast_connect(sock, &tmp_dst) != 0) {
01456       ast_log(LOG_WARNING, "STUN Failed to connect to %s\n", ast_sockaddr_stringify(&tmp_dst));
01457       close(sock);
01458       return -1;
01459    }
01460 
01461    if ((ast_stun_request(sock, &stunaddr, NULL, &answer))) {
01462       close(sock);
01463       return -1;
01464    }
01465 
01466    newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
01467    memcpy(externip, newaddr, sizeof(externip));
01468 
01469    close(sock);
01470    return 0;
01471 
01472 }
01473 
01474 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
01475 {
01476    struct gtalk_candidate *tmp;
01477    struct hostent *hp;
01478    struct ast_hostent ahp;
01479    struct sockaddr_in sin = { 0, };
01480    struct sockaddr_in aux = { 0, };
01481    struct ast_sockaddr sin_tmp;
01482    struct ast_sockaddr aux_tmp;
01483 
01484    if (time(NULL) == p->laststun)
01485       return 0;
01486 
01487    tmp = p->theircandidates;
01488    p->laststun = time(NULL);
01489    while (tmp) {
01490       char username[256];
01491 
01492       /* Find the IP address of the host */
01493       if (!(hp = ast_gethostbyname(tmp->ip, &ahp))) {
01494          ast_log(LOG_WARNING, "Could not get host by name for %s\n", tmp->ip);
01495          tmp = tmp->next;
01496          continue;
01497       }
01498       sin.sin_family = AF_INET;
01499       memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
01500       sin.sin_port = htons(tmp->port);
01501       snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);
01502 
01503       /* Find out the result of the STUN */
01504       ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
01505       ast_sockaddr_to_sin(&aux_tmp, &aux);
01506 
01507       /* If the STUN result is different from the IP of the hostname,
01508        * lock on the stun IP of the hostname advertised by the
01509        * remote client */
01510       if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
01511          ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
01512       } else {
01513          ast_sockaddr_from_sin(&sin_tmp, &sin);
01514          ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
01515       }
01516       if (aux.sin_addr.s_addr) {
01517          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);
01518          ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
01519       }
01520 
01521       tmp = tmp->next;
01522    }
01523    return 1;
01524 }
01525 
01526 static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
01527 {
01528    struct gtalk_pvt *p = NULL, *tmp = NULL;
01529    struct aji_client *c = client->connection;
01530    struct gtalk_candidate *newcandidate = NULL;
01531    iks *traversenodes = NULL, *receipt = NULL;
01532    char *from;
01533 
01534    from = iks_find_attrib(pak->x,"to");
01535    if (!from) {
01536       from = c->jid->full;
01537    }
01538 
01539    for (tmp = client->p; tmp; tmp = tmp->next) {
01540       if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01541          (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01542          p = tmp;
01543          break;
01544       }
01545    }
01546 
01547    if (!p) {
01548       return -1;
01549    }
01550    traversenodes = pak->query;
01551    while(traversenodes) {
01552       if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
01553          traversenodes = iks_first_tag(traversenodes);
01554          continue;
01555       }
01556       if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
01557          traversenodes = iks_child(traversenodes);
01558          continue;
01559       }
01560       if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
01561          newcandidate = ast_calloc(1, sizeof(*newcandidate));
01562          if (!newcandidate)
01563             return 0;
01564          ast_copy_string(newcandidate->name,
01565             S_OR(iks_find_attrib(traversenodes, "name"), ""),
01566             sizeof(newcandidate->name));
01567          ast_copy_string(newcandidate->ip,
01568             S_OR(iks_find_attrib(traversenodes, "address"), ""),
01569             sizeof(newcandidate->ip));
01570          newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
01571          ast_copy_string(newcandidate->username,
01572             S_OR(iks_find_attrib(traversenodes, "username"), ""),
01573             sizeof(newcandidate->username));
01574          ast_copy_string(newcandidate->password,
01575             S_OR(iks_find_attrib(traversenodes, "password"), ""),
01576             sizeof(newcandidate->password));
01577          newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
01578          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
01579             newcandidate->protocol = AJI_PROTOCOL_UDP;
01580          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
01581             newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
01582 
01583          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
01584             newcandidate->type = AJI_CONNECT_STUN;
01585          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
01586             newcandidate->type = AJI_CONNECT_LOCAL;
01587          if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
01588             newcandidate->type = AJI_CONNECT_RELAY;
01589          ast_copy_string(newcandidate->network,
01590             S_OR(iks_find_attrib(traversenodes, "network"), ""),
01591             sizeof(newcandidate->network));
01592          newcandidate->generation = atoi(S_OR(iks_find_attrib(traversenodes, "generation"), "0"));
01593          newcandidate->next = NULL;
01594 
01595          newcandidate->next = p->theircandidates;
01596          p->theircandidates = newcandidate;
01597          p->laststun = 0;
01598          gtalk_update_stun(p->parent, p);
01599          newcandidate = NULL;
01600       }
01601       traversenodes = iks_next_tag(traversenodes);
01602    }
01603 
01604    receipt = iks_new("iq");
01605    iks_insert_attrib(receipt, "type", "result");
01606    iks_insert_attrib(receipt, "from", from);
01607    iks_insert_attrib(receipt, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
01608    iks_insert_attrib(receipt, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
01609    ast_aji_send(c, receipt);
01610 
01611    iks_delete(receipt);
01612 
01613    return 1;
01614 }
01615 
01616 static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
01617 {
01618    struct ast_frame *f;
01619 
01620    if (!p->rtp) {
01621       return &ast_null_frame;
01622    }
01623    f = ast_rtp_instance_read(p->rtp, 0);
01624    gtalk_update_stun(p->parent, p);
01625    if (p->owner) {
01626       /* We already hold the channel lock */
01627       if (f->frametype == AST_FRAME_VOICE) {
01628          if (!ast_format_cap_iscompatible(ast_channel_nativeformats(p->owner), &f->subclass.format)) {
01629             ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
01630             ast_format_cap_remove_bytype(ast_channel_nativeformats(p->owner), AST_FORMAT_TYPE_AUDIO);
01631             ast_format_cap_add(ast_channel_nativeformats(p->owner), &f->subclass.format);
01632             ast_set_read_format(p->owner, ast_channel_readformat(p->owner));
01633             ast_set_write_format(p->owner, ast_channel_writeformat(p->owner));
01634          }
01635          /* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
01636             f = ast_dsp_process(p->owner, p->vad, f);
01637             if (option_debug && f && (f->frametype == AST_FRAME_DTMF))
01638                ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
01639            } */
01640       }
01641    }
01642    return f;
01643 }
01644 
01645 static struct ast_frame *gtalk_read(struct ast_channel *ast)
01646 {
01647    struct ast_frame *fr;
01648    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01649 
01650    ast_mutex_lock(&p->lock);
01651    fr = gtalk_rtp_read(ast, p);
01652    ast_mutex_unlock(&p->lock);
01653    return fr;
01654 }
01655 
01656 /*! \brief Send frame to media channel (rtp) */
01657 static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
01658 {
01659    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01660    int res = 0;
01661    char buf[256];
01662 
01663    switch (frame->frametype) {
01664    case AST_FRAME_VOICE:
01665       if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format))) {
01666          ast_log(LOG_WARNING,
01667                "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
01668                ast_getformatname(&frame->subclass.format),
01669                ast_getformatname_multiple(buf, sizeof(buf), ast_channel_nativeformats(ast)),
01670                ast_getformatname(ast_channel_readformat(ast)),
01671                ast_getformatname(ast_channel_writeformat(ast)));
01672          return 0;
01673       }
01674       if (p) {
01675          ast_mutex_lock(&p->lock);
01676          if (p->rtp) {
01677             res = ast_rtp_instance_write(p->rtp, frame);
01678          }
01679          ast_mutex_unlock(&p->lock);
01680       }
01681       break;
01682    case AST_FRAME_VIDEO:
01683       if (p) {
01684          ast_mutex_lock(&p->lock);
01685          if (p->vrtp) {
01686             res = ast_rtp_instance_write(p->vrtp, frame);
01687          }
01688          ast_mutex_unlock(&p->lock);
01689       }
01690       break;
01691    case AST_FRAME_IMAGE:
01692       return 0;
01693       break;
01694    default:
01695       ast_log(LOG_WARNING, "Can't send %d type frames with Gtalk write\n",
01696             frame->frametype);
01697       return 0;
01698    }
01699 
01700    return res;
01701 }
01702 
01703 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
01704 {
01705    struct gtalk_pvt *p = ast_channel_tech_pvt(newchan);
01706    ast_mutex_lock(&p->lock);
01707 
01708    if ((p->owner != oldchan)) {
01709       ast_mutex_unlock(&p->lock);
01710       return -1;
01711    }
01712    if (p->owner == oldchan)
01713       p->owner = newchan;
01714    ast_mutex_unlock(&p->lock);
01715    return 0;
01716 }
01717 
01718 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
01719 {
01720    int res = 0;
01721 
01722    switch (condition) {
01723    case AST_CONTROL_HOLD:
01724       ast_moh_start(ast, data, NULL);
01725       break;
01726    case AST_CONTROL_UNHOLD:
01727       ast_moh_stop(ast);
01728       break;
01729    default:
01730       ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
01731       /* fallthrough */
01732    case AST_CONTROL_PVT_CAUSE_CODE:
01733       res = -1;
01734    }
01735 
01736    return res;
01737 }
01738 
01739 static int gtalk_sendtext(struct ast_channel *chan, const char *text)
01740 {
01741    int res = 0;
01742    struct aji_client *client = NULL;
01743    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
01744 
01745    if (!p->parent) {
01746       ast_log(LOG_ERROR, "Parent channel not found\n");
01747       return -1;
01748    }
01749    if (!p->parent->connection) {
01750       ast_log(LOG_ERROR, "XMPP client not found\n");
01751       return -1;
01752    }
01753    client = p->parent->connection;
01754    res = ast_aji_send_chat(client, p->them, text);
01755    return res;
01756 }
01757 
01758 static int gtalk_digit_begin(struct ast_channel *chan, char digit)
01759 {
01760    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
01761    int res = 0;
01762 
01763    ast_mutex_lock(&p->lock);
01764    if (p->rtp) {
01765       ast_rtp_instance_dtmf_begin(p->rtp, digit);
01766    } else {
01767       res = -1;
01768    }
01769    ast_mutex_unlock(&p->lock);
01770 
01771    return res;
01772 }
01773 
01774 static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
01775 {
01776    struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
01777    int res = 0;
01778 
01779    ast_mutex_lock(&p->lock);
01780    if (p->rtp) {
01781       ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
01782    } else {
01783       res = -1;
01784    }
01785    ast_mutex_unlock(&p->lock);
01786 
01787    return res;
01788 }
01789 
01790 /* This function is of not in use at the moment, but I am choosing to leave this
01791  * within the code base as a reference to how DTMF is possible through
01792  * jingle signaling.  However, google currently does DTMF through the RTP. */
01793 #if 0
01794 static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
01795 {
01796    struct gtalk_pvt *p = ast->tech_pvt;
01797    struct gtalk *client = p->parent;
01798    iks *iq, *gtalk, *dtmf;
01799    char buffer[2] = {digit, '\0'};
01800    char *lowerthem = NULL;
01801    iq = iks_new("iq");
01802    gtalk = iks_new("gtalk");
01803    dtmf = iks_new("dtmf");
01804    if(!iq || !gtalk || !dtmf) {
01805       iks_delete(iq);
01806       iks_delete(gtalk);
01807       iks_delete(dtmf);
01808       ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
01809       return -1;
01810    }
01811 
01812    iks_insert_attrib(iq, "type", "set");
01813    iks_insert_attrib(iq, "to", p->them);
01814    iks_insert_attrib(iq, "from", p->us);
01815    iks_insert_attrib(iq, "id", client->connection->mid);
01816    ast_aji_increment_mid(client->connection->mid);
01817    iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
01818    iks_insert_attrib(gtalk, "action", "session-info");
01819    // put the initiator attribute to lower case if we receive the call
01820    // otherwise GoogleTalk won't establish the session
01821    if (!p->initiator) {
01822            char c;
01823            char *t = lowerthem = ast_strdupa(p->them);
01824            while (((c = *t) != '/') && (*t++ = tolower(c)));
01825    }
01826    iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: lowerthem);
01827    iks_insert_attrib(gtalk, "sid", p->sid);
01828    iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
01829    iks_insert_attrib(dtmf, "code", buffer);
01830    iks_insert_node(iq, gtalk);
01831    iks_insert_node(gtalk, dtmf);
01832 
01833    ast_mutex_lock(&p->lock);
01834    if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
01835       iks_insert_attrib(dtmf, "action", "button-down");
01836    } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
01837       iks_insert_attrib(dtmf, "action", "button-up");
01838    }
01839    ast_aji_send(client->connection, iq);
01840 
01841    iks_delete(iq);
01842    iks_delete(gtalk);
01843    iks_delete(dtmf);
01844    ast_mutex_unlock(&p->lock);
01845    return 0;
01846 }
01847 #endif
01848 
01849 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
01850 {
01851    ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
01852 
01853    return -1;
01854 }
01855 
01856 /*!\brief Initiate new call, part of PBX interface
01857  * dest is the dial string */
01858 static int gtalk_call(struct ast_channel *ast, const char *dest, int timeout)
01859 {
01860    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01861 
01862    if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
01863       ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
01864       return -1;
01865    }
01866 
01867    ast_setstate(ast, AST_STATE_RING);
01868    if (!p->ringrule) {
01869       ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
01870       p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
01871                      IKS_RULE_ID, p->ring, IKS_RULE_DONE);
01872    } else {
01873       ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
01874    }
01875 
01876    gtalk_invite(p, p->them, p->us, p->sid, 1);
01877 
01878    return 0;
01879 }
01880 
01881 /*! \brief Hangup a call through the gtalk proxy channel */
01882 static int gtalk_hangup(struct ast_channel *ast)
01883 {
01884    struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
01885    struct gtalk *client;
01886 
01887    ast_mutex_lock(&p->lock);
01888    client = p->parent;
01889    p->owner = NULL;
01890    ast_channel_tech_pvt_set(ast, NULL);
01891    if (!p->alreadygone) {
01892       gtalk_action(client, p, "terminate");
01893    }
01894    ast_mutex_unlock(&p->lock);
01895 
01896    gtalk_free_pvt(client, p);
01897    ast_module_unref(ast_module_info->self);
01898 
01899    return 0;
01900 }
01901 
01902 /*!\brief Part of PBX interface */
01903 static struct ast_channel *gtalk_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
01904 {
01905    struct gtalk_pvt *p = NULL;
01906    struct gtalk *client = NULL;
01907    char *sender = NULL, *to = NULL, *s = NULL;
01908    struct ast_channel *chan = NULL;
01909 
01910    if (data) {
01911       s = ast_strdupa(data);
01912       sender = strsep(&s, "/");
01913       if (sender && (sender[0] != '\0')) {
01914          to = strsep(&s, "/");
01915       }
01916       if (!to) {
01917          ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", data);
01918          return NULL;
01919       }
01920       if (!to) {
01921          ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
01922          return NULL;
01923       }
01924    }
01925 
01926    client = find_gtalk(to, sender);
01927    if (!client) {
01928       ast_log(LOG_WARNING, "Could not find recipient.\n");
01929       return NULL;
01930    }
01931    if (!strcasecmp(client->name, "guest")){
01932       /* the guest account is not tied to any configured XMPP client,
01933          let's set it now */
01934       if (client->connection) {
01935          ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01936       }
01937       client->connection = ast_aji_get_client(sender);
01938       if (!client->connection) {
01939          ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
01940          ASTOBJ_UNREF(client, gtalk_member_destroy);
01941          return NULL;
01942       }
01943    }
01944 
01945    ASTOBJ_WRLOCK(client);
01946    p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
01947    if (p) {
01948       chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? ast_channel_linkedid(requestor) : NULL);
01949    }
01950    ASTOBJ_UNLOCK(client);
01951    return chan;
01952 }
01953 
01954 /*! \brief CLI command "gtalk show channels" */
01955 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01956 {
01957 #define FORMAT  "%-30.30s  %-30.30s  %-15.15s  %-5.5s %-5.5s \n"
01958    struct gtalk_pvt *p;
01959    struct ast_channel *chan;
01960    int numchans = 0;
01961    char them[AJI_MAX_JIDLEN];
01962    char *jid = NULL;
01963    char *resource = NULL;
01964 
01965    switch (cmd) {
01966    case CLI_INIT:
01967       e->command = "gtalk show channels";
01968       e->usage =
01969          "Usage: gtalk show channels\n"
01970          "       Shows current state of the Gtalk channels.\n";
01971       return NULL;
01972    case CLI_GENERATE:
01973       return NULL;
01974    }
01975 
01976    if (a->argc != 3)
01977       return CLI_SHOWUSAGE;
01978 
01979    ast_mutex_lock(&gtalklock);
01980    ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
01981    ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
01982       ASTOBJ_WRLOCK(iterator);
01983       p = iterator->p;
01984       while(p) {
01985          chan = p->owner;
01986          ast_copy_string(them, p->them, sizeof(them));
01987          jid = them;
01988          resource = strchr(them, '/');
01989          if (!resource)
01990             resource = "None";
01991          else {
01992             *resource = '\0';
01993             resource ++;
01994          }
01995          if (chan)
01996             ast_cli(a->fd, FORMAT,
01997                ast_channel_name(chan),
01998                jid,
01999                resource,
02000                ast_getformatname(ast_channel_readformat(chan)),
02001                ast_getformatname(ast_channel_writeformat(chan))
02002                );
02003          else
02004             ast_log(LOG_WARNING, "No available channel\n");
02005          numchans ++;
02006          p = p->next;
02007       }
02008       ASTOBJ_UNLOCK(iterator);
02009    });
02010 
02011    ast_mutex_unlock(&gtalklock);
02012 
02013    ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
02014    return CLI_SUCCESS;
02015 #undef FORMAT
02016 }
02017 
02018 /*! \brief List global settings for the GoogleTalk channel */
02019 static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02020 {
02021    char codec_buf[BUFSIZ];
02022    switch (cmd) {
02023    case CLI_INIT:
02024       e->command = "gtalk show settings";
02025       e->usage =
02026          "Usage: gtalk show settings\n"
02027          "       Provides detailed list of the configuration on the GoogleTalk channel.\n";
02028       return NULL;
02029    case CLI_GENERATE:
02030       return NULL;
02031    }
02032 
02033    if (a->argc != 3) {
02034       return CLI_SHOWUSAGE;
02035    }
02036 
02037 #define FORMAT "  %-25.20s  %-15.30s\n"
02038 
02039    ast_cli(a->fd, "\nGlobal Settings:\n");
02040    ast_cli(a->fd, "----------------\n");
02041    ast_cli(a->fd, FORMAT, "UDP Bindaddress:", ast_inet_ntoa(bindaddr.sin_addr));
02042    ast_cli(a->fd, FORMAT, "Stun Address:", global_stunaddr != 0 ? ast_inet_ntoa(stunaddr.sin_addr) : "Disabled");
02043    ast_cli(a->fd, FORMAT, "External IP:", S_OR(externip, "Disabled"));
02044    ast_cli(a->fd, FORMAT, "Context:", global_context);
02045    ast_cli(a->fd, FORMAT, "Codecs:", ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, global_capability));
02046    ast_cli(a->fd, FORMAT, "Parking Lot:", global_parkinglot);
02047    ast_cli(a->fd, FORMAT, "Allow Guest:", AST_CLI_YESNO(global_allowguest));
02048    ast_cli(a->fd, "\n----\n");
02049 
02050    return CLI_SUCCESS;
02051 #undef FORMAT
02052 }
02053 
02054 /*! \brief CLI command "gtalk reload"
02055  *  \todo XXX TODO make this work. */
02056 #if 0
02057 static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02058 {
02059    switch (cmd) {
02060    case CLI_INIT:
02061       e->command = "gtalk reload";
02062       e->usage =
02063          "Usage: gtalk reload\n"
02064          "       Reload gtalk channel driver.\n";
02065       return NULL;
02066    case CLI_GENERATE:
02067       return NULL;
02068    }
02069 
02070    ast_verbose("IT DOES WORK!\n");
02071    return CLI_SUCCESS;
02072 }
02073 #endif
02074 
02075 static int gtalk_parser(void *data, ikspak *pak)
02076 {
02077    struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
02078    int res;
02079    iks *tmp;
02080 
02081    if (!strcasecmp(iks_name(pak->query), "jin:jingle") && (tmp = iks_next(pak->query)) && !strcasecmp(iks_name(tmp), "ses:session")) {
02082       ast_debug(1, "New method detected. Skipping jingle offer and using old gtalk method.\n");
02083       pak->query = tmp;
02084    }
02085 
02086    if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
02087       ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
02088    }
02089 
02090    if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
02091       ast_log(LOG_NOTICE, "No attribute \"type\" found.  Ignoring message.\n");
02092    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
02093       /* New call */
02094       gtalk_newcall(client, pak);
02095    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
02096       ast_debug(3, "About to add candidate!\n");
02097       res = gtalk_add_candidate(client, pak);
02098       if (!res) {
02099          ast_log(LOG_WARNING, "Could not add any candidate\n");
02100       } else {
02101          ast_debug(3, "Candidate Added!\n");
02102       }
02103    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "accept")) {
02104       gtalk_is_answered(client, pak);
02105    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
02106       gtalk_is_accepted(client, pak);
02107    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
02108       gtalk_handle_dtmf(client, pak);
02109    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
02110       gtalk_hangup_farend(client, pak);
02111    } else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
02112       gtalk_hangup_farend(client, pak);
02113    }
02114    ASTOBJ_UNREF(client, gtalk_member_destroy);
02115    return IKS_FILTER_EAT;
02116 }
02117 
02118 static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
02119                         struct ast_codec_pref prefs, char *context,
02120                         struct gtalk *member)
02121 {
02122    struct aji_client *client;
02123 
02124    if (!member)
02125       ast_log(LOG_WARNING, "Out of memory.\n");
02126 
02127    ast_copy_string(member->name, label, sizeof(member->name));
02128    ast_copy_string(member->user, label, sizeof(member->user));
02129    ast_copy_string(member->context, context, sizeof(member->context));
02130    member->allowguest = allowguest;
02131    member->prefs = prefs;
02132    while (var) {
02133       if (!strcasecmp(var->name, "username"))
02134          ast_copy_string(member->user, var->value, sizeof(member->user));
02135       else if (!strcasecmp(var->name, "disallow"))
02136          ast_parse_allow_disallow(&member->prefs, member->cap, var->value, 0);
02137       else if (!strcasecmp(var->name, "allow"))
02138          ast_parse_allow_disallow(&member->prefs, member->cap, var->value, 1);
02139       else if (!strcasecmp(var->name, "context"))
02140          ast_copy_string(member->context, var->value, sizeof(member->context));
02141       else if (!strcasecmp(var->name, "parkinglot"))
02142          ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
02143       else if (!strcasecmp(var->name, "connection")) {
02144          if ((client = ast_aji_get_client(var->value))) {
02145             member->connection = client;
02146             iks_filter_add_rule(client->f, gtalk_parser, member,
02147                       IKS_RULE_TYPE, IKS_PAK_IQ,
02148                       IKS_RULE_FROM_PARTIAL, member->user,
02149                       IKS_RULE_NS, GOOGLE_NS,
02150                       IKS_RULE_DONE);
02151          } else {
02152             ast_log(LOG_ERROR, "connection referenced not found!\n");
02153             return 0;
02154          }
02155       }
02156       var = var->next;
02157    }
02158    if (member->connection && member->user)
02159       member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
02160    else {
02161       ast_log(LOG_ERROR, "No Connection or Username!\n");
02162    }
02163    return 1;
02164 }
02165 
02166 static int gtalk_load_config(void)
02167 {
02168    char *cat = NULL;
02169    struct ast_config *cfg = NULL;
02170    struct ast_variable *var;
02171    struct gtalk *member;
02172    struct ast_codec_pref prefs;
02173    struct aji_client_container *clients;
02174    struct gtalk_candidate *global_candidates = NULL;
02175    struct hostent *hp;
02176    struct ast_hostent ahp;
02177    struct ast_flags config_flags = { 0 };
02178 
02179    cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
02180    if (!cfg) {
02181       return 0;
02182    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02183       ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", GOOGLE_CONFIG);
02184       return 0;
02185    }
02186 
02187    /* Copy the default jb config over global_jbconf */
02188    memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
02189 
02190    /* set defaults */
02191    memset(&prefs, 0, sizeof(prefs));
02192    memset(&stunaddr, 0, sizeof(stunaddr));
02193    global_stunaddr = 0;
02194    global_allowguest = DEFAULT_ALLOWGUEST;
02195    ast_copy_string(global_context, DEFAULT_CONTEXT, sizeof(global_context));
02196    ast_copy_string(global_parkinglot, DEFAULT_PARKINGLOT, sizeof(global_parkinglot));
02197 
02198    cat = ast_category_browse(cfg, NULL);
02199    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02200       /* handle jb conf */
02201       if (!ast_jb_read_conf(&global_jbconf, var->name, var->value)) {
02202          continue;
02203       }
02204 
02205       if (!strcasecmp(var->name, "allowguest")) {
02206          global_allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
02207       } else if (!strcasecmp(var->name, "disallow")) {
02208          ast_parse_allow_disallow(&prefs, global_capability, var->value, 0);
02209       } else if (!strcasecmp(var->name, "allow")) {
02210          ast_parse_allow_disallow(&prefs, global_capability, var->value, 1);
02211       } else if (!strcasecmp(var->name, "context")) {
02212          ast_copy_string(global_context, var->value, sizeof(global_context));
02213       } else if (!strcasecmp(var->name, "externip")) {
02214          ast_copy_string(externip, var->value, sizeof(externip));
02215       } else if (!strcasecmp(var->name, "parkinglot")) {
02216          ast_copy_string(global_parkinglot, var->value, sizeof(global_parkinglot));
02217       } else if (!strcasecmp(var->name, "bindaddr")) {
02218          if (!(hp = ast_gethostbyname(var->value, &ahp))) {
02219             ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
02220          } else {
02221             memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
02222          }
02223       } else if (!strcasecmp(var->name, "stunaddr")) {
02224          stunaddr.sin_port = htons(STANDARD_STUN_PORT);
02225          global_stunaddr = 1;
02226          if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
02227             ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
02228          }
02229       }
02230    }
02231    while (cat) {
02232       if (strcasecmp(cat, "general")) {
02233          var = ast_variable_browse(cfg, cat);
02234          member = ast_calloc(1, sizeof(*member));
02235          ASTOBJ_INIT(member);
02236          ASTOBJ_WRLOCK(member);
02237          member->cap = ast_format_cap_alloc_nolock();
02238          if (!strcasecmp(cat, "guest")) {
02239             ast_copy_string(member->name, "guest", sizeof(member->name));
02240             ast_copy_string(member->user, "guest", sizeof(member->user));
02241             ast_copy_string(member->context, global_context, sizeof(member->context));
02242             ast_copy_string(member->parkinglot, global_parkinglot, sizeof(member->parkinglot));
02243             member->allowguest = global_allowguest;
02244             member->prefs = prefs;
02245             while (var) {
02246                if (!strcasecmp(var->name, "disallow")) {
02247                   ast_parse_allow_disallow(&member->prefs, member->cap,
02248                                      var->value, 0);
02249                } else if (!strcasecmp(var->name, "allow")) {
02250                   ast_parse_allow_disallow(&member->prefs, member->cap,
02251                                      var->value, 1);
02252                } else if (!strcasecmp(var->name, "context")) {
02253                   ast_copy_string(member->context, var->value,
02254                               sizeof(member->context));
02255                } else if (!strcasecmp(var->name, "parkinglot")) {
02256                   ast_copy_string(member->parkinglot, var->value,
02257                               sizeof(member->parkinglot));
02258                }
02259                var = var->next;
02260             }
02261             ASTOBJ_UNLOCK(member);
02262             clients = ast_aji_get_clients();
02263             if (clients) {
02264                ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
02265                   ASTOBJ_WRLOCK(iterator);
02266                   ASTOBJ_WRLOCK(member);
02267                   if (member->connection) {
02268                      ASTOBJ_UNREF(member->connection, ast_aji_client_destroy);
02269                   }
02270                   member->connection = NULL;
02271                   iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
02272                   iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_JINGLE_NS, IKS_RULE_DONE);
02273                   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);
02274                   ASTOBJ_UNLOCK(member);
02275                   ASTOBJ_UNLOCK(iterator);
02276                });
02277                ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
02278                ASTOBJ_UNREF(member, gtalk_member_destroy);
02279             } else {
02280                ASTOBJ_UNLOCK(member);
02281                ASTOBJ_UNREF(member, gtalk_member_destroy);
02282             }
02283          } else {
02284             ASTOBJ_UNLOCK(member);
02285             if (gtalk_create_member(cat, var, global_allowguest, prefs, global_context, member)) {
02286                ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
02287             }
02288             ASTOBJ_UNREF(member, gtalk_member_destroy);
02289          }
02290       }
02291       cat = ast_category_browse(cfg, cat);
02292    }
02293 
02294    ast_config_destroy(cfg);
02295    gtalk_update_externip();
02296    gtalk_free_candidates(global_candidates);
02297    return 1;
02298 }
02299 
02300 /*! \brief Load module into PBX, register channel */
02301 static int load_module(void)
02302 {
02303    struct ast_sockaddr bindaddr_tmp;
02304    struct ast_sockaddr ourip_tmp;
02305    char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
02306    struct ast_format tmpfmt;
02307 
02308    if (!(gtalk_tech.capabilities = ast_format_cap_alloc())) {
02309       return AST_MODULE_LOAD_DECLINE;
02310    }
02311    if (!(global_capability = ast_format_cap_alloc())) {
02312       return AST_MODULE_LOAD_DECLINE;
02313    }
02314 
02315    ast_format_cap_add_all_by_type(gtalk_tech.capabilities, AST_FORMAT_TYPE_AUDIO);
02316    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0));
02317    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0));
02318    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0));
02319    ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_H263, 0));
02320 
02321    free(jabber_loaded);
02322    if (!jabber_loaded) {
02323       /* If embedded, check for a different module name */
02324       jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0);
02325       free(jabber_loaded);
02326       if (!jabber_loaded) {
02327          ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n");
02328          return AST_MODULE_LOAD_DECLINE;
02329       }
02330    }
02331 
02332    ASTOBJ_CONTAINER_INIT(&gtalk_list);
02333    if (!gtalk_load_config()) {
02334       ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
02335       return 0;
02336    }
02337 
02338    sched = ast_sched_context_create();
02339    if (!sched) {
02340       ast_log(LOG_WARNING, "Unable to create schedule context\n");
02341    }
02342 
02343    io = io_context_create();
02344    if (!io) {
02345       ast_log(LOG_WARNING, "Unable to create I/O context\n");
02346    }
02347 
02348    ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
02349    if (gtalk_get_local_ip(&ourip_tmp)) {
02350       ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
02351       return 0;
02352    }
02353    __ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));
02354 
02355    ast_rtp_glue_register(&gtalk_rtp_glue);
02356    ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02357 
02358    /* Make sure we can register our channel type */
02359    if (ast_channel_register(&gtalk_tech)) {
02360       ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
02361       return -1;
02362    }
02363    return 0;
02364 }
02365 
02366 /*! \brief Reload module
02367  *  \todo XXX TODO make this work. */
02368 #if 0
02369 static int reload(void)
02370 {
02371    return 0;
02372 }
02373 #endif
02374 /*! \brief Unload the gtalk channel from Asterisk */
02375 static int unload_module(void)
02376 {
02377    struct gtalk_pvt *privates = NULL;
02378    ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02379    /* First, take us out of the channel loop */
02380    ast_channel_unregister(&gtalk_tech);
02381    ast_rtp_glue_unregister(&gtalk_rtp_glue);
02382 
02383    if (!ast_mutex_lock(&gtalklock)) {
02384       /* Hangup all interfaces if they have an owner */
02385       ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
02386          ASTOBJ_WRLOCK(iterator);
02387          privates = iterator->p;
02388          while(privates) {
02389             if (privates->owner)
02390                ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
02391             privates = privates->next;
02392          }
02393          iterator->p = NULL;
02394          ASTOBJ_UNLOCK(iterator);
02395       });
02396       ast_mutex_unlock(&gtalklock);
02397    } else {
02398       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
02399       return -1;
02400    }
02401    ASTOBJ_CONTAINER_DESTROYALL(&gtalk_list, gtalk_member_destroy);
02402    ASTOBJ_CONTAINER_DESTROY(&gtalk_list);
02403    global_capability = ast_format_cap_destroy(global_capability);
02404    gtalk_tech.capabilities = ast_format_cap_destroy(gtalk_tech.capabilities);
02405    return 0;
02406 }
02407 
02408 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gtalk Channel Driver",
02409       .load = load_module,
02410       .unload = unload_module,
02411       /* .reload = reload, */
02412       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02413       );