Sat Apr 26 2014 22:02:07

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 "asterisk/features.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 FORMAT   " %-25.20s %-15.30s\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, struct ast_format *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, const 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 void gtalk_get_codec (struct ast_channel *chan, struct ast_format_cap *result)
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, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *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, const struct ast_format_cap *cap, 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 char * gtalk_show_settings (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 List global settings for the GoogleTalk channel.
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 = AST_BUILDOPT_SUM, .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 const int DEFAULT_ALLOWGUEST = 1
static const char DEFAULT_CONTEXT [] = "default"
static struct ast_jb_conf default_jbconf
static const char desc [] = "Gtalk Channel"
static char externip [16]
static int global_allowguest
static struct ast_format_capglobal_capability
static char global_context [AST_MAX_CONTEXT]
static struct ast_jb_conf global_jbconf
static char global_parkinglot [AST_MAX_CONTEXT]
static int global_stunaddr
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 ast_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 FORMAT   " %-25.20s %-15.30s\n"
#define GOOGLE_CONFIG   "gtalk.conf"

Definition at line 82 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 100 of file chan_gtalk.c.

Enumerator:
AJI_PROTOCOL_UDP 
AJI_PROTOCOL_SSLTCP 

Definition at line 95 of file chan_gtalk.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 2413 of file chan_gtalk.c.

static void __unreg_module ( void  ) [static]

Definition at line 2413 of file chan_gtalk.c.

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

Definition at line 296 of file chan_gtalk.c.

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

Referenced by gtalk_invite().

{
   int res = 0;
   const 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\n");
         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\n");
         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\n");
         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\n");
         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\n");
         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\n");
         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 264 of file chan_gtalk.c.

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

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 1218 of file chan_gtalk.c.

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

Referenced by gtalk_hangup(), and gtalk_newcall().

{
   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 1526 of file chan_gtalk.c.

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

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 1003 of file chan_gtalk.c.

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

Referenced by gtalk_newcall(), and gtalk_request().

{
   struct gtalk_pvt *tmp = NULL;
   struct aji_resource *resources = NULL;
   struct aji_buddy *buddy = NULL;
   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");
         if (buddy) {
            ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
         }
         return NULL;
      }
      if (buddy) {
         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
      }
   }
   if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
      return NULL;
   }
   tmp->cap = ast_format_cap_alloc_nolock();
   tmp->jointcap = ast_format_cap_alloc_nolock();
   tmp->peercap = ast_format_cap_alloc_nolock();
   if (!tmp->jointcap || !tmp->peercap || !tmp->cap) {
      tmp->cap = ast_format_cap_destroy(tmp->cap);
      tmp->jointcap = ast_format_cap_destroy(tmp->jointcap);
      tmp->peercap = ast_format_cap_destroy(tmp->peercap);
      ast_free(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 (!(ast_format_cap_is_empty(client->cap))) {
      ast_format_cap_copy(tmp->cap, client->cap);
   } else if (!(ast_format_cap_is_empty(global_capability))) {
      ast_format_cap_copy(tmp->cap, 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 533 of file chan_gtalk.c.

References ast_channel_name(), ast_channel_tech_pvt(), ast_debug, ast_mutex_lock, ast_mutex_unlock, EVENT_FLAG_SYSTEM, gtalk_invite(), gtalk_pvt::lock, manager_event, gtalk_pvt::sid, gtalk_pvt::them, and gtalk_pvt::us.

{
   struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
   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_channel_name(ast), "GTALK", p->sid);
   ast_mutex_unlock(&p->lock);
   return res;
}
static int gtalk_call ( struct ast_channel ast,
const char *  dest,
int  timeout 
) [static]

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

Definition at line 1858 of file chan_gtalk.c.

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

{
   struct gtalk_pvt *p = ast_channel_tech_pvt(ast);

   if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
      ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
      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 866 of file chan_gtalk.c.

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

Referenced by gtalk_newcall(), and gtalk_ringing_ack().

{
   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.\n");
   }

   /* 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 cur;
         char *t = lowerfrom = ast_strdupa(from);
         while (((cur = *t) != '/') && (*t++ = tolower(cur)));
      }
      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 2118 of file chan_gtalk.c.

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

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->cap, var->value, 0);
      else if (!strcasecmp(var->name, "allow"))
         ast_parse_allow_disallow(&member->prefs, member->cap, 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 1758 of file chan_gtalk.c.

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

{
   struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
   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 1774 of file chan_gtalk.c.

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

{
   struct gtalk_pvt *p = ast_channel_tech_pvt(chan);
   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 1703 of file chan_gtalk.c.

References ast_channel_tech_pvt(), ast_mutex_lock, ast_mutex_unlock, gtalk_pvt::lock, and gtalk_pvt::owner.

{
   struct gtalk_pvt *p = ast_channel_tech_pvt(newchan);
   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 1256 of file chan_gtalk.c.

References ast_free, last, and gtalk_candidate::next.

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 1266 of file chan_gtalk.c.

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

Referenced by gtalk_hangup(), and gtalk_newcall().

{
   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);
   p->cap = ast_format_cap_destroy(p->cap);
   p->jointcap = ast_format_cap_destroy(p->jointcap);
   p->peercap = ast_format_cap_destroy(p->peercap);
   ast_free(p);
}
static void gtalk_get_codec ( struct ast_channel chan,
struct ast_format_cap result 
) [static]
static int gtalk_get_local_ip ( struct ast_sockaddr ourip) [static]

Definition at line 839 of file chan_gtalk.c.

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

Referenced by gtalk_create_candidates(), and load_module().

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

   /* 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 (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]
static int gtalk_handle_dtmf ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 740 of file chan_gtalk.c.

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

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 1882 of file chan_gtalk.c.

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

Referenced by gtalk_newcall().

{
   struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
   struct gtalk *client;

   ast_mutex_lock(&p->lock);
   client = p->parent;
   p->owner = NULL;
   ast_channel_tech_pvt_set(ast, 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 809 of file chan_gtalk.c.

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

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 1718 of file chan_gtalk.c.

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

{
   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);
      /* fallthrough */
   case AST_CONTROL_PVT_CAUSE_CODE:
      res = -1;
   }

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

Definition at line 401 of file chan_gtalk.c.

References add_codec_to_answer(), ast_aji_increment_mid(), ast_aji_send(), ast_codec_pref_index(), AST_CODEC_PREF_SIZE, ast_format_cap_add(), ast_format_cap_alloc_nolock(), ast_format_cap_destroy(), ast_format_cap_iscompatible(), ast_log(), gtalk::cap, gtalk::connection, GOOGLE_AUDIO_NS, GOOGLE_NS, GOOGLE_TRANSPORT_NS, LOG_ERROR, aji_client::mid, gtalk_pvt::parent, and gtalk::prefs.

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

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

   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");

   if (!(alreadysent = ast_format_cap_alloc_nolock())) {
      return 0;
   }
   for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
      if (!(ast_codec_pref_index(&client->prefs, x, &tmpfmt))) {
         break;
      }
      if (!(ast_format_cap_iscompatible(client->cap, &tmpfmt))) {
         continue;
      }
      if (ast_format_cap_iscompatible(alreadysent, &tmpfmt)) {
         continue;
      }
      codecs_num = add_codec_to_answer(p, &tmpfmt, dcodecs);
      ast_format_cap_add(alreadysent, &tmpfmt);
   }
   alreadysent = ast_format_cap_destroy(alreadysent);

   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 711 of file chan_gtalk.c.

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

Referenced by gtalk_parser().

{
   struct gtalk_pvt *tmp;
   char *from;

   ast_debug(1, "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 633 of file chan_gtalk.c.

References AST_CONTROL_ANSWER, ast_debug, ast_format_cap_is_empty(), ast_format_cap_joint_copy(), ast_getformatname_multiple(), ast_log(), ast_queue_control(), ast_queue_hangup(), ast_rtp_codecs_payload_formats(), ast_rtp_codecs_payloads_set_m_type(), ast_rtp_codecs_payloads_set_rtpmap_type(), ast_rtp_instance_get_codecs(), gtalk_pvt::cap, gtalk::connection, gtalk_response(), gtalk_update_stun(), aji_client::jid, gtalk_pvt::jointcap, LOG_WARNING, gtalk::name, gtalk_pvt::next, gtalk_pvt::owner, gtalk::p, gtalk_pvt::parent, gtalk_pvt::peercap, gtalk_pvt::rtp, and gtalk_pvt::sid.

Referenced by gtalk_parser().

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

   ast_debug(1, "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->peercap, &peernoncodeccapability);

   /* at this point, we received an answer from the remote Gtalk client,
      which allows us to compare capabilities */
   ast_format_cap_joint_copy(tmp->cap, tmp->peercap, tmp->jointcap);
   if (ast_format_cap_is_empty(tmp->jointcap)) {
      ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->cap),
         ast_getformatname_multiple(s2, BUFSIZ, tmp->peercap),
         ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcap));
      /* 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 2166 of file chan_gtalk.c.

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

Referenced by load_module().

{
   char *cat = NULL;
   struct ast_config *cfg = NULL;
   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(&prefs, 0, sizeof(prefs));
   memset(&stunaddr, 0, sizeof(stunaddr));
   global_stunaddr = 0;
   global_allowguest = DEFAULT_ALLOWGUEST;
   ast_copy_string(global_context, DEFAULT_CONTEXT, sizeof(global_context));
   ast_copy_string(global_parkinglot, DEFAULT_PARKINGLOT, sizeof(global_parkinglot));

   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")) {
         global_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(global_context, var->value, sizeof(global_context));
      } else if (!strcasecmp(var->name, "externip")) {
         ast_copy_string(externip, var->value, sizeof(externip));
      } else if (!strcasecmp(var->name, "parkinglot")) {
         ast_copy_string(global_parkinglot, var->value, sizeof(global_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);
         global_stunaddr = 1;
         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);
         member->cap = ast_format_cap_alloc_nolock();
         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, global_context, sizeof(member->context));
            ast_copy_string(member->parkinglot, global_parkinglot, sizeof(member->parkinglot));
            member->allowguest = global_allowguest;
            member->prefs = prefs;
            while (var) {
               if (!strcasecmp(var->name, "disallow")) {
                  ast_parse_allow_disallow(&member->prefs, member->cap,
                                     var->value, 0);
               } else if (!strcasecmp(var->name, "allow")) {
                  ast_parse_allow_disallow(&member->prefs, member->cap,
                                     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);
                  if (member->connection) {
                     ASTOBJ_UNREF(member->connection, ast_aji_client_destroy);
                  }
                  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, global_allowguest, prefs, global_context, member)) {
               ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
            }
            ASTOBJ_UNREF(member, gtalk_member_destroy);
         }
      }
      cat = ast_category_browse(cfg, cat);
   }

   ast_config_destroy(cfg);
   gtalk_update_externip();
   gtalk_free_candidates(global_candidates);
   return 1;
}
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 1117 of file chan_gtalk.c.

References gtalk::accountcode, gtalk::amaflags, AST_ADSI_UNAVAILABLE, ast_best_codec(), AST_CAUSE_SWITCH_CONGESTION, ast_channel_adsicpe_set(), ast_channel_alloc(), ast_channel_amaflags_set(), ast_channel_caller(), ast_channel_callgroup_set(), ast_channel_context_set(), ast_channel_dialed(), ast_channel_exten_set(), ast_channel_hangupcause_set(), ast_channel_name(), ast_channel_nativeformats(), ast_channel_pickupgroup_set(), ast_channel_priority_set(), ast_channel_rawreadformat(), ast_channel_rawwriteformat(), ast_channel_readformat(), ast_channel_rings_set(), ast_channel_set_fd(), ast_channel_tech_pvt_set(), ast_channel_tech_set(), ast_channel_writeformat(), ast_codec_choose(), ast_format_cap_add(), ast_format_cap_is_empty(), ast_format_cap_iter_end(), ast_format_cap_iter_next(), ast_format_cap_iter_start(), ast_format_copy(), AST_FORMAT_GET_TYPE, AST_FORMAT_TYPE_VIDEO, ast_hangup(), ast_jb_configure(), ast_log(), ast_module_ref(), ast_pbx_start(), ast_random(), ast_rtp_codecs_packetization_set(), ast_rtp_instance_fd(), ast_rtp_instance_get_codecs(), AST_STATE_DOWN, AST_STATE_RING, ast_strdup, ast_strlen_zero(), gtalk::callgroup, gtalk::callingpres, gtalk_pvt::cap, gtalk_pvt::cid_name, gtalk_pvt::cid_num, gtalk::context, EVENT_FLAG_SYSTEM, gtalk_pvt::exten, global_capability, global_jbconf, ast_format::id, ast_party_caller::id, gtalk_pvt::jointcap, gtalk::language, LOG_WARNING, manager_event, gtalk::musicclass, ast_party_id::name, ast_party_id::number, ast_party_dialed::number, gtalk_pvt::owner, gtalk::parkinglot, gtalk::pickupgroup, gtalk_pvt::prefs, ast_party_name::presentation, ast_party_number::presentation, gtalk_pvt::rtp, ast_module_info::self, gtalk_pvt::sid, ast_party_dialed::str, gtalk_pvt::us, and gtalk_pvt::vrtp.

Referenced by gtalk_newcall(), and gtalk_request().

{
   struct ast_channel *tmp;
   const char *n2;
   struct ast_format_cap *what; /* used as SHALLOW COPY DO NOT DESTROY */
   struct ast_format tmpfmt;

   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;
   }
   ast_channel_tech_set(tmp, &gtalk_tech);

   /* Select our native format based on codec preference until we receive
      something from another device to the contrary. */
   if (!(ast_format_cap_is_empty(i->jointcap))) {
      what = i->jointcap;
   } else if (i->cap) {
      what = i->cap;
   } 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);
   }

   ast_codec_choose(&i->prefs, what, 1, &tmpfmt);
   ast_format_cap_add(ast_channel_nativeformats(tmp), &tmpfmt);

   ast_format_cap_iter_start(i->jointcap);
   while (!(ast_format_cap_iter_next(i->jointcap, &tmpfmt))) {
      if (AST_FORMAT_GET_TYPE(tmpfmt.id) == AST_FORMAT_TYPE_VIDEO) {
         ast_format_cap_add(ast_channel_nativeformats(tmp), &tmpfmt);
      }
   }
   ast_format_cap_iter_end(i->jointcap);

   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)
      ast_channel_rings_set(tmp, 1);
   ast_channel_adsicpe_set(tmp, AST_ADSI_UNAVAILABLE);

   ast_best_codec(ast_channel_nativeformats(tmp), &tmpfmt);
   ast_format_copy(ast_channel_writeformat(tmp), &tmpfmt);
   ast_format_copy(ast_channel_rawwriteformat(tmp), &tmpfmt);
   ast_format_copy(ast_channel_readformat(tmp), &tmpfmt);
   ast_format_copy(ast_channel_rawreadformat(tmp), &tmpfmt);
   ast_channel_tech_pvt_set(tmp, i);

   ast_channel_callgroup_set(tmp, client->callgroup);
   ast_channel_pickupgroup_set(tmp, client->pickupgroup);
   ast_channel_caller(tmp)->id.name.presentation = client->callingpres;
   ast_channel_caller(tmp)->id.number.presentation = client->callingpres;
   if (!ast_strlen_zero(client->accountcode))
      ast_channel_accountcode_set(tmp, client->accountcode);
   if (client->amaflags)
      ast_channel_amaflags_set(tmp, client->amaflags);
   if (!ast_strlen_zero(client->language))
      ast_channel_language_set(tmp, client->language);
   if (!ast_strlen_zero(client->musicclass))
      ast_channel_musicclass_set(tmp, client->musicclass);
   if (!ast_strlen_zero(client->parkinglot))
      ast_channel_parkinglot_set(tmp, client->parkinglot);
   i->owner = tmp;
   ast_module_ref(ast_module_info->self);
   ast_channel_context_set(tmp, client->context);
   ast_channel_exten_set(tmp, i->exten);

   if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
      ast_channel_dialed(tmp)->number.str = ast_strdup(i->exten);
   }
   ast_channel_priority_set(tmp, 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", ast_channel_name(tmp));
      ast_channel_hangupcause_set(tmp, 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 ? ast_channel_name(i->owner) : "", "Gtalk", i->sid);
   }
   return tmp;
}
static int gtalk_newcall ( struct gtalk client,
ikspak *  pak 
) [static]

Definition at line 1297 of file chan_gtalk.c.

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

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 */
      if (client->connection) {
         ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
      }
      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->peercap, &peernoncodeccapability);
   ast_format_cap_joint_copy(p->cap, p->peercap, p->jointcap);
   ast_mutex_unlock(&p->lock);

   ast_setstate(chan, AST_STATE_RING);
   if (ast_format_cap_is_empty(p->jointcap)) {
      ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->cap),
         ast_getformatname_multiple(s2, BUFSIZ, p->peercap),
         ast_getformatname_multiple(s3, BUFSIZ, p->jointcap));
      /* 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 2075 of file chan_gtalk.c.

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

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 1645 of file chan_gtalk.c.

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

{
   struct ast_frame *fr;
   struct gtalk_pvt *p = ast_channel_tech_pvt(ast);

   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,
struct ast_format_cap cap,
const struct ast_channel requestor,
const char *  data,
int *  cause 
) [static, read]

Part of PBX interface.

Definition at line 1903 of file chan_gtalk.c.

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

{
   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);
      sender = strsep(&s, "/");
      if (sender && (sender[0] != '\0')) {
         to = strsep(&s, "/");
      }
      if (!to) {
         ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", data);
         return NULL;
      }
      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 */
      if (client->connection) {
         ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
      }
      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 ? ast_channel_linkedid(requestor) : 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 601 of file chan_gtalk.c.

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

Referenced by gtalk_handle_dtmf(), gtalk_hangup_farend(), gtalk_is_accepted(), gtalk_is_answered(), 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 486 of file chan_gtalk.c.

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

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 = iks_find_cdata(traversenodes, "sta:redirect"))) &&
            (redirect = strstr(redirect, "xmpp:"))) {
            redirect += 5;
            ast_debug(1, "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 1616 of file chan_gtalk.c.

References ast_channel_nativeformats(), ast_channel_readformat(), ast_channel_writeformat(), ast_debug, ast_format_cap_add(), ast_format_cap_iscompatible(), ast_format_cap_remove_bytype(), AST_FORMAT_TYPE_AUDIO, AST_FRAME_VOICE, ast_getformatname(), ast_null_frame, ast_rtp_instance_read(), ast_set_read_format(), ast_set_write_format(), f, ast_frame_subclass::format, ast_frame::frametype, gtalk_update_stun(), gtalk_pvt::owner, gtalk_pvt::parent, gtalk_pvt::rtp, and ast_frame::subclass.

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 (!ast_format_cap_iscompatible(ast_channel_nativeformats(p->owner), &f->subclass.format)) {
            ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
            ast_format_cap_remove_bytype(ast_channel_nativeformats(p->owner), AST_FORMAT_TYPE_AUDIO);
            ast_format_cap_add(ast_channel_nativeformats(p->owner), &f->subclass.format);
            ast_set_read_format(p->owner, ast_channel_readformat(p->owner));
            ast_set_write_format(p->owner, ast_channel_writeformat(p->owner));
         }
         /* 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 1849 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 1739 of file chan_gtalk.c.

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

{
   int res = 0;
   struct aji_client *client = NULL;
   struct gtalk_pvt *p = ast_channel_tech_pvt(chan);

   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,
const struct ast_format_cap cap,
int  nat_active 
) [static]

Definition at line 574 of file chan_gtalk.c.

References ast_channel_tech_pvt(), ast_mutex_lock, ast_mutex_unlock, and gtalk_pvt::lock.

{
   struct gtalk_pvt *p;

   p = ast_channel_tech_pvt(chan);
   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 1955 of file chan_gtalk.c.

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

{
#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,
               ast_channel_name(chan),
               jid,
               resource,
               ast_getformatname(ast_channel_readformat(chan)),
               ast_getformatname(ast_channel_writeformat(chan))
               );
         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 char * gtalk_show_settings ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

List global settings for the GoogleTalk channel.

Definition at line 2019 of file chan_gtalk.c.

References ast_cli_args::argc, ast_cli(), AST_CLI_YESNO, ast_getformatname_multiple(), ast_inet_ntoa(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, FORMAT, S_OR, stunaddr, and ast_cli_entry::usage.

{
   char codec_buf[BUFSIZ];
   switch (cmd) {
   case CLI_INIT:
      e->command = "gtalk show settings";
      e->usage =
         "Usage: gtalk show settings\n"
         "       Provides detailed list of the configuration on the GoogleTalk channel.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

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

#define FORMAT "  %-25.20s  %-15.30s\n"

   ast_cli(a->fd, "\nGlobal Settings:\n");
   ast_cli(a->fd, "----------------\n");
   ast_cli(a->fd, FORMAT, "UDP Bindaddress:", ast_inet_ntoa(bindaddr.sin_addr));
   ast_cli(a->fd, FORMAT, "Stun Address:", global_stunaddr != 0 ? ast_inet_ntoa(stunaddr.sin_addr) : "Disabled");
   ast_cli(a->fd, FORMAT, "External IP:", S_OR(externip, "Disabled"));
   ast_cli(a->fd, FORMAT, "Context:", global_context);
   ast_cli(a->fd, FORMAT, "Codecs:", ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, global_capability));
   ast_cli(a->fd, FORMAT, "Parking Lot:", global_parkinglot);
   ast_cli(a->fd, FORMAT, "Allow Guest:", AST_CLI_YESNO(global_allowguest));
   ast_cli(a->fd, "\n----\n");

   return CLI_SUCCESS;
#undef FORMAT
}
static int gtalk_update_externip ( void  ) [static]

Definition at line 1435 of file chan_gtalk.c.

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

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 1474 of file chan_gtalk.c.

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

Referenced by gtalk_add_candidate(), gtalk_is_accepted(), gtalk_is_answered(), 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 1657 of file chan_gtalk.c.

References ast_channel_nativeformats(), ast_channel_readformat(), ast_channel_tech_pvt(), ast_channel_writeformat(), ast_format_cap_iscompatible(), AST_FRAME_IMAGE, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_getformatname(), ast_getformatname_multiple(), ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_rtp_instance_write(), ast_frame_subclass::format, ast_frame::frametype, gtalk_pvt::lock, LOG_WARNING, gtalk_pvt::rtp, ast_frame::subclass, and gtalk_pvt::vrtp.

{
   struct gtalk_pvt *p = ast_channel_tech_pvt(ast);
   int res = 0;
   char buf[256];

   switch (frame->frametype) {
   case AST_FRAME_VOICE:
      if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format))) {
         ast_log(LOG_WARNING,
               "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
               ast_getformatname(&frame->subclass.format),
               ast_getformatname_multiple(buf, sizeof(buf), ast_channel_nativeformats(ast)),
               ast_getformatname(ast_channel_readformat(ast)),
               ast_getformatname(ast_channel_writeformat(ast)));
         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 2301 of file chan_gtalk.c.

References __ourip, ARRAY_LEN, ast_channel_register(), ast_cli_register_multiple(), AST_FORMAT_ALAW, ast_format_cap_add(), ast_format_cap_add_all_by_type(), ast_format_cap_alloc(), AST_FORMAT_GSM, AST_FORMAT_H263, ast_format_set(), AST_FORMAT_TYPE_AUDIO, AST_FORMAT_ULAW, ast_log(), ast_module_helper(), AST_MODULE_LOAD_DECLINE, ast_rtp_glue_register, ast_sched_context_create(), ast_sockaddr_from_sin, ast_sockaddr_ipv4(), ASTOBJ_CONTAINER_INIT, ast_channel_tech::capabilities, free, GOOGLE_CONFIG, gtalk_get_local_ip(), gtalk_list, gtalk_load_config(), io_context_create(), LOG_ERROR, LOG_WARNING, 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);
   struct ast_format tmpfmt;

   if (!(gtalk_tech.capabilities = ast_format_cap_alloc())) {
      return AST_MODULE_LOAD_DECLINE;
   }
   if (!(global_capability = ast_format_cap_alloc())) {
      return AST_MODULE_LOAD_DECLINE;
   }

   ast_format_cap_add_all_by_type(gtalk_tech.capabilities, AST_FORMAT_TYPE_AUDIO);
   ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0));
   ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0));
   ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0));
   ast_format_cap_add(global_capability, ast_format_set(&tmpfmt, AST_FORMAT_H263, 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 = ast_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 2375 of file chan_gtalk.c.

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

{
   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);
   global_capability = ast_format_cap_destroy(global_capability);
   gtalk_tech.capabilities = ast_format_cap_destroy(gtalk_tech.capabilities);
   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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, } [static]

Definition at line 2413 of file chan_gtalk.c.

struct in_addr __ourip [static]

Definition at line 233 of file chan_gtalk.c.

Referenced by load_module().

Definition at line 2413 of file chan_gtalk.c.

struct sockaddr_in bindaddr = { 0, } [static]
const int DEFAULT_ALLOWGUEST = 1 [static]

Definition at line 173 of file chan_gtalk.c.

Referenced by gtalk_load_config(), and reload_config().

const char DEFAULT_CONTEXT[] = "default" [static]

Definition at line 172 of file chan_gtalk.c.

struct ast_jb_conf default_jbconf [static]

Global jitterbuffer configuration - by default, jb is disabled

Definition at line 85 of file chan_gtalk.c.

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

Definition at line 171 of file chan_gtalk.c.

char externip[16] [static]

Definition at line 241 of file chan_gtalk.c.

int global_allowguest [static]

Definition at line 244 of file chan_gtalk.c.

Referenced by gtalk_load_config().

Definition at line 175 of file chan_gtalk.c.

Referenced by gtalk_new().

Definition at line 242 of file chan_gtalk.c.

struct ast_jb_conf global_jbconf [static]

Definition at line 93 of file chan_gtalk.c.

Referenced by gtalk_load_config(), and gtalk_new().

Definition at line 243 of file chan_gtalk.c.

int global_stunaddr [static]

Definition at line 246 of file chan_gtalk.c.

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

   AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"),
   AST_CLI_DEFINE(gtalk_show_settings, "Show GoogleTalk global settings"),
}

Definition at line 235 of file chan_gtalk.c.

struct ast_rtp_glue gtalk_rtp_glue [static]

Definition at line 594 of file chan_gtalk.c.

struct ast_channel_tech gtalk_tech [static]

PBX interface structure for channel registration.

Definition at line 206 of file chan_gtalk.c.

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

Protect the interface list (of gtalk_pvt's)

Definition at line 177 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 232 of file chan_gtalk.c.

Referenced by ast_udptl_new_with_bindaddr().

struct ast_sched_context* sched [static]

The scheduling context

Definition at line 231 of file chan_gtalk.c.

struct sockaddr_in stunaddr [static]

the stun server we get the externip from

Definition at line 245 of file chan_gtalk.c.

Referenced by gtalk_load_config(), gtalk_show_settings(), and gtalk_update_externip().