Sun Oct 16 2011 08:42:12

Asterisk developer's documentation


chan_gtalk.c File Reference

Gtalk Channel Driver, until google/libjingle works with jingle spec. More...

#include "asterisk.h"
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include <iksemel.h>
#include <pthread.h>
#include <ctype.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/stun.h"
#include "asterisk/acl.h"
#include "asterisk/callerid.h"
#include "asterisk/file.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/manager.h"
#include "asterisk/stringfields.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astobj.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/jabber.h"
#include "asterisk/jingle.h"
Include dependency graph for chan_gtalk.c:

Go to the source code of this file.

Data Structures

struct  gtalk
struct  gtalk_candidate
struct  gtalk_container
struct  gtalk_pvt

Defines

#define FORMAT   "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
#define GOOGLE_CONFIG   "gtalk.conf"

Enumerations

enum  gtalk_connect_type { AJI_CONNECT_STUN = 1, AJI_CONNECT_LOCAL = 2, AJI_CONNECT_RELAY = 3 }
enum  gtalk_protocol { AJI_PROTOCOL_UDP = 1, AJI_PROTOCOL_SSLTCP = 2 }

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int add_codec_to_answer (const struct gtalk_pvt *p, int codec, iks *dcodecs)
static struct gtalkfind_gtalk (char *name, char *connection)
static int gtalk_action (struct gtalk *client, struct gtalk_pvt *p, const char *action)
static int gtalk_add_candidate (struct gtalk *client, ikspak *pak)
static struct gtalk_pvtgtalk_alloc (struct gtalk *client, const char *us, const char *them, const char *sid)
static int gtalk_answer (struct ast_channel *ast)
static int gtalk_call (struct ast_channel *ast, char *dest, int timeout)
 Initiate new call, part of PBX interface dest is the dial string.
static int gtalk_create_candidates (struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to)
static int gtalk_create_member (char *label, struct ast_variable *var, int allowguest, struct ast_codec_pref prefs, char *context, struct gtalk *member)
static int gtalk_digit_begin (struct ast_channel *ast, char digit)
static int gtalk_digit_end (struct ast_channel *ast, char digit, unsigned int duration)
static int gtalk_fixup (struct ast_channel *oldchan, struct ast_channel *newchan)
static void gtalk_free_candidates (struct gtalk_candidate *candidate)
static void gtalk_free_pvt (struct gtalk *client, struct gtalk_pvt *p)
static format_t gtalk_get_codec (struct ast_channel *chan)
static int gtalk_get_local_ip (struct ast_sockaddr *ourip)
static enum ast_rtp_glue_result gtalk_get_rtp_peer (struct ast_channel *chan, struct ast_rtp_instance **instance)
static int gtalk_handle_dtmf (struct gtalk *client, ikspak *pak)
static int gtalk_hangup (struct ast_channel *ast)
 Hangup a call through the gtalk proxy channel.
static int gtalk_hangup_farend (struct gtalk *client, ikspak *pak)
static int gtalk_indicate (struct ast_channel *ast, int condition, const void *data, size_t datalen)
static int gtalk_invite (struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator)
static int gtalk_is_accepted (struct gtalk *client, ikspak *pak)
static int gtalk_is_answered (struct gtalk *client, ikspak *pak)
static int gtalk_load_config (void)
static void gtalk_member_destroy (struct gtalk *obj)
static struct ast_channelgtalk_new (struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid)
 Start new gtalk channel.
static int gtalk_newcall (struct gtalk *client, ikspak *pak)
static int gtalk_parser (void *data, ikspak *pak)
 CLI command "gtalk reload".
static struct ast_framegtalk_read (struct ast_channel *ast)
static struct ast_channelgtalk_request (const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
 Part of PBX interface.
static int gtalk_response (struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2)
static int gtalk_ringing_ack (void *data, ikspak *pak)
static struct ast_framegtalk_rtp_read (struct ast_channel *ast, struct gtalk_pvt *p)
static int gtalk_sendhtml (struct ast_channel *ast, int subclass, const char *data, int datalen)
static int gtalk_sendtext (struct ast_channel *ast, const char *text)
static int gtalk_set_rtp_peer (struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, format_t codecs, int nat_active)
static char * gtalk_show_channels (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command "gtalk show channels".
static int gtalk_update_externip (void)
static int gtalk_update_stun (struct gtalk *client, struct gtalk_pvt *p)
static int gtalk_write (struct ast_channel *ast, struct ast_frame *frame)
 Send frame to media channel (rtp)
static int load_module (void)
 Load module into PBX, register channel.
static int unload_module (void)
 Reload module.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Gtalk Channel Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "7a1b8b48c852d7a7061c7e499b9bd0d2" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, }
static struct in_addr __ourip
static struct ast_module_infoast_module_info = &__mod_info
static struct sockaddr_in bindaddr = { 0, }
static struct ast_jb_conf default_jbconf
static const char desc [] = "Gtalk Channel"
static char externip [16]
static format_t global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263
static struct ast_jb_conf global_jbconf
static struct ast_cli_entry gtalk_cli []
static struct gtalk_container gtalk_list
static struct ast_rtp_glue gtalk_rtp_glue
static struct ast_channel_tech gtalk_tech
 PBX interface structure for channel registration.
static ast_mutex_t gtalklock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static struct io_contextio
static struct sched_contextsched
static struct sockaddr_in stunaddr

Detailed Description

Gtalk Channel Driver, until google/libjingle works with jingle spec.

Author:
Matt O'Gorman <mogorman@digium.com>
Philippe Sultan <philippe.sultan@gmail.com>

********** General TODO:s

Todo:

Support config reloading.

Fix native bridging.

Definition in file chan_gtalk.c.


Define Documentation

#define FORMAT   "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
#define GOOGLE_CONFIG   "gtalk.conf"

Definition at line 80 of file chan_gtalk.c.

Referenced by gtalk_load_config(), and load_module().


Enumeration Type Documentation

Enumerator:
AJI_CONNECT_STUN 
AJI_CONNECT_LOCAL 
AJI_CONNECT_RELAY 

Definition at line 98 of file chan_gtalk.c.

Enumerator:
AJI_PROTOCOL_UDP 
AJI_PROTOCOL_SSLTCP 

Definition at line 93 of file chan_gtalk.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 2284 of file chan_gtalk.c.

static void __unreg_module ( void  ) [static]

Definition at line 2284 of file chan_gtalk.c.

static int add_codec_to_answer ( const struct gtalk_pvt p,
int  codec,
iks *  dcodecs 
) [static]

Definition at line 278 of file chan_gtalk.c.

References format, ast_getformatname(), ast_log(), and LOG_WARNING.

Referenced by gtalk_invite().

{
   int res = 0;
   char *format = ast_getformatname(codec);

   if (!strcasecmp("ulaw", format)) {
      iks *payload_eg711u, *payload_pcmu;
      payload_pcmu = iks_new("payload-type");
      payload_eg711u = iks_new("payload-type");

      if(!payload_eg711u || !payload_pcmu) {
         iks_delete(payload_pcmu);
         iks_delete(payload_eg711u);
         ast_log(LOG_WARNING,"Failed to allocate iks node");
         return -1;
      }
      iks_insert_attrib(payload_pcmu, "id", "0");
      iks_insert_attrib(payload_pcmu, "name", "PCMU");
      iks_insert_attrib(payload_pcmu, "clockrate","8000");
      iks_insert_attrib(payload_pcmu, "bitrate","64000");
      iks_insert_attrib(payload_eg711u, "id", "100");
      iks_insert_attrib(payload_eg711u, "name", "EG711U");
      iks_insert_attrib(payload_eg711u, "clockrate","8000");
      iks_insert_attrib(payload_eg711u, "bitrate","64000");
      iks_insert_node(dcodecs, payload_pcmu);
      iks_insert_node(dcodecs, payload_eg711u);
      res ++;
   }
   if (!strcasecmp("alaw", format)) {
      iks *payload_eg711a, *payload_pcma;
      payload_pcma = iks_new("payload-type");
      payload_eg711a = iks_new("payload-type");
      if(!payload_eg711a || !payload_pcma) {
         iks_delete(payload_eg711a);
         iks_delete(payload_pcma);
         ast_log(LOG_WARNING,"Failed to allocate iks node");
         return -1;
      }
      iks_insert_attrib(payload_pcma, "id", "8");
      iks_insert_attrib(payload_pcma, "name", "PCMA");
      iks_insert_attrib(payload_pcma, "clockrate","8000");
      iks_insert_attrib(payload_pcma, "bitrate","64000");
      payload_eg711a = iks_new("payload-type");
      iks_insert_attrib(payload_eg711a, "id", "101");
      iks_insert_attrib(payload_eg711a, "name", "EG711A");
      iks_insert_attrib(payload_eg711a, "clockrate","8000");
      iks_insert_attrib(payload_eg711a, "bitrate","64000");
      iks_insert_node(dcodecs, payload_pcma);
      iks_insert_node(dcodecs, payload_eg711a);
      res ++;
   }
   if (!strcasecmp("ilbc", format)) {
      iks *payload_ilbc = iks_new("payload-type");
      if(!payload_ilbc) {
         ast_log(LOG_WARNING,"Failed to allocate iks node");
         return -1;
      }
      iks_insert_attrib(payload_ilbc, "id", "97");
      iks_insert_attrib(payload_ilbc, "name", "iLBC");
      iks_insert_attrib(payload_ilbc, "clockrate","8000");
      iks_insert_attrib(payload_ilbc, "bitrate","13300");
      iks_insert_node(dcodecs, payload_ilbc);
      res ++;
   }
   if (!strcasecmp("g723", format)) {
      iks *payload_g723 = iks_new("payload-type");
      if(!payload_g723) {
         ast_log(LOG_WARNING,"Failed to allocate iks node");
         return -1;
      }
      iks_insert_attrib(payload_g723, "id", "4");
      iks_insert_attrib(payload_g723, "name", "G723");
      iks_insert_attrib(payload_g723, "clockrate","8000");
      iks_insert_attrib(payload_g723, "bitrate","6300");
      iks_insert_node(dcodecs, payload_g723);
      res ++;
   }
   if (!strcasecmp("speex", format)) {
      iks *payload_speex = iks_new("payload-type");
      if(!payload_speex) {
         ast_log(LOG_WARNING,"Failed to allocate iks node");
         return -1;
      }
      iks_insert_attrib(payload_speex, "id", "110");
      iks_insert_attrib(payload_speex, "name", "speex");
      iks_insert_attrib(payload_speex, "clockrate","8000");
      iks_insert_attrib(payload_speex, "bitrate","11000");
      iks_insert_node(dcodecs, payload_speex);
      res++;
   }
   if (!strcasecmp("gsm", format)) {
      iks *payload_gsm = iks_new("payload-type");
      if(!payload_gsm) {
         ast_log(LOG_WARNING,"Failed to allocate iks node");
         return -1;
      }
      iks_insert_attrib(payload_gsm, "id", "103");
      iks_insert_attrib(payload_gsm, "name", "gsm");
      iks_insert_node(dcodecs, payload_gsm);
      res++;
   }

   return res;
}
static struct gtalk* find_gtalk ( char *  name,
char *  connection 
) [static, read]

Definition at line 246 of file chan_gtalk.c.

References ast_strdupa, strsep(), ast_verbose(), ASTOBJ_CONTAINER_FIND, gtalk_list, ASTOBJ_CONTAINER_FIND_FULL, ASTOBJ_CONTAINER_TRAVERSE, ASTOBJ_RDLOCK, and ASTOBJ_UNLOCK.

Referenced by gtalk_request().

{
   struct gtalk *gtalk = NULL;
   char *domain = NULL , *s = NULL;

   if (strchr(connection, '@')) {
      s = ast_strdupa(connection);
      domain = strsep(&s, "@");
      ast_verbose("OOOOH domain = %s\n", domain);
   }
   gtalk = ASTOBJ_CONTAINER_FIND(&gtalk_list, name);
   if (!gtalk && strchr(name, '@'))
      gtalk = ASTOBJ_CONTAINER_FIND_FULL(&gtalk_list, name, user,,, strcasecmp);

   if (!gtalk) {
      /* guest call */
      ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
         ASTOBJ_RDLOCK(iterator);
         if (!strcasecmp(iterator->name, "guest")) {
            gtalk = iterator;
         }
         ASTOBJ_UNLOCK(iterator);

         if (gtalk)
            break;
      });

   }
   return gtalk;
}
static int gtalk_action ( struct gtalk client,
struct gtalk_pvt p,
const char *  action 
) [static]

Definition at line 1164 of file chan_gtalk.c.

References gtalk_pvt::us, gtalk_pvt::them, gtalk::connection, aji_client::mid, ast_aji_increment_mid(), gtalk_pvt::sid, gtalk_pvt::initiator, ast_strdupa, GOOGLE_NS, and ast_aji_send().

Referenced by gtalk_newcall(), and gtalk_hangup().

{
   iks *request, *session = NULL;
   int res = -1;
   char *lowerthem = NULL;

   request = iks_new("iq");
   if (request) {
      iks_insert_attrib(request, "type", "set");
      iks_insert_attrib(request, "from", p->us);
      iks_insert_attrib(request, "to", p->them);
      iks_insert_attrib(request, "id", client->connection->mid);
      ast_aji_increment_mid(client->connection->mid);
      session = iks_new("session");
      if (session) {
         iks_insert_attrib(session, "type", action);
         iks_insert_attrib(session, "id", p->sid);
         /* put the initiator attribute to lower case if we receive the call
          * otherwise GoogleTalk won't establish the session */
         if (!p->initiator) {
                 char c;
            char *t = lowerthem = ast_strdupa(p->them);
            while (((c = *t) != '/') && (*t++ = tolower(c)));
         }
         iks_insert_attrib(session, "initiator", p->initiator ? p->us : lowerthem);
         iks_insert_attrib(session, "xmlns", GOOGLE_NS);
         iks_insert_node(request, session);
         ast_aji_send(client->connection, request);
         res = 0;
      }
   }

   iks_delete(session);
   iks_delete(request);

   return res;
}
static int gtalk_add_candidate ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 1466 of file chan_gtalk.c.

References gtalk::connection, gtalk_candidate::receipt, aji_client::jid, gtalk::p, gtalk_pvt::next, S_OR, ast_calloc, ast_copy_string(), gtalk_candidate::name, gtalk_candidate::ip, gtalk_candidate::port, gtalk_candidate::username, gtalk_candidate::password, gtalk_candidate::preference, gtalk_candidate::protocol, AJI_PROTOCOL_UDP, AJI_PROTOCOL_SSLTCP, gtalk_candidate::type, AJI_CONNECT_STUN, AJI_CONNECT_LOCAL, AJI_CONNECT_RELAY, gtalk_candidate::network, gtalk_candidate::generation, gtalk_candidate::next, gtalk_pvt::theircandidates, gtalk_pvt::laststun, gtalk_update_stun(), gtalk_pvt::parent, and ast_aji_send().

Referenced by gtalk_parser().

{
   struct gtalk_pvt *p = NULL, *tmp = NULL;
   struct aji_client *c = client->connection;
   struct gtalk_candidate *newcandidate = NULL;
   iks *traversenodes = NULL, *receipt = NULL;
   char *from;

   from = iks_find_attrib(pak->x,"to");
   if (!from) {
      from = c->jid->full;
   }

   for (tmp = client->p; tmp; tmp = tmp->next) {
      if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
         (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
         p = tmp;
         break;
      }
   }

   if (!p) {
      return -1;
   }
   traversenodes = pak->query;
   while(traversenodes) {
      if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
         traversenodes = iks_first_tag(traversenodes);
         continue;
      }
      if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
         traversenodes = iks_child(traversenodes);
         continue;
      }
      if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
         newcandidate = ast_calloc(1, sizeof(*newcandidate));
         if (!newcandidate)
            return 0;
         ast_copy_string(newcandidate->name,
            S_OR(iks_find_attrib(traversenodes, "name"), ""),
            sizeof(newcandidate->name));
         ast_copy_string(newcandidate->ip,
            S_OR(iks_find_attrib(traversenodes, "address"), ""),
            sizeof(newcandidate->ip));
         newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
         ast_copy_string(newcandidate->username,
            S_OR(iks_find_attrib(traversenodes, "username"), ""),
            sizeof(newcandidate->username));
         ast_copy_string(newcandidate->password,
            S_OR(iks_find_attrib(traversenodes, "password"), ""),
            sizeof(newcandidate->password));
         newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
            newcandidate->protocol = AJI_PROTOCOL_UDP;
         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
            newcandidate->protocol = AJI_PROTOCOL_SSLTCP;

         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
            newcandidate->type = AJI_CONNECT_STUN;
         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
            newcandidate->type = AJI_CONNECT_LOCAL;
         if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
            newcandidate->type = AJI_CONNECT_RELAY;
         ast_copy_string(newcandidate->network,
            S_OR(iks_find_attrib(traversenodes, "network"), ""),
            sizeof(newcandidate->network));
         newcandidate->generation = atoi(S_OR(iks_find_attrib(traversenodes, "generation"), "0"));
         newcandidate->next = NULL;

         newcandidate->next = p->theircandidates;
         p->theircandidates = newcandidate;
         p->laststun = 0;
         gtalk_update_stun(p->parent, p);
         newcandidate = NULL;
      }
      traversenodes = iks_next_tag(traversenodes);
   }

   receipt = iks_new("iq");
   iks_insert_attrib(receipt, "type", "result");
   iks_insert_attrib(receipt, "from", from);
   iks_insert_attrib(receipt, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
   iks_insert_attrib(receipt, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
   ast_aji_send(c, receipt);

   iks_delete(receipt);

   return 1;
}
static struct gtalk_pvt * gtalk_alloc ( struct gtalk client,
const char *  us,
const char *  them,
const char *  sid 
) [static, read]

Definition at line 976 of file chan_gtalk.c.

References exten, ast_debug, gtalk::name, ASTOBJ_CONTAINER_FIND, gtalk::connection, aji_client::buddies, aji_buddy::resources, gtalk::buddy, aji_resource::cap, aji_version::jingle, aji_resource::next, aji_resource::resource, ast_log(), LOG_ERROR, ast_calloc, gtalk_pvt::prefs, gtalk::prefs, ast_copy_string(), gtalk_pvt::sid, gtalk_pvt::them, gtalk_pvt::us, ast_random(), gtalk_pvt::initiator, ast_sockaddr_from_sin, gtalk_pvt::rtp, ast_rtp_instance_new(), ast_free, ast_rtp_instance_set_prop(), AST_RTP_PROPERTY_RTCP, AST_RTP_PROPERTY_STUN, AST_RTP_PROPERTY_DTMF, ast_rtp_instance_dtmf_mode_set(), AST_RTP_DTMF_MODE_RFC2833, ast_rtp_codecs_payloads_clear(), ast_rtp_instance_get_codecs(), gtalk::capability, gtalk_pvt::capability, global_capability, gtalk_pvt::parent, LOG_WARNING, gtalk_pvt::cid_name, ast_strdupa, strsep(), gtalk_pvt::exten, ast_mutex_init, gtalk_pvt::lock, ast_mutex_lock, gtalklock, gtalk_pvt::next, gtalk::p, and ast_mutex_unlock.

Referenced by gtalk_newcall(), and gtalk_request().

{
   struct gtalk_pvt *tmp = NULL;
   struct aji_resource *resources = NULL;
   struct aji_buddy *buddy;
   char idroster[200];
   char *data, *exten = NULL;
   struct ast_sockaddr bindaddr_tmp;

   ast_debug(1, "The client is %s for alloc\n", client->name);
   if (!sid && !strchr(them, '/')) {   /* I started call! */
      if (!strcasecmp(client->name, "guest")) {
         buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them);
         if (buddy) {
            resources = buddy->resources;
         }
      } else if (client->buddy) {
         resources = client->buddy->resources;
      }

      while (resources) {
         if (resources->cap->jingle) {
            break;
         }
         resources = resources->next;
      }
      if (resources) {
         snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource);
      } else if ((*them == '+') || (strstr(them, "@voice.google.com"))) {
         snprintf(idroster, sizeof(idroster), "%s", them);
      } else {
         ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n");
         return NULL;
      }
   }
   if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
      return NULL;
   }

   memcpy(&tmp->prefs, &client->prefs, sizeof(struct ast_codec_pref));

   if (sid) {
      ast_copy_string(tmp->sid, sid, sizeof(tmp->sid));
      ast_copy_string(tmp->them, them, sizeof(tmp->them));
      ast_copy_string(tmp->us, us, sizeof(tmp->us));
   } else {
      snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random());
      ast_copy_string(tmp->them, idroster, sizeof(tmp->them));
      ast_copy_string(tmp->us, us, sizeof(tmp->us));
      tmp->initiator = 1;
   }
   /* clear codecs */
   bindaddr.sin_family = AF_INET;
   ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
   if (!(tmp->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL))) {
     ast_log(LOG_ERROR, "Failed to create a new RTP instance (possibly an invalid bindaddr?)\n");
     ast_free(tmp);
     return NULL;
   }
   ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1);
   ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_STUN, 1);
   ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_DTMF, 1);
   ast_rtp_instance_dtmf_mode_set(tmp->rtp, AST_RTP_DTMF_MODE_RFC2833);
   ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp);

   /* add user configured codec capabilites */
   if (client->capability) {
      tmp->capability = client->capability;
   } else if (global_capability) {
      tmp->capability = global_capability;
   }

   tmp->parent = client;
   if (!tmp->rtp) {
      ast_log(LOG_WARNING, "Out of RTP sessions?\n");
      ast_free(tmp);
      return NULL;
   }

   /* Set CALLERID(name) to the full JID of the remote peer */
   ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name));

   if(strchr(tmp->us, '/')) {
      data = ast_strdupa(tmp->us);
      exten = strsep(&data, "/");
   } else {
      exten = tmp->us;
   }
   ast_copy_string(tmp->exten,  exten, sizeof(tmp->exten));
   ast_mutex_init(&tmp->lock);
   ast_mutex_lock(&gtalklock);
   tmp->next = client->p;
   client->p = tmp;
   ast_mutex_unlock(&gtalklock);
   return tmp;
}
static int gtalk_answer ( struct ast_channel ast) [static]

Definition at line 507 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_debug, ast_mutex_lock, gtalk_pvt::lock, gtalk_invite(), gtalk_pvt::them, gtalk_pvt::us, gtalk_pvt::sid, manager_event, EVENT_FLAG_SYSTEM, ast_channel::name, and ast_mutex_unlock.

{
   struct gtalk_pvt *p = ast->tech_pvt;
   int res = 0;

   ast_debug(1, "Answer!\n");
   ast_mutex_lock(&p->lock);
   gtalk_invite(p, p->them, p->us,p->sid, 0);
   manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
      ast->name, "GTALK", p->sid);
   ast_mutex_unlock(&p->lock);
   return res;
}
static int gtalk_call ( struct ast_channel ast,
char *  dest,
int  timeout 
) [static]

Initiate new call, part of PBX interface dest is the dial string.

Definition at line 1796 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_channel::_state, AST_STATE_DOWN, AST_STATE_RESERVED, ast_log(), LOG_WARNING, ast_channel::name, ast_setstate(), AST_STATE_RING, gtalk_pvt::ringrule, ast_copy_string(), gtalk_pvt::ring, gtalk_pvt::parent, gtalk::connection, aji_client::mid, aji_client::f, gtalk_ringing_ack(), gtalk_invite(), gtalk_pvt::them, gtalk_pvt::us, and gtalk_pvt::sid.

{
   struct gtalk_pvt *p = ast->tech_pvt;

   if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
      ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name);
      return -1;
   }

   ast_setstate(ast, AST_STATE_RING);
   if (!p->ringrule) {
      ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
      p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
                     IKS_RULE_ID, p->ring, IKS_RULE_DONE);
   } else {
      ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
   }

   gtalk_invite(p, p->them, p->us, p->sid, 1);

   return 0;
}
static int gtalk_create_candidates ( struct gtalk client,
struct gtalk_pvt p,
char *  sid,
char *  from,
char *  to 
) [static]

Definition at line 839 of file chan_gtalk.c.

References gtalk::connection, pass, ast_log(), LOG_ERROR, ast_calloc, GOOGLE_TRANSPORT_NS, gtalk_pvt::next, gtalk_pvt::sid, LOG_NOTICE, ast_rtp_instance_get_local_address(), gtalk_pvt::rtp, ast_sockaddr_to_sin, gtalk_get_local_ip(), ast_sockaddr_stringify_addr(), LOG_WARNING, ast_copy_string(), gtalk_candidate::name, gtalk_candidate::port, gtalk_candidate::preference, ast_random(), gtalk_candidate::username, gtalk_candidate::password, gtalk_candidate::ip, gtalk_candidate::protocol, AJI_PROTOCOL_UDP, gtalk_candidate::type, AJI_CONNECT_LOCAL, gtalk_candidate::generation, gtalk_pvt::ourcandidates, gtalk_update_externip(), ast_strlen_zero(), AJI_CONNECT_STUN, gtalk_candidate::next, aji_client::mid, ast_aji_increment_mid(), gtalk_pvt::initiator, ast_strdupa, GOOGLE_NS, AJI_PROTOCOL_SSLTCP, AJI_CONNECT_RELAY, ast_aji_send(), gtalk_pvt::laststun, and ast_free.

Referenced by gtalk_ringing_ack(), and gtalk_newcall().

{
   struct gtalk_candidate *tmp;
   struct aji_client *c = client->connection;
   struct gtalk_candidate *ours1 = NULL, *ours2 = NULL;
   struct sockaddr_in sin = { 0, };
   struct ast_sockaddr sin_tmp;
   struct ast_sockaddr us;
   iks *iq, *gtalk, *candidate, *transport;
   char user[17], pass[17], preference[5], port[7];
   char *lowerfrom = NULL;

   iq = iks_new("iq");
   gtalk = iks_new("session");
   candidate = iks_new("candidate");
   transport = iks_new("transport");
   if (!iq || !gtalk || !candidate || !transport) {
      ast_log(LOG_ERROR, "Memory allocation error\n");
      goto safeout;
   }
   ours1 = ast_calloc(1, sizeof(*ours1));
   ours2 = ast_calloc(1, sizeof(*ours2));
   if (!ours1 || !ours2)
      goto safeout;

   iks_insert_attrib(transport, "xmlns",GOOGLE_TRANSPORT_NS);
   iks_insert_node(iq, gtalk);
   iks_insert_node(gtalk,candidate);
   iks_insert_node(gtalk,transport);

   for (; p; p = p->next) {
      if (!strcasecmp(p->sid, sid))
         break;
   }

   if (!p) {
      ast_log(LOG_NOTICE, "No matching gtalk session - SID %s!\n", sid);
      goto safeout;
   }

   ast_rtp_instance_get_local_address(p->rtp, &sin_tmp);
   ast_sockaddr_to_sin(&sin_tmp, &sin);

   gtalk_get_local_ip(&us);

   if (!strcmp(ast_sockaddr_stringify_addr(&us), "127.0.0.1")) {
      ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute.");
   }

   /* Setup our gtalk candidates */
   ast_copy_string(ours1->name, "rtp", sizeof(ours1->name));
   ours1->port = ntohs(sin.sin_port);
   ours1->preference = 1;
   snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
   snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
   ast_copy_string(ours1->username, user, sizeof(ours1->username));
   ast_copy_string(ours1->password, pass, sizeof(ours1->password));
   ast_copy_string(ours1->ip, ast_sockaddr_stringify_addr(&us),
         sizeof(ours1->ip));
   ours1->protocol = AJI_PROTOCOL_UDP;
   ours1->type = AJI_CONNECT_LOCAL;
   ours1->generation = 0;
   p->ourcandidates = ours1;

   /* XXX this is a blocking action.  We send a STUN request to the server
    * and wait for the response.  If blocking here is a problem the STUN requests/responses
    * for the externip may need to be done differently. */
   gtalk_update_externip();
   if (!ast_strlen_zero(externip)) {
      ast_copy_string(ours2->username, user, sizeof(ours2->username));
      ast_copy_string(ours2->password, pass, sizeof(ours2->password));
      ast_copy_string(ours2->ip, externip, sizeof(ours2->ip));
      ast_copy_string(ours2->name, "rtp", sizeof(ours1->name));
      ours2->port = ntohs(sin.sin_port);
      ours2->preference = 0.9;
      ours2->protocol = AJI_PROTOCOL_UDP;
      ours2->type = AJI_CONNECT_STUN;
      ours2->generation = 0;
      ours1->next = ours2;
      ours2 = NULL;
   }
   ours1 = NULL;

   for (tmp = p->ourcandidates; tmp; tmp = tmp->next) {
      snprintf(port, sizeof(port), "%d", tmp->port);
      snprintf(preference, sizeof(preference), "%.2f", tmp->preference);
      iks_insert_attrib(iq, "from", to);
      iks_insert_attrib(iq, "to", from);
      iks_insert_attrib(iq, "type", "set");
      iks_insert_attrib(iq, "id", c->mid);
      ast_aji_increment_mid(c->mid);
      iks_insert_attrib(gtalk, "type", "candidates");
      iks_insert_attrib(gtalk, "id", sid);
      /* put the initiator attribute to lower case if we receive the call
       * otherwise GoogleTalk won't establish the session */
      if (!p->initiator) {
              char c;
         char *t = lowerfrom = ast_strdupa(from);
         while (((c = *t) != '/') && (*t++ = tolower(c)));
      }
      iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : lowerfrom);
      iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
      iks_insert_attrib(candidate, "name", tmp->name);
      iks_insert_attrib(candidate, "address", tmp->ip);
      iks_insert_attrib(candidate, "port", port);
      iks_insert_attrib(candidate, "username", tmp->username);
      iks_insert_attrib(candidate, "password", tmp->password);
      iks_insert_attrib(candidate, "preference", preference);
      if (tmp->protocol == AJI_PROTOCOL_UDP)
         iks_insert_attrib(candidate, "protocol", "udp");
      if (tmp->protocol == AJI_PROTOCOL_SSLTCP)
         iks_insert_attrib(candidate, "protocol", "ssltcp");
      if (tmp->type == AJI_CONNECT_STUN)
         iks_insert_attrib(candidate, "type", "stun");
      if (tmp->type == AJI_CONNECT_LOCAL)
         iks_insert_attrib(candidate, "type", "local");
      if (tmp->type == AJI_CONNECT_RELAY)
         iks_insert_attrib(candidate, "type", "relay");
      iks_insert_attrib(candidate, "network", "0");
      iks_insert_attrib(candidate, "generation", "0");
      ast_aji_send(c, iq);
   }
   p->laststun = 0;

safeout:
   if (ours1)
      ast_free(ours1);
   if (ours2)
      ast_free(ours2);
   iks_delete(iq);
   iks_delete(gtalk);
   iks_delete(candidate);
   iks_delete(transport);

   return 1;
}
static int gtalk_create_member ( char *  label,
struct ast_variable var,
int  allowguest,
struct ast_codec_pref  prefs,
char *  context,
struct gtalk member 
) [static]

Definition at line 2015 of file chan_gtalk.c.

References ast_log(), LOG_WARNING, ast_copy_string(), gtalk::name, gtalk::user, gtalk::context, gtalk::allowguest, aji_client::allowguest, gtalk::prefs, prefs, ast_variable::name, ast_variable::value, ast_parse_allow_disallow(), gtalk::capability, gtalk::parkinglot, ast_aji_get_client(), gtalk::connection, aji_client::f, gtalk_parser(), GOOGLE_NS, LOG_ERROR, ast_variable::next, gtalk::buddy, ASTOBJ_CONTAINER_FIND, and aji_client::buddies.

Referenced by gtalk_load_config().

{
   struct aji_client *client;

   if (!member)
      ast_log(LOG_WARNING, "Out of memory.\n");

   ast_copy_string(member->name, label, sizeof(member->name));
   ast_copy_string(member->user, label, sizeof(member->user));
   ast_copy_string(member->context, context, sizeof(member->context));
   member->allowguest = allowguest;
   member->prefs = prefs;
   while (var) {
      if (!strcasecmp(var->name, "username"))
         ast_copy_string(member->user, var->value, sizeof(member->user));
      else if (!strcasecmp(var->name, "disallow"))
         ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0);
      else if (!strcasecmp(var->name, "allow"))
         ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
      else if (!strcasecmp(var->name, "context"))
         ast_copy_string(member->context, var->value, sizeof(member->context));
      else if (!strcasecmp(var->name, "parkinglot"))
         ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
      else if (!strcasecmp(var->name, "connection")) {
         if ((client = ast_aji_get_client(var->value))) {
            member->connection = client;
            iks_filter_add_rule(client->f, gtalk_parser, member,
                      IKS_RULE_TYPE, IKS_PAK_IQ,
                      IKS_RULE_FROM_PARTIAL, member->user,
                      IKS_RULE_NS, GOOGLE_NS,
                      IKS_RULE_DONE);
         } else {
            ast_log(LOG_ERROR, "connection referenced not found!\n");
            return 0;
         }
      }
      var = var->next;
   }
   if (member->connection && member->user)
      member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
   else {
      ast_log(LOG_ERROR, "No Connection or Username!\n");
   }
   return 1;
}
static int gtalk_digit_begin ( struct ast_channel ast,
char  digit 
) [static]

Definition at line 1696 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_mutex_lock, gtalk_pvt::lock, gtalk_pvt::rtp, ast_rtp_instance_dtmf_begin(), and ast_mutex_unlock.

{
   struct gtalk_pvt *p = chan->tech_pvt;
   int res = 0;

   ast_mutex_lock(&p->lock);
   if (p->rtp) {
      ast_rtp_instance_dtmf_begin(p->rtp, digit);
   } else {
      res = -1;
   }
   ast_mutex_unlock(&p->lock);

   return res;
}
static int gtalk_digit_end ( struct ast_channel ast,
char  digit,
unsigned int  duration 
) [static]

Definition at line 1712 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_mutex_lock, gtalk_pvt::lock, gtalk_pvt::rtp, ast_rtp_instance_dtmf_end_with_duration(), and ast_mutex_unlock.

{
   struct gtalk_pvt *p = chan->tech_pvt;
   int res = 0;

   ast_mutex_lock(&p->lock);
   if (p->rtp) {
      ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
   } else {
      res = -1;
   }
   ast_mutex_unlock(&p->lock);

   return res;
}
static int gtalk_fixup ( struct ast_channel oldchan,
struct ast_channel newchan 
) [static]

Definition at line 1643 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_mutex_lock, gtalk_pvt::lock, gtalk_pvt::owner, and ast_mutex_unlock.

{
   struct gtalk_pvt *p = newchan->tech_pvt;
   ast_mutex_lock(&p->lock);

   if ((p->owner != oldchan)) {
      ast_mutex_unlock(&p->lock);
      return -1;
   }
   if (p->owner == oldchan)
      p->owner = newchan;
   ast_mutex_unlock(&p->lock);
   return 0;
}
static void gtalk_free_candidates ( struct gtalk_candidate candidate) [static]

Definition at line 1202 of file chan_gtalk.c.

References last, gtalk_candidate::next, and ast_free.

Referenced by gtalk_free_pvt(), and gtalk_load_config().

{
   struct gtalk_candidate *last;
   while (candidate) {
      last = candidate;
      candidate = candidate->next;
      ast_free(last);
   }
}
static void gtalk_free_pvt ( struct gtalk client,
struct gtalk_pvt p 
) [static]

Definition at line 1212 of file chan_gtalk.c.

References gtalk::p, gtalk_pvt::next, gtalk_pvt::ringrule, gtalk_pvt::parent, gtalk::connection, aji_client::f, gtalk_pvt::owner, ast_log(), LOG_WARNING, gtalk_pvt::rtp, ast_rtp_instance_destroy(), gtalk_pvt::vrtp, gtalk_free_candidates(), gtalk_pvt::theircandidates, and ast_free.

Referenced by gtalk_newcall(), and gtalk_hangup().

{
   struct gtalk_pvt *cur, *prev = NULL;
   cur = client->p;
   while (cur) {
      if (cur == p) {
         if (prev)
            prev->next = p->next;
         else
            client->p = p->next;
         break;
      }
      prev = cur;
      cur = cur->next;
   }
   if (p->ringrule)
      iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
   if (p->owner)
      ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
   if (p->rtp)
      ast_rtp_instance_destroy(p->rtp);
   if (p->vrtp)
      ast_rtp_instance_destroy(p->vrtp);
   gtalk_free_candidates(p->theircandidates);
   ast_free(p);
}
static format_t gtalk_get_codec ( struct ast_channel chan) [static]

Definition at line 540 of file chan_gtalk.c.

References ast_channel::tech_pvt, and gtalk_pvt::peercapability.

{
   struct gtalk_pvt *p = chan->tech_pvt;
   return p->peercapability;
}
static int gtalk_get_local_ip ( struct ast_sockaddr ourip) [static]

Definition at line 811 of file chan_gtalk.c.

References ast_sockaddr_from_sin, ast_sockaddr_is_any(), ast_sockaddr_copy(), ast_sockaddr_resolve(), PARSE_PORT_FORBID, ast_free, ast_ouraddrfor(), and ast_find_ourip().

Referenced by gtalk_create_candidates(), and load_module().

{
   struct ast_sockaddr root;
   struct ast_sockaddr bindaddr_tmp;
   struct ast_sockaddr *addrs;
   int addrs_cnt;

   /* If bind address is not 0.0.0.0, then bindaddr is our local ip. */
   ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
   if (!ast_sockaddr_is_any(&bindaddr_tmp)) {
      ast_sockaddr_copy(ourip, &bindaddr_tmp);
      return 0;
   }

   /* If no bind address was provided, lets see what ip we would use to connect to google.com and use that.
    * If you can't resolve google.com from your network, then this module is useless for you anyway. */
   if ((addrs_cnt = ast_sockaddr_resolve(&addrs, "google.com", PARSE_PORT_FORBID, AF_INET)) > 0) {
      ast_sockaddr_copy(&root, &addrs[0]);
      ast_free(addrs);
      if (!ast_ouraddrfor(&root, ourip)) {
         return 0;
      }
   }

   /* As a last resort, use this function to find our local address. */
   return ast_find_ourip(ourip, &bindaddr_tmp, AF_INET);
}
static enum ast_rtp_glue_result gtalk_get_rtp_peer ( struct ast_channel chan,
struct ast_rtp_instance **  instance 
) [static]

Definition at line 521 of file chan_gtalk.c.

References ast_channel::tech_pvt, AST_RTP_GLUE_RESULT_FORBID, ast_mutex_lock, gtalk_pvt::lock, gtalk_pvt::rtp, ao2_ref, AST_RTP_GLUE_RESULT_LOCAL, and ast_mutex_unlock.

{
   struct gtalk_pvt *p = chan->tech_pvt;
   enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;

   if (!p)
      return res;

   ast_mutex_lock(&p->lock);
   if (p->rtp){
      ao2_ref(p->rtp, +1);
      *instance = p->rtp;
      res = AST_RTP_GLUE_RESULT_LOCAL;
   }
   ast_mutex_unlock(&p->lock);

   return res;
}
static int gtalk_handle_dtmf ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 712 of file chan_gtalk.c.

References gtalk::p, gtalk_pvt::next, gtalk_pvt::sid, gtalk::connection, aji_client::jid, gtalk_response(), AST_FRAME_DTMF_BEGIN, ast_frame::subclass, ast_frame_subclass::integer, ast_queue_frame(), gtalk_pvt::owner, ast_verbose(), AST_FRAME_DTMF_END, AST_FRAME_DTMF, ast_log(), and LOG_NOTICE.

Referenced by gtalk_parser().

{
   struct gtalk_pvt *tmp;
   iks *dtmfnode = NULL, *dtmfchild = NULL;
   char *dtmf;
   char *from;
   /* Make sure our new call doesn't exist yet */
   for (tmp = client->p; tmp; tmp = tmp->next) {
      if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid))
         break;
   }
   from = iks_find_attrib(pak->x, "to");
   if (!from) {
      from = client->connection->jid->full;
   }

   if (tmp) {
      if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) {
         gtalk_response(client, from, pak,
               "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
               "unsupported-dtmf-method xmlns='http://jabber.org/protocol/gtalk/info/dtmf#errors'");
         return -1;
      }
      if ((dtmfnode = iks_find(pak->x, "dtmf"))) {
         if((dtmf = iks_find_attrib(dtmfnode, "code"))) {
            if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) {
               struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
               f.subclass.integer = dtmf[0];
               ast_queue_frame(tmp->owner, &f);
               ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
            } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
               struct ast_frame f = {AST_FRAME_DTMF_END, };
               f.subclass.integer = dtmf[0];
               ast_queue_frame(tmp->owner, &f);
               ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
            } else if(iks_find_attrib(pak->x, "dtmf")) { /* 250 millasecond default */
               struct ast_frame f = {AST_FRAME_DTMF, };
               f.subclass.integer = dtmf[0];
               ast_queue_frame(tmp->owner, &f);
               ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
            }
         }
      } else if ((dtmfnode = iks_find_with_attrib(pak->x, "gtalk", "action", "session-info"))) {
         if((dtmfchild = iks_find(dtmfnode, "dtmf"))) {
            if((dtmf = iks_find_attrib(dtmfchild, "code"))) {
               if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) {
                  struct ast_frame f = {AST_FRAME_DTMF_END, };
                  f.subclass.integer = dtmf[0];
                  ast_queue_frame(tmp->owner, &f);
                  ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
               } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
                  struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
                  f.subclass.integer = dtmf[0];
                  ast_queue_frame(tmp->owner, &f);
                  ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
               }
            }
         }
      }
      gtalk_response(client, from, pak, NULL, NULL);
      return 1;
   } else {
      ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
   }

   gtalk_response(client, from, pak, NULL, NULL);
   return 1;
}
static int gtalk_hangup ( struct ast_channel ast) [static]

Hangup a call through the gtalk proxy channel.

Definition at line 1820 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_mutex_lock, gtalk_pvt::lock, gtalk_pvt::parent, gtalk_pvt::owner, gtalk_pvt::alreadygone, gtalk_action(), ast_mutex_unlock, gtalk_free_pvt(), ast_module_unref(), and ast_module_info::self.

Referenced by gtalk_newcall().

{
   struct gtalk_pvt *p = ast->tech_pvt;
   struct gtalk *client;

   ast_mutex_lock(&p->lock);
   client = p->parent;
   p->owner = NULL;
   ast->tech_pvt = NULL;
   if (!p->alreadygone) {
      gtalk_action(client, p, "terminate");
   }
   ast_mutex_unlock(&p->lock);

   gtalk_free_pvt(client, p);
   ast_module_unref(ast_module_info->self);

   return 0;
}
static int gtalk_hangup_farend ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 781 of file chan_gtalk.c.

References ast_debug, gtalk::name, gtalk::p, gtalk_pvt::next, gtalk_pvt::sid, gtalk::connection, aji_client::jid, gtalk_pvt::alreadygone, gtalk_pvt::owner, ast_queue_hangup(), ast_log(), LOG_NOTICE, and gtalk_response().

Referenced by gtalk_parser().

{
   struct gtalk_pvt *tmp;
   char *from;

   ast_debug(1, "The client is %s\n", client->name);
   /* Make sure our new call doesn't exist yet */
   for (tmp = client->p; tmp; tmp = tmp->next) {
      if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
         (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
         break;
      }
   }
   from = iks_find_attrib(pak->x, "to");
   if (!from) {
      from = client->connection->jid->full;
   }

   if (tmp) {
      tmp->alreadygone = 1;
      if (tmp->owner) {
         ast_queue_hangup(tmp->owner);
      }
   } else {
      ast_log(LOG_NOTICE, "Whoa, didn't find call during hangup!\n");
   }
   gtalk_response(client, from, pak, NULL, NULL);
   return 1;
}
static int gtalk_indicate ( struct ast_channel ast,
int  condition,
const void *  data,
size_t  datalen 
) [static]

Definition at line 1658 of file chan_gtalk.c.

References AST_CONTROL_HOLD, ast_moh_start(), AST_CONTROL_UNHOLD, ast_moh_stop(), and ast_debug.

{
   int res = 0;

   switch (condition) {
   case AST_CONTROL_HOLD:
      ast_moh_start(ast, data, NULL);
      break;
   case AST_CONTROL_UNHOLD:
      ast_moh_stop(ast);
      break;
   default:
      ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
      res = -1;
   }

   return res;
}
static int gtalk_invite ( struct gtalk_pvt p,
char *  to,
char *  from,
char *  sid,
int  initiator 
) [static]

Definition at line 383 of file chan_gtalk.c.

References gtalk_pvt::parent, ast_log(), LOG_ERROR, GOOGLE_AUDIO_NS, ast_codec_pref_index(), gtalk::prefs, gtalk::capability, add_codec_to_answer(), GOOGLE_TRANSPORT_NS, gtalk::connection, aji_client::mid, ast_aji_increment_mid(), GOOGLE_NS, ast_strdupa, and ast_aji_send().

Referenced by gtalk_ringing_ack(), gtalk_answer(), and gtalk_call().

{
   struct gtalk *client = p->parent;
   iks *iq, *gtalk, *dcodecs, *payload_telephone, *transport;
   int x;
   int pref_codec = 0;
   int alreadysent = 0;
   int codecs_num = 0;
   char *lowerto = NULL;

   iq = iks_new("iq");
   gtalk = iks_new("session");
   dcodecs = iks_new("description");
   transport = iks_new("transport");
   payload_telephone = iks_new("payload-type");
   if (!(iq && gtalk && dcodecs && transport && payload_telephone)){
      iks_delete(iq);
      iks_delete(gtalk);
      iks_delete(dcodecs);
      iks_delete(transport);
      iks_delete(payload_telephone);

      ast_log(LOG_ERROR, "Could not allocate iksemel nodes\n");
      return 0;
   }
   iks_insert_attrib(dcodecs, "xmlns", GOOGLE_AUDIO_NS);
   iks_insert_attrib(dcodecs, "xml:lang", "en");

   for (x = 0; x < 64; x++) {
      if (!(pref_codec = ast_codec_pref_index(&client->prefs, x)))
         break;
      if (!(client->capability & pref_codec))
         continue;
      if (alreadysent & pref_codec)
         continue;
      codecs_num = add_codec_to_answer(p, pref_codec, dcodecs);
      alreadysent |= pref_codec;
   }

   if (codecs_num) {
      /* only propose DTMF within an audio session */
      iks_insert_attrib(payload_telephone, "id", "101");
      iks_insert_attrib(payload_telephone, "name", "telephone-event");
      iks_insert_attrib(payload_telephone, "clockrate", "8000");
   }
   iks_insert_attrib(transport,"xmlns",GOOGLE_TRANSPORT_NS);

   iks_insert_attrib(iq, "type", "set");
   iks_insert_attrib(iq, "to", to);
   iks_insert_attrib(iq, "from", from);
   iks_insert_attrib(iq, "id", client->connection->mid);
   ast_aji_increment_mid(client->connection->mid);

   iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
   iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept");
   /* put the initiator attribute to lower case if we receive the call
    * otherwise GoogleTalk won't establish the session */
   if (!initiator) {
           char c;
           char *t = lowerto = ast_strdupa(to);
      while (((c = *t) != '/') && (*t++ = tolower(c)));
   }
   iks_insert_attrib(gtalk, "initiator", initiator ? from : lowerto);
   iks_insert_attrib(gtalk, "id", sid);
   iks_insert_node(iq, gtalk);
   iks_insert_node(gtalk, dcodecs);
   iks_insert_node(dcodecs, payload_telephone);

   ast_aji_send(client->connection, iq);

   iks_delete(payload_telephone);
   iks_delete(transport);
   iks_delete(dcodecs);
   iks_delete(gtalk);
   iks_delete(iq);
   return 1;
}
static int gtalk_is_accepted ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 683 of file chan_gtalk.c.

References ast_log(), LOG_DEBUG, gtalk::name, gtalk::p, gtalk_pvt::next, gtalk_pvt::sid, gtalk::connection, aji_client::jid, gtalk_update_stun(), gtalk_pvt::parent, LOG_NOTICE, and gtalk_response().

Referenced by gtalk_parser().

{
   struct gtalk_pvt *tmp;
   char *from;

   ast_log(LOG_DEBUG, "The client is %s\n", client->name);
   /* find corresponding call */
   for (tmp = client->p; tmp; tmp = tmp->next) {
      if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
         break;
      }
   }

   from = iks_find_attrib(pak->x, "to");
   if (!from) {
      from = client->connection->jid->full;
   }

   if (tmp) {
      gtalk_update_stun(tmp->parent, tmp);
   } else {
      ast_log(LOG_NOTICE, "Whoa, didn't find call during accept?!\n");
   }

   /* answer 'iq' packet to let the remote peer know that we're alive */
   gtalk_response(client, from, pak, NULL, NULL);
   return 1;
}
static int gtalk_is_answered ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 605 of file chan_gtalk.c.

References ast_log(), LOG_DEBUG, gtalk::name, gtalk::p, gtalk_pvt::next, gtalk_pvt::sid, LOG_WARNING, ast_rtp_codecs_payloads_set_m_type(), ast_rtp_instance_get_codecs(), gtalk_pvt::rtp, ast_rtp_codecs_payloads_set_rtpmap_type(), ast_rtp_codecs_payload_formats(), gtalk_pvt::peercapability, gtalk_pvt::jointcapability, gtalk_pvt::capability, ast_getformatname_multiple(), ast_queue_hangup(), gtalk_pvt::owner, gtalk::connection, aji_client::jid, ast_queue_control(), AST_CONTROL_ANSWER, gtalk_update_stun(), gtalk_pvt::parent, and gtalk_response().

Referenced by gtalk_parser().

{
   struct gtalk_pvt *tmp = NULL;
   char *from;
   iks *codec;
   char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
   int peernoncodeccapability;

   ast_log(LOG_DEBUG, "The client is %s\n", client->name);

   /* Make sure our new call does exist */
   for (tmp = client->p; tmp; tmp = tmp->next) {
      if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
         break;
      } else if (iks_find_with_attrib(pak->x, "ses:session", "id", tmp->sid)) {
         break;
      }
   }

   if (!tmp) {
      ast_log(LOG_WARNING, "Could not find session in iq\n");
      return -1;
   }

   /* codec points to the first <payload-type/> tag */
   codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
   while (codec) {
      char *codec_id = iks_find_attrib(codec, "id");
      char *codec_name = iks_find_attrib(codec, "name");
      if (!codec_id || !codec_name) {
         codec = iks_next_tag(codec);
         continue;
      }

      ast_rtp_codecs_payloads_set_m_type(
         ast_rtp_instance_get_codecs(tmp->rtp),
         tmp->rtp,
         atoi(codec_id));
      ast_rtp_codecs_payloads_set_rtpmap_type(
         ast_rtp_instance_get_codecs(tmp->rtp),
         tmp->rtp,
         atoi(codec_id),
         "audio",
         codec_name,
         0);
      codec = iks_next_tag(codec);
   }

   /* Now gather all of the codecs that we are asked for */
   ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), &tmp->peercapability, &peernoncodeccapability);

   /* at this point, we received an awser from the remote Gtalk client,
      which allows us to compare capabilities */
   tmp->jointcapability = tmp->capability & tmp->peercapability;
   if (!tmp->jointcapability) {
      ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->capability),
         ast_getformatname_multiple(s2, BUFSIZ, tmp->peercapability),
         ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcapability));
      /* close session if capabilities don't match */
      ast_queue_hangup(tmp->owner);

      return -1;

   }

   from = iks_find_attrib(pak->x, "to");
   if (!from) {
      from = client->connection->jid->full;
   }

   if (tmp->owner) {
      ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
   }
   gtalk_update_stun(tmp->parent, tmp);
   gtalk_response(client, from, pak, NULL, NULL);
   return 1;
}
static int gtalk_load_config ( void  ) [static]

Definition at line 2063 of file chan_gtalk.c.

References context, AST_MAX_CONTEXT, parkinglot, var, clients, hp, ast_config_load, GOOGLE_CONFIG, CONFIG_STATUS_FILEINVALID, ast_log(), LOG_ERROR, global_jbconf, stunaddr, ast_category_browse(), ast_variable_browse(), ast_variable::next, ast_jb_read_conf(), ast_variable::name, ast_variable::value, ast_true(), ast_variable_retrieve(), ast_parse_allow_disallow(), ast_copy_string(), ast_gethostbyname(), LOG_WARNING, STANDARD_STUN_PORT, ast_parse_arg(), PARSE_INADDR, ast_calloc, ASTOBJ_INIT, ASTOBJ_WRLOCK, gtalk::name, gtalk::user, gtalk::context, gtalk::parkinglot, gtalk::allowguest, gtalk::prefs, prefs, gtalk::capability, ASTOBJ_UNLOCK, ast_aji_get_clients(), ASTOBJ_CONTAINER_TRAVERSE, gtalk::connection, gtalk_parser(), GOOGLE_NS, GOOGLE_JINGLE_NS, ASTOBJ_CONTAINER_LINK, gtalk_list, ASTOBJ_UNREF, gtalk_member_destroy(), gtalk_create_member(), gtalk_update_externip(), and gtalk_free_candidates().

Referenced by load_module().

{
   char *cat = NULL;
   struct ast_config *cfg = NULL;
   char context[AST_MAX_CONTEXT];
   char parkinglot[AST_MAX_CONTEXT];
   int allowguest = 1;
   struct ast_variable *var;
   struct gtalk *member;
   struct ast_codec_pref prefs;
   struct aji_client_container *clients;
   struct gtalk_candidate *global_candidates = NULL;
   struct hostent *hp;
   struct ast_hostent ahp;
   struct ast_flags config_flags = { 0 };

   cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
   if (!cfg) {
      return 0;
   } else if (cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", GOOGLE_CONFIG);
      return 0;
   }

   /* Copy the default jb config over global_jbconf */
   memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));

   /* set defaults */
   memset(&stunaddr, 0, sizeof(stunaddr));

   cat = ast_category_browse(cfg, NULL);
   for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
      /* handle jb conf */
      if (!ast_jb_read_conf(&global_jbconf, var->name, var->value))
         continue;

      if (!strcasecmp(var->name, "allowguest")) {
         allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
      } else if (!strcasecmp(var->name, "disallow")) {
         ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0);
      } else if (!strcasecmp(var->name, "allow")) {
         ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
      } else if (!strcasecmp(var->name, "context")) {
         ast_copy_string(context, var->value, sizeof(context));
      } else if (!strcasecmp(var->name, "externip")) {
         ast_copy_string(externip, var->value, sizeof(externip));
      } else if (!strcasecmp(var->name, "parkinglot")) {
         ast_copy_string(parkinglot, var->value, sizeof(parkinglot));
      } else if (!strcasecmp(var->name, "bindaddr")) {
         if (!(hp = ast_gethostbyname(var->value, &ahp))) {
            ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
         } else {
            memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
         }
      } else if (!strcasecmp(var->name, "stunaddr")) {
         stunaddr.sin_port = htons(STANDARD_STUN_PORT);
         if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
            ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
         }
      }
   }
   while (cat) {
      if (strcasecmp(cat, "general")) {
         var = ast_variable_browse(cfg, cat);
         member = ast_calloc(1, sizeof(*member));
         ASTOBJ_INIT(member);
         ASTOBJ_WRLOCK(member);
         if (!strcasecmp(cat, "guest")) {
            ast_copy_string(member->name, "guest", sizeof(member->name));
            ast_copy_string(member->user, "guest", sizeof(member->user));
            ast_copy_string(member->context, context, sizeof(member->context));
            ast_copy_string(member->parkinglot, parkinglot, sizeof(member->parkinglot));
            member->allowguest = allowguest;
            member->prefs = prefs;
            while (var) {
               if (!strcasecmp(var->name, "disallow")) {
                  ast_parse_allow_disallow(&member->prefs, &member->capability,
                                     var->value, 0);
               } else if (!strcasecmp(var->name, "allow")) {
                  ast_parse_allow_disallow(&member->prefs, &member->capability,
                                     var->value, 1);
               } else if (!strcasecmp(var->name, "context")) {
                  ast_copy_string(member->context, var->value,
                              sizeof(member->context));
               } else if (!strcasecmp(var->name, "parkinglot")) {
                  ast_copy_string(member->parkinglot, var->value,
                              sizeof(member->parkinglot));
               }
               var = var->next;
            }
            ASTOBJ_UNLOCK(member);
            clients = ast_aji_get_clients();
            if (clients) {
               ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
                  ASTOBJ_WRLOCK(iterator);
                  ASTOBJ_WRLOCK(member);
                  member->connection = NULL;
                  iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
                  iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_JINGLE_NS, IKS_RULE_DONE);
                  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);
                  ASTOBJ_UNLOCK(member);
                  ASTOBJ_UNLOCK(iterator);
               });
               ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
               ASTOBJ_UNREF(member, gtalk_member_destroy);
            } else {
               ASTOBJ_UNLOCK(member);
               ASTOBJ_UNREF(member, gtalk_member_destroy);
            }
         } else {
            ASTOBJ_UNLOCK(member);
            if (gtalk_create_member(cat, var, allowguest, prefs, context, member))
               ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
            ASTOBJ_UNREF(member, gtalk_member_destroy);
         }
      }
      cat = ast_category_browse(cfg, cat);
   }

   gtalk_update_externip();
   gtalk_free_candidates(global_candidates);
   return 1;
}
static void gtalk_member_destroy ( struct gtalk obj) [static]

Definition at line 241 of file chan_gtalk.c.

References ast_free.

Referenced by gtalk_request(), gtalk_parser(), gtalk_load_config(), and unload_module().

{
   ast_free(obj);
}
static struct ast_channel* gtalk_new ( struct gtalk client,
struct gtalk_pvt i,
int  state,
const char *  title,
const char *  linkedid 
) [static, read]

Start new gtalk channel.

Definition at line 1074 of file chan_gtalk.c.

References gtalk_pvt::us, ast_channel_alloc(), gtalk_pvt::cid_num, gtalk_pvt::cid_name, gtalk::accountcode, gtalk_pvt::exten, gtalk::context, gtalk::amaflags, ast_random(), ast_log(), LOG_WARNING, ast_channel::tech, gtalk_tech, gtalk_pvt::jointcapability, gtalk_pvt::capability, global_capability, gtalk_pvt::rtp, ast_rtp_codecs_packetization_set(), ast_rtp_instance_get_codecs(), gtalk_pvt::prefs, ast_channel::nativeformats, ast_codec_choose(), AST_FORMAT_VIDEO_MASK, ast_best_codec(), ast_channel_set_fd(), ast_rtp_instance_fd(), gtalk_pvt::vrtp, AST_STATE_RING, ast_channel::rings, ast_channel::adsicpe, AST_ADSI_UNAVAILABLE, ast_channel::writeformat, ast_channel::rawwriteformat, ast_channel::readformat, ast_channel::rawreadformat, ast_channel::tech_pvt, ast_channel::callgroup, gtalk::callgroup, ast_channel::pickupgroup, gtalk::pickupgroup, ast_channel::caller, ast_party_caller::id, ast_party_id::name, ast_party_name::presentation, gtalk::callingpres, ast_party_id::number, ast_party_number::presentation, ast_strlen_zero(), ast_string_field_set, accountcode, ast_channel::amaflags, gtalk::language, language, gtalk::musicclass, musicclass, gtalk::parkinglot, parkinglot, gtalk_pvt::owner, ast_module_ref(), ast_module_info::self, ast_copy_string(), ast_channel::context, ast_channel::exten, ast_channel::dialed, ast_party_dialed::number, ast_party_dialed::str, ast_strdup, ast_channel::priority, ast_jb_configure(), global_jbconf, AST_STATE_DOWN, ast_pbx_start(), ast_channel::name, ast_channel::hangupcause, AST_CAUSE_SWITCH_CONGESTION, ast_hangup(), manager_event, EVENT_FLAG_SYSTEM, and gtalk_pvt::sid.

Referenced by gtalk_newcall(), and gtalk_request().

{
   struct ast_channel *tmp;
   int fmt;
   int what;
   const char *n2;

   if (title)
      n2 = title;
   else
      n2 = i->us;
   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);
   if (!tmp) {
      ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
      return NULL;
   }
   tmp->tech = &gtalk_tech;

   /* Select our native format based on codec preference until we receive
      something from another device to the contrary. */
   if (i->jointcapability)
      what = i->jointcapability;
   else if (i->capability)
      what = i->capability;
   else
      what = global_capability;

   /* Set Frame packetization */
   if (i->rtp) {
      ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
   }

   tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
   fmt = ast_best_codec(tmp->nativeformats);

   if (i->rtp) {
      ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
      ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
   }
   if (i->vrtp) {
      ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
      ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
   }
   if (state == AST_STATE_RING)
      tmp->rings = 1;
   tmp->adsicpe = AST_ADSI_UNAVAILABLE;
   tmp->writeformat = fmt;
   tmp->rawwriteformat = fmt;
   tmp->readformat = fmt;
   tmp->rawreadformat = fmt;
   tmp->tech_pvt = i;

   tmp->callgroup = client->callgroup;
   tmp->pickupgroup = client->pickupgroup;
   tmp->caller.id.name.presentation = client->callingpres;
   tmp->caller.id.number.presentation = client->callingpres;
   if (!ast_strlen_zero(client->accountcode))
      ast_string_field_set(tmp, accountcode, client->accountcode);
   if (client->amaflags)
      tmp->amaflags = client->amaflags;
   if (!ast_strlen_zero(client->language))
      ast_string_field_set(tmp, language, client->language);
   if (!ast_strlen_zero(client->musicclass))
      ast_string_field_set(tmp, musicclass, client->musicclass);
   if (!ast_strlen_zero(client->parkinglot))
      ast_string_field_set(tmp, parkinglot, client->parkinglot);
   i->owner = tmp;
   ast_module_ref(ast_module_info->self);
   ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
   ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));

   if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
      tmp->dialed.number.str = ast_strdup(i->exten);
   }
   tmp->priority = 1;
   if (i->rtp)
      ast_jb_configure(tmp, &global_jbconf);
   if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
      ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
      tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
      ast_hangup(tmp);
      tmp = NULL;
   } else {
      manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
         "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
         i->owner ? i->owner->name : "", "Gtalk", i->sid);
   }
   return tmp;
}
static int gtalk_newcall ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 1240 of file chan_gtalk.c.

References gtalk::p, gtalk::connection, aji_client::jid, gtalk_pvt::sid, ast_log(), LOG_NOTICE, gtalk_response(), gtalk_pvt::next, gtalk::name, ast_aji_get_client(), LOG_ERROR, LOG_WARNING, gtalk_alloc(), gtalk_new(), AST_STATE_DOWN, gtalk_free_pvt(), ast_mutex_lock, gtalk_pvt::lock, ast_copy_string(), gtalk_pvt::them, S_OR, gtalk_pvt::vrtp, ast_rtp_codecs_payloads_set_m_type(), ast_rtp_instance_get_codecs(), ast_rtp_codecs_payloads_set_rtpmap_type(), gtalk_pvt::rtp, ast_rtp_codecs_payload_formats(), gtalk_pvt::peercapability, gtalk_pvt::jointcapability, gtalk_pvt::capability, ast_mutex_unlock, ast_setstate(), AST_STATE_RING, ast_getformatname_multiple(), gtalk_action(), gtalk_pvt::alreadygone, gtalk_hangup(), ast_channel_release(), ast_pbx_start(), AST_PBX_FAILED, AST_PBX_CALL_LIMIT, AST_PBX_SUCCESS, gtalk_create_candidates(), and gtalk_pvt::us.

Referenced by gtalk_parser().

{
   struct gtalk_pvt *p, *tmp = client->p;
   struct ast_channel *chan;
   int res;
   iks *codec;
   char *from = NULL;
   char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
   int peernoncodeccapability;
   char *sid;

   /* Make sure our new call doesn't exist yet */
   from = iks_find_attrib(pak->x,"to");
   if (!from) {
      from = client->connection->jid->full;
   }

   while (tmp) {
      if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
         (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
         ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
         gtalk_response(client, from, pak, "out-of-order", NULL);
         return -1;
      }
      tmp = tmp->next;
   }

   if (!strcasecmp(client->name, "guest")){
      /* the guest account is not tied to any configured XMPP client,
         let's set it now */
      client->connection = ast_aji_get_client(from);
      if (!client->connection) {
         ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
         return -1;
      }
   }

   if (!(sid = iks_find_attrib(pak->query, "id"))) {
      ast_log(LOG_WARNING, "Received Initiate without id attribute. Can not start call.\n");
      return -1;
   }

   p = gtalk_alloc(client, from, pak->from->full, sid);
   if (!p) {
      ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
      return -1;
   }

   chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
   if (!chan) {
      gtalk_free_pvt(client, p);
      return -1;
   }

   ast_mutex_lock(&p->lock);
   ast_copy_string(p->them, pak->from->full, sizeof(p->them));
   ast_copy_string(p->sid, sid, sizeof(p->sid));

   /* codec points to the first <payload-type/> tag */
   codec = iks_first_tag(iks_first_tag(pak->query));

   while (codec) {
      char *codec_id = iks_find_attrib(codec, "id");
      char *codec_name = iks_find_attrib(codec, "name");
      if (!codec_id || !codec_name) {
         codec = iks_next_tag(codec);
         continue;
      }
      if (!strcmp(S_OR(iks_name(codec), ""), "vid:payload-type") && p->vrtp) {
         ast_rtp_codecs_payloads_set_m_type(
            ast_rtp_instance_get_codecs(p->vrtp),
            p->vrtp,
            atoi(codec_id));
         ast_rtp_codecs_payloads_set_rtpmap_type(
            ast_rtp_instance_get_codecs(p->vrtp),
            p->vrtp,
            atoi(codec_id),
            "video",
            codec_name,
            0);
      } else {
         ast_rtp_codecs_payloads_set_m_type(
            ast_rtp_instance_get_codecs(p->rtp),
            p->rtp,
            atoi(codec_id));
         ast_rtp_codecs_payloads_set_rtpmap_type(
            ast_rtp_instance_get_codecs(p->rtp),
            p->rtp,
            atoi(codec_id),
            "audio",
            codec_name,
            0);
      }
      codec = iks_next_tag(codec);
   }

   /* Now gather all of the codecs that we are asked for */
   ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability);
   p->jointcapability = p->capability & p->peercapability;
   ast_mutex_unlock(&p->lock);

   ast_setstate(chan, AST_STATE_RING);
   if (!p->jointcapability) {
      ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->capability),
         ast_getformatname_multiple(s2, BUFSIZ, p->peercapability),
         ast_getformatname_multiple(s3, BUFSIZ, p->jointcapability));
      /* close session if capabilities don't match */
      gtalk_action(client, p, "reject");
      p->alreadygone = 1;
      gtalk_hangup(chan);
      ast_channel_release(chan);
      return -1;
   }

   res = ast_pbx_start(chan);

   switch (res) {
   case AST_PBX_FAILED:
      ast_log(LOG_WARNING, "Failed to start PBX :(\n");
      gtalk_response(client, from, pak, "service-unavailable", NULL);
      break;
   case AST_PBX_CALL_LIMIT:
      ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
      gtalk_response(client, from, pak, "service-unavailable", NULL);
      break;
   case AST_PBX_SUCCESS:
      gtalk_response(client, from, pak, NULL, NULL);
      gtalk_create_candidates(client, p, p->sid, p->them, p->us);
      /* nothing to do */
      break;
   }

   return 1;
}
static int gtalk_parser ( void *  data,
ikspak *  pak 
) [static]

CLI command "gtalk reload".

Todo:
XXX TODO make this work.

Definition at line 1972 of file chan_gtalk.c.

References ASTOBJ_REF, ast_debug, S_OR, ast_log(), LOG_NOTICE, ast_strlen_zero(), gtalk_newcall(), gtalk_add_candidate(), LOG_WARNING, gtalk_is_answered(), gtalk_is_accepted(), gtalk_handle_dtmf(), gtalk_hangup_farend(), ASTOBJ_UNREF, and gtalk_member_destroy().

Referenced by gtalk_create_member(), and gtalk_load_config().

{
   struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
   int res;
   iks *tmp;

   if (!strcasecmp(iks_name(pak->query), "jin:jingle") && (tmp = iks_next(pak->query)) && !strcasecmp(iks_name(tmp), "ses:session")) {
      ast_debug(1, "New method detected. Skipping jingle offer and using old gtalk method.\n");
      pak->query = tmp;
   }

   if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
      ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
   }

   if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
      ast_log(LOG_NOTICE, "No attribute \"type\" found.  Ignoring message.\n");
   } else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
      /* New call */
      gtalk_newcall(client, pak);
   } else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
      ast_debug(3, "About to add candidate!\n");
      res = gtalk_add_candidate(client, pak);
      if (!res) {
         ast_log(LOG_WARNING, "Could not add any candidate\n");
      } else {
         ast_debug(3, "Candidate Added!\n");
      }
   } else if (!strcmp(iks_find_attrib(pak->query, "type"), "accept")) {
      gtalk_is_answered(client, pak);
   } else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
      gtalk_is_accepted(client, pak);
   } else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
      gtalk_handle_dtmf(client, pak);
   } else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
      gtalk_hangup_farend(client, pak);
   } else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
      gtalk_hangup_farend(client, pak);
   }
   ASTOBJ_UNREF(client, gtalk_member_destroy);
   return IKS_FILTER_EAT;
}
static struct ast_frame * gtalk_read ( struct ast_channel ast) [static, read]

Definition at line 1585 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_mutex_lock, gtalk_pvt::lock, gtalk_rtp_read(), and ast_mutex_unlock.

{
   struct ast_frame *fr;
   struct gtalk_pvt *p = ast->tech_pvt;

   ast_mutex_lock(&p->lock);
   fr = gtalk_rtp_read(ast, p);
   ast_mutex_unlock(&p->lock);
   return fr;
}
static struct ast_channel * gtalk_request ( const char *  type,
format_t  format,
const struct ast_channel requestor,
void *  data,
int *  cause 
) [static, read]

Part of PBX interface.

Definition at line 1841 of file chan_gtalk.c.

References ast_strdupa, strsep(), ast_log(), LOG_ERROR, find_gtalk(), LOG_WARNING, gtalk::name, gtalk::connection, ast_aji_get_client(), ASTOBJ_UNREF, gtalk_member_destroy(), ASTOBJ_WRLOCK, gtalk_alloc(), aji_client::jid, gtalk::user, gtalk_new(), AST_STATE_DOWN, ast_channel::linkedid, and ASTOBJ_UNLOCK.

{
   struct gtalk_pvt *p = NULL;
   struct gtalk *client = NULL;
   char *sender = NULL, *to = NULL, *s = NULL;
   struct ast_channel *chan = NULL;

   if (data) {
      s = ast_strdupa(data);
      if (s) {
         sender = strsep(&s, "/");
         if (sender && (sender[0] != '\0')) {
            to = strsep(&s, "/");
         }
         if (!to) {
            ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
            return NULL;
         }
      }
   }

   client = find_gtalk(to, sender);
   if (!client) {
      ast_log(LOG_WARNING, "Could not find recipient.\n");
      return NULL;
   }
   if (!strcasecmp(client->name, "guest")){
      /* the guest account is not tied to any configured XMPP client,
         let's set it now */
      client->connection = ast_aji_get_client(sender);
      if (!client->connection) {
         ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
         ASTOBJ_UNREF(client, gtalk_member_destroy);
         return NULL;
      }
   }

   ASTOBJ_WRLOCK(client);
   p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
   if (p) {
      chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
   }
   ASTOBJ_UNLOCK(client);
   return chan;
}
static int gtalk_response ( struct gtalk client,
char *  from,
ikspak *  pak,
const char *  reasonstr,
const char *  reasonstr2 
) [static]

Definition at line 573 of file chan_gtalk.c.

References S_OR, ast_aji_send(), and gtalk::connection.

Referenced by gtalk_is_answered(), gtalk_is_accepted(), gtalk_handle_dtmf(), gtalk_hangup_farend(), and gtalk_newcall().

{
   iks *response = NULL, *error = NULL, *reason = NULL;
   int res = -1;

   response = iks_new("iq");
   if (response) {
      iks_insert_attrib(response, "type", "result");
      iks_insert_attrib(response, "from", from);
      iks_insert_attrib(response, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
      iks_insert_attrib(response, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
      if (reasonstr) {
         error = iks_new("error");
         if (error) {
            iks_insert_attrib(error, "type", "cancel");
            reason = iks_new(reasonstr);
            if (reason)
               iks_insert_node(error, reason);
            iks_insert_node(response, error);
         }
      }
      ast_aji_send(client->connection, response);
      res = 0;
   }

   iks_delete(reason);
   iks_delete(error);
   iks_delete(response);

   return res;
}
static int gtalk_ringing_ack ( void *  data,
ikspak *  pak 
) [static]

Definition at line 461 of file chan_gtalk.c.

References ast_mutex_lock, gtalk_pvt::lock, gtalk_pvt::ringrule, gtalk_pvt::parent, gtalk::connection, aji_client::f, S_OR, name, ast_log(), LOG_DEBUG, ast_copy_string(), gtalk_pvt::them, gtalk_invite(), gtalk_pvt::us, gtalk_pvt::sid, gtalk_create_candidates(), gtalk_pvt::owner, ast_mutex_unlock, ast_queue_control(), and AST_CONTROL_RINGING.

Referenced by gtalk_call().

{
   struct gtalk_pvt *p = data;
   struct ast_channel *owner;

   ast_mutex_lock(&p->lock);

   if (p->ringrule) {
      iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
   }
   p->ringrule = NULL;

   /* this may be a redirect */
   if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
      char *name = NULL;
      char *redirect = NULL;
      iks *traversenodes = NULL;
      traversenodes = pak->query;
      while (traversenodes) {
         if (!(name = iks_name(traversenodes))) {
            break;
         }
         if (!strcasecmp(name, "error") &&
            (redirect = iks_find_cdata(traversenodes, "redirect")) &&
            (redirect = strstr(redirect, "xmpp:"))) {
            redirect += 5;
            ast_log(LOG_DEBUG, "redirect %s\n", redirect);
            ast_copy_string(p->them, redirect, sizeof(p->them));

            gtalk_invite(p, p->them, p->us, p->sid, 1);
            break;
         }
         traversenodes = iks_next_tag(traversenodes);
      }
   }
   gtalk_create_candidates(p->parent, p, p->sid, p->them, p->us);
   owner = p->owner;
   ast_mutex_unlock(&p->lock);

   if (owner) {
      ast_queue_control(owner, AST_CONTROL_RINGING);
   }

   return IKS_FILTER_EAT;
}
static struct ast_frame* gtalk_rtp_read ( struct ast_channel ast,
struct gtalk_pvt p 
) [static, read]

Definition at line 1556 of file chan_gtalk.c.

References f, gtalk_pvt::rtp, ast_null_frame, ast_rtp_instance_read(), gtalk_update_stun(), gtalk_pvt::parent, gtalk_pvt::owner, ast_frame::frametype, AST_FRAME_VOICE, ast_frame::subclass, ast_frame_subclass::codec, ast_channel::nativeformats, AST_FORMAT_AUDIO_MASK, ast_debug, ast_getformatname(), AST_FORMAT_VIDEO_MASK, ast_set_read_format(), ast_channel::readformat, ast_set_write_format(), and ast_channel::writeformat.

Referenced by gtalk_read().

{
   struct ast_frame *f;

   if (!p->rtp) {
      return &ast_null_frame;
   }
   f = ast_rtp_instance_read(p->rtp, 0);
   gtalk_update_stun(p->parent, p);
   if (p->owner) {
      /* We already hold the channel lock */
      if (f->frametype == AST_FRAME_VOICE) {
         if (f->subclass.codec != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
            ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
            p->owner->nativeformats =
               (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass.codec;
            ast_set_read_format(p->owner, p->owner->readformat);
            ast_set_write_format(p->owner, p->owner->writeformat);
         }
         /* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
            f = ast_dsp_process(p->owner, p->vad, f);
            if (option_debug && f && (f->frametype == AST_FRAME_DTMF))
               ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
           } */
      }
   }
   return f;
}
static int gtalk_sendhtml ( struct ast_channel ast,
int  subclass,
const char *  data,
int  datalen 
) [static]

Definition at line 1787 of file chan_gtalk.c.

References ast_log(), and LOG_NOTICE.

{
   ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");

   return -1;
}
static int gtalk_sendtext ( struct ast_channel ast,
const char *  text 
) [static]

Definition at line 1677 of file chan_gtalk.c.

References ast_channel::tech_pvt, gtalk_pvt::parent, ast_log(), LOG_ERROR, gtalk::connection, ast_aji_send_chat(), and gtalk_pvt::them.

{
   int res = 0;
   struct aji_client *client = NULL;
   struct gtalk_pvt *p = chan->tech_pvt;

   if (!p->parent) {
      ast_log(LOG_ERROR, "Parent channel not found\n");
      return -1;
   }
   if (!p->parent->connection) {
      ast_log(LOG_ERROR, "XMPP client not found\n");
      return -1;
   }
   client = p->parent->connection;
   res = ast_aji_send_chat(client, p->them, text);
   return res;
}
static int gtalk_set_rtp_peer ( struct ast_channel chan,
struct ast_rtp_instance rtp,
struct ast_rtp_instance vrtp,
struct ast_rtp_instance trtp,
format_t  codecs,
int  nat_active 
) [static]

Definition at line 546 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_mutex_lock, gtalk_pvt::lock, and ast_mutex_unlock.

{
   struct gtalk_pvt *p;

   p = chan->tech_pvt;
   if (!p)
      return -1;
   ast_mutex_lock(&p->lock);

/* if (rtp)
      ast_rtp_get_peer(rtp, &p->redirip);
   else
      memset(&p->redirip, 0, sizeof(p->redirip));
   p->redircodecs = codecs; */

   /* Reset lastrtprx timer */
   ast_mutex_unlock(&p->lock);
   return 0;
}
static char * gtalk_show_channels ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

CLI command "gtalk show channels".

Definition at line 1888 of file chan_gtalk.c.

References AJI_MAX_JIDLEN, CLI_INIT, ast_cli_entry::command, ast_cli_entry::usage, CLI_GENERATE, ast_cli_args::argc, CLI_SHOWUSAGE, ast_mutex_lock, gtalklock, ast_cli(), ast_cli_args::fd, FORMAT, ASTOBJ_CONTAINER_TRAVERSE, gtalk_list, ASTOBJ_WRLOCK, gtalk_pvt::owner, ast_copy_string(), gtalk_pvt::them, ast_channel::name, ast_getformatname(), ast_channel::readformat, ast_channel::writeformat, ast_log(), LOG_WARNING, gtalk_pvt::next, ASTOBJ_UNLOCK, ast_mutex_unlock, and CLI_SUCCESS.

{
#define FORMAT  "%-30.30s  %-30.30s  %-15.15s  %-5.5s %-5.5s \n"
   struct gtalk_pvt *p;
   struct ast_channel *chan;
   int numchans = 0;
   char them[AJI_MAX_JIDLEN];
   char *jid = NULL;
   char *resource = NULL;

   switch (cmd) {
   case CLI_INIT:
      e->command = "gtalk show channels";
      e->usage =
         "Usage: gtalk show channels\n"
         "       Shows current state of the Gtalk channels.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != 3)
      return CLI_SHOWUSAGE;

   ast_mutex_lock(&gtalklock);
   ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
   ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
      ASTOBJ_WRLOCK(iterator);
      p = iterator->p;
      while(p) {
         chan = p->owner;
         ast_copy_string(them, p->them, sizeof(them));
         jid = them;
         resource = strchr(them, '/');
         if (!resource)
            resource = "None";
         else {
            *resource = '\0';
            resource ++;
         }
         if (chan)
            ast_cli(a->fd, FORMAT,
               chan->name,
               jid,
               resource,
               ast_getformatname(chan->readformat),
               ast_getformatname(chan->writeformat)
               );
         else
            ast_log(LOG_WARNING, "No available channel\n");
         numchans ++;
         p = p->next;
      }
      ASTOBJ_UNLOCK(iterator);
   });

   ast_mutex_unlock(&gtalklock);

   ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
   return CLI_SUCCESS;
#undef FORMAT
}
static int gtalk_update_externip ( void  ) [static]

Definition at line 1375 of file chan_gtalk.c.

References stunaddr, ast_log(), LOG_WARNING, errno, ast_sockaddr_from_sin, ast_connect(), ast_sockaddr_stringify(), ast_stun_request(), ast_strdupa, and ast_inet_ntoa().

Referenced by gtalk_create_candidates(), and gtalk_load_config().

{
   int sock;
   char *newaddr;
   struct sockaddr_in answer = { 0, };
   struct sockaddr_in *dst;
   struct ast_sockaddr tmp_dst;

   if (!stunaddr.sin_addr.s_addr) {
      return -1;
   }
   dst = &stunaddr;

   sock = socket(AF_INET, SOCK_DGRAM, 0);
   if (sock < 0) {
      ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
      return -1;
   }

   ast_sockaddr_from_sin(&tmp_dst, dst);
   if (ast_connect(sock, &tmp_dst) != 0) {
      ast_log(LOG_WARNING, "STUN Failed to connect to %s\n", ast_sockaddr_stringify(&tmp_dst));
      close(sock);
      return -1;
   }

   if ((ast_stun_request(sock, &stunaddr, NULL, &answer))) {
      close(sock);
      return -1;
   }

   newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
   memcpy(externip, newaddr, sizeof(externip));

   close(sock);
   return 0;

}
static int gtalk_update_stun ( struct gtalk client,
struct gtalk_pvt p 
) [static]

Definition at line 1414 of file chan_gtalk.c.

References hp, gtalk_pvt::laststun, gtalk_pvt::theircandidates, ast_gethostbyname(), gtalk_candidate::ip, ast_log(), LOG_WARNING, gtalk_candidate::next, gtalk_candidate::port, gtalk_candidate::username, gtalk_pvt::ourcandidates, ast_rtp_instance_get_remote_address(), gtalk_pvt::rtp, ast_sockaddr_to_sin, ast_rtp_instance_stun_request(), ast_sockaddr_from_sin, ast_debug, and ast_inet_ntoa().

Referenced by gtalk_is_answered(), gtalk_is_accepted(), gtalk_add_candidate(), and gtalk_rtp_read().

{
   struct gtalk_candidate *tmp;
   struct hostent *hp;
   struct ast_hostent ahp;
   struct sockaddr_in sin = { 0, };
   struct sockaddr_in aux = { 0, };
   struct ast_sockaddr sin_tmp;
   struct ast_sockaddr aux_tmp;

   if (time(NULL) == p->laststun)
      return 0;

   tmp = p->theircandidates;
   p->laststun = time(NULL);
   while (tmp) {
      char username[256];

      /* Find the IP address of the host */
      if (!(hp = ast_gethostbyname(tmp->ip, &ahp))) {
         ast_log(LOG_WARNING, "Could not get host by name for %s\n", tmp->ip);
         tmp = tmp->next;
         continue;
      }
      sin.sin_family = AF_INET;
      memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
      sin.sin_port = htons(tmp->port);
      snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);

      /* Find out the result of the STUN */
      ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
      ast_sockaddr_to_sin(&aux_tmp, &aux);

      /* If the STUN result is different from the IP of the hostname,
       * lock on the stun IP of the hostname advertised by the
       * remote client */
      if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
         ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
      } else {
         ast_sockaddr_from_sin(&sin_tmp, &sin);
         ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
      }
      if (aux.sin_addr.s_addr) {
         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);
         ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
      }

      tmp = tmp->next;
   }
   return 1;
}
static int gtalk_write ( struct ast_channel ast,
struct ast_frame f 
) [static]

Send frame to media channel (rtp)

Definition at line 1597 of file chan_gtalk.c.

References ast_channel::tech_pvt, ast_frame::frametype, AST_FRAME_VOICE, ast_frame::subclass, ast_frame_subclass::codec, ast_channel::nativeformats, ast_log(), LOG_WARNING, ast_getformatname(), ast_getformatname_multiple(), ast_channel::readformat, ast_channel::writeformat, ast_mutex_lock, gtalk_pvt::lock, gtalk_pvt::rtp, ast_rtp_instance_write(), ast_mutex_unlock, AST_FRAME_VIDEO, gtalk_pvt::vrtp, and AST_FRAME_IMAGE.

{
   struct gtalk_pvt *p = ast->tech_pvt;
   int res = 0;
   char buf[256];

   switch (frame->frametype) {
   case AST_FRAME_VOICE:
      if (!(frame->subclass.codec & ast->nativeformats)) {
         ast_log(LOG_WARNING,
               "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
               ast_getformatname(frame->subclass.codec),
               ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
               ast_getformatname(ast->readformat),
               ast_getformatname(ast->writeformat));
         return 0;
      }
      if (p) {
         ast_mutex_lock(&p->lock);
         if (p->rtp) {
            res = ast_rtp_instance_write(p->rtp, frame);
         }
         ast_mutex_unlock(&p->lock);
      }
      break;
   case AST_FRAME_VIDEO:
      if (p) {
         ast_mutex_lock(&p->lock);
         if (p->vrtp) {
            res = ast_rtp_instance_write(p->vrtp, frame);
         }
         ast_mutex_unlock(&p->lock);
      }
      break;
   case AST_FRAME_IMAGE:
      return 0;
      break;
   default:
      ast_log(LOG_WARNING, "Can't send %d type frames with Gtalk write\n",
            frame->frametype);
      return 0;
   }

   return res;
}
static int load_module ( void  ) [static]

Load module into PBX, register channel.

Definition at line 2188 of file chan_gtalk.c.

References ast_module_helper(), free, ast_log(), LOG_ERROR, AST_MODULE_LOAD_DECLINE, ASTOBJ_CONTAINER_INIT, gtalk_list, gtalk_load_config(), GOOGLE_CONFIG, sched_context_create(), LOG_WARNING, io_context_create(), ast_sockaddr_from_sin, gtalk_get_local_ip(), __ourip, ast_sockaddr_ipv4(), ast_rtp_glue_register, ast_cli_register_multiple(), ARRAY_LEN, ast_channel_register(), and ast_channel_tech::type.

{
   struct ast_sockaddr bindaddr_tmp;
   struct ast_sockaddr ourip_tmp;

   char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
   free(jabber_loaded);
   if (!jabber_loaded) {
      /* If embedded, check for a different module name */
      jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0);
      free(jabber_loaded);
      if (!jabber_loaded) {
         ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n");
         return AST_MODULE_LOAD_DECLINE;
      }
   }

   ASTOBJ_CONTAINER_INIT(&gtalk_list);
   if (!gtalk_load_config()) {
      ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
      return 0;
   }

   sched = sched_context_create();
   if (!sched) {
      ast_log(LOG_WARNING, "Unable to create schedule context\n");
   }

   io = io_context_create();
   if (!io) {
      ast_log(LOG_WARNING, "Unable to create I/O context\n");
   }

   ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
   if (gtalk_get_local_ip(&ourip_tmp)) {
      ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
      return 0;
   }
   __ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));

   ast_rtp_glue_register(&gtalk_rtp_glue);
   ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));

   /* Make sure we can register our channel type */
   if (ast_channel_register(&gtalk_tech)) {
      ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
      return -1;
   }
   return 0;
}
static int unload_module ( void  ) [static]

Reload module.

Todo:
XXX TODO make this work.

Unload the gtalk channel from Asterisk

Definition at line 2248 of file chan_gtalk.c.

References ast_cli_unregister_multiple(), ARRAY_LEN, ast_channel_unregister(), ast_rtp_glue_unregister(), ast_mutex_lock, gtalklock, ASTOBJ_CONTAINER_TRAVERSE, gtalk_list, ASTOBJ_WRLOCK, gtalk_pvt::owner, ast_softhangup(), AST_SOFTHANGUP_APPUNLOAD, gtalk_pvt::next, ASTOBJ_UNLOCK, ast_mutex_unlock, ast_log(), LOG_WARNING, ASTOBJ_CONTAINER_DESTROYALL, gtalk_member_destroy(), and ASTOBJ_CONTAINER_DESTROY.

{
   struct gtalk_pvt *privates = NULL;
   ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
   /* First, take us out of the channel loop */
   ast_channel_unregister(&gtalk_tech);
   ast_rtp_glue_unregister(&gtalk_rtp_glue);

   if (!ast_mutex_lock(&gtalklock)) {
      /* Hangup all interfaces if they have an owner */
      ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
         ASTOBJ_WRLOCK(iterator);
         privates = iterator->p;
         while(privates) {
            if (privates->owner)
               ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
            privates = privates->next;
         }
         iterator->p = NULL;
         ASTOBJ_UNLOCK(iterator);
      });
      ast_mutex_unlock(&gtalklock);
   } else {
      ast_log(LOG_WARNING, "Unable to lock the monitor\n");
      return -1;
   }
   ASTOBJ_CONTAINER_DESTROYALL(&gtalk_list, gtalk_member_destroy);
   ASTOBJ_CONTAINER_DESTROY(&gtalk_list);
   return 0;
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Gtalk Channel Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "7a1b8b48c852d7a7061c7e499b9bd0d2" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, } [static]

Definition at line 2284 of file chan_gtalk.c.

struct in_addr __ourip [static]

Definition at line 229 of file chan_gtalk.c.

Referenced by load_module().

Definition at line 2284 of file chan_gtalk.c.

struct sockaddr_in bindaddr = { 0, } [static]
struct ast_jb_conf default_jbconf [static]

Global jitterbuffer configuration - by default, jb is disabled

Definition at line 83 of file chan_gtalk.c.

const char desc[] = "Gtalk Channel" [static]

Definition at line 169 of file chan_gtalk.c.

char externip[16] [static]

Definition at line 236 of file chan_gtalk.c.

format_t global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263 [static]

Definition at line 171 of file chan_gtalk.c.

Referenced by gtalk_alloc(), and gtalk_new().

struct ast_jb_conf global_jbconf [static]

Definition at line 91 of file chan_gtalk.c.

Referenced by gtalk_new(), and gtalk_load_config().

struct ast_cli_entry gtalk_cli[] [static]
Initial value:
 {

   AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"),
}

Definition at line 231 of file chan_gtalk.c.

struct ast_rtp_glue gtalk_rtp_glue [static]

Definition at line 566 of file chan_gtalk.c.

struct ast_channel_tech gtalk_tech [static]

PBX interface structure for channel registration.

Definition at line 201 of file chan_gtalk.c.

Referenced by gtalk_new().

ast_mutex_t gtalklock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]

Protect the interface list (of gtalk_pvt's)

Definition at line 173 of file chan_gtalk.c.

Referenced by gtalk_alloc(), gtalk_show_channels(), and unload_module().

struct io_context* io [static]

The IO context

Definition at line 228 of file chan_gtalk.c.

Referenced by ast_udptl_new_with_bindaddr().

struct sched_context* sched [static]

The scheduling context

Definition at line 227 of file chan_gtalk.c.

struct sockaddr_in stunaddr [static]

the stun server we get the externip from

Definition at line 237 of file chan_gtalk.c.

Referenced by gtalk_update_externip(), and gtalk_load_config().