2006-09-18 16:36:14 +00:00
|
|
|
/*
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1999 - 2005, Digium, Inc.
|
|
|
|
*
|
|
|
|
* Matt O'Gorman <mogorman@digium.com>
|
2010-10-07 22:39:29 +00:00
|
|
|
* Philippe Sultan <philippe.sultan@gmail.com>
|
2006-09-18 16:36:14 +00:00
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file
|
|
|
|
*
|
|
|
|
* \author Matt O'Gorman <mogorman@digium.com>
|
2010-10-07 22:39:29 +00:00
|
|
|
* \author Philippe Sultan <philippe.sultan@gmail.com>
|
2006-09-18 16:36:14 +00:00
|
|
|
*
|
|
|
|
* \brief Gtalk Channel Driver, until google/libjingle works with jingle spec
|
2010-10-05 22:01:52 +00:00
|
|
|
*
|
2006-09-18 16:36:14 +00:00
|
|
|
* \ingroup channel_drivers
|
2010-10-11 21:39:37 +00:00
|
|
|
*
|
|
|
|
* ********** General TODO:s
|
|
|
|
* \todo Support config reloading.
|
|
|
|
* \todo Fix native bridging.
|
2006-09-18 16:36:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*** MODULEINFO
|
|
|
|
<depend>iksemel</depend>
|
2007-02-09 23:53:51 +00:00
|
|
|
<depend>res_jabber</depend>
|
2007-11-01 23:26:51 +00:00
|
|
|
<use>openssl</use>
|
2006-09-18 16:36:14 +00:00
|
|
|
***/
|
|
|
|
|
|
|
|
#include "asterisk.h"
|
|
|
|
|
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
|
|
|
|
#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>
|
2007-05-24 19:05:42 +00:00
|
|
|
#include <pthread.h>
|
2009-02-12 10:31:36 +00:00
|
|
|
#include <ctype.h>
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
#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"
|
2009-04-02 17:20:52 +00:00
|
|
|
#include "asterisk/rtp_engine.h"
|
|
|
|
#include "asterisk/stun.h"
|
2006-09-18 16:36:14 +00:00
|
|
|
#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"
|
2010-10-05 22:01:52 +00:00
|
|
|
#include "asterisk/jingle.h"
|
2010-11-02 15:14:12 +00:00
|
|
|
#include "asterisk/features.h"
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2010-11-02 15:14:12 +00:00
|
|
|
#define GOOGLE_CONFIG "gtalk.conf"
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
/*! Global jitterbuffer configuration - by default, jb is disabled */
|
|
|
|
static struct ast_jb_conf default_jbconf =
|
|
|
|
{
|
|
|
|
.flags = 0,
|
|
|
|
.max_size = -1,
|
|
|
|
.resync_threshold = -1,
|
2010-03-02 19:08:38 +00:00
|
|
|
.impl = "",
|
|
|
|
.target_extra = -1,
|
2006-09-18 16:36:14 +00:00
|
|
|
};
|
|
|
|
static struct ast_jb_conf global_jbconf;
|
|
|
|
|
|
|
|
enum gtalk_protocol {
|
|
|
|
AJI_PROTOCOL_UDP = 1,
|
|
|
|
AJI_PROTOCOL_SSLTCP = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum gtalk_connect_type {
|
|
|
|
AJI_CONNECT_STUN = 1,
|
|
|
|
AJI_CONNECT_LOCAL = 2,
|
|
|
|
AJI_CONNECT_RELAY = 3,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct gtalk_pvt {
|
|
|
|
ast_mutex_t lock; /*!< Channel private lock */
|
|
|
|
time_t laststun;
|
|
|
|
struct gtalk *parent; /*!< Parent client */
|
|
|
|
char sid[100];
|
2007-09-06 17:00:58 +00:00
|
|
|
char us[AJI_MAX_JIDLEN];
|
|
|
|
char them[AJI_MAX_JIDLEN];
|
2006-09-18 16:36:14 +00:00
|
|
|
char ring[10]; /*!< Message ID of ring */
|
|
|
|
iksrule *ringrule; /*!< Rule for matching RING request */
|
|
|
|
int initiator; /*!< If we're the initiator */
|
|
|
|
int alreadygone;
|
|
|
|
struct ast_codec_pref prefs;
|
|
|
|
struct gtalk_candidate *theircandidates;
|
|
|
|
struct gtalk_candidate *ourcandidates;
|
|
|
|
char cid_num[80]; /*!< Caller ID num */
|
|
|
|
char cid_name[80]; /*!< Caller ID name */
|
|
|
|
char exten[80]; /*!< Called extension */
|
|
|
|
struct ast_channel *owner; /*!< Master Channel */
|
2009-04-02 17:20:52 +00:00
|
|
|
struct ast_rtp_instance *rtp; /*!< RTP audio session */
|
|
|
|
struct ast_rtp_instance *vrtp; /*!< RTP video session */
|
2011-02-03 16:22:10 +00:00
|
|
|
struct ast_format_cap *cap;
|
|
|
|
struct ast_format_cap *jointcap; /*!< Supported capability at both ends (codecs ) */
|
|
|
|
struct ast_format_cap *peercap;
|
2006-09-18 16:36:14 +00:00
|
|
|
struct gtalk_pvt *next; /* Next entity */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct gtalk_candidate {
|
|
|
|
char name[100];
|
|
|
|
enum gtalk_protocol protocol;
|
|
|
|
double preference;
|
|
|
|
char username[100];
|
|
|
|
char password[100];
|
|
|
|
enum gtalk_connect_type type;
|
|
|
|
char network[6];
|
|
|
|
int generation;
|
|
|
|
char ip[16];
|
|
|
|
int port;
|
|
|
|
int receipt;
|
|
|
|
struct gtalk_candidate *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct gtalk {
|
|
|
|
ASTOBJ_COMPONENTS(struct gtalk);
|
|
|
|
struct aji_client *connection;
|
|
|
|
struct aji_buddy *buddy;
|
|
|
|
struct gtalk_pvt *p;
|
|
|
|
struct ast_codec_pref prefs;
|
|
|
|
int amaflags; /*!< AMA Flags */
|
2007-09-06 17:00:58 +00:00
|
|
|
char user[AJI_MAX_JIDLEN];
|
|
|
|
char context[AST_MAX_CONTEXT];
|
2008-04-21 23:42:45 +00:00
|
|
|
char parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */
|
2006-09-18 16:36:14 +00:00
|
|
|
char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
|
2011-02-03 16:22:10 +00:00
|
|
|
struct ast_format_cap *cap;
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_group_t callgroup; /*!< Call group */
|
|
|
|
ast_group_t pickupgroup; /*!< Pickup group */
|
|
|
|
int callingpres; /*!< Calling presentation */
|
|
|
|
int allowguest;
|
|
|
|
char language[MAX_LANGUAGE]; /*!< Default language for prompts */
|
|
|
|
char musicclass[MAX_MUSICCLASS]; /*!< Music on Hold class */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct gtalk_container {
|
|
|
|
ASTOBJ_CONTAINER_COMPONENTS(struct gtalk);
|
|
|
|
};
|
|
|
|
|
2010-11-02 15:14:12 +00:00
|
|
|
static const char desc[] = "Gtalk Channel";
|
|
|
|
static const char DEFAULT_CONTEXT[] = "default";
|
|
|
|
static const int DEFAULT_ALLOWGUEST = 1;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2011-02-03 16:22:10 +00:00
|
|
|
static struct ast_format_cap *global_capability;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pvt's) */
|
|
|
|
|
|
|
|
/* Forward declarations */
|
2011-02-03 16:22:10 +00:00
|
|
|
static struct ast_channel *gtalk_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int *cause);
|
2010-10-06 21:09:14 +00:00
|
|
|
/*static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration);*/
|
2009-09-25 10:54:42 +00:00
|
|
|
static int gtalk_sendtext(struct ast_channel *ast, const char *text);
|
Merged revisions 51311 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r51311 | russell | 2007-01-19 11:49:38 -0600 (Fri, 19 Jan 2007) | 23 lines
Merge the changes from the /team/group/vldtmf_fixup branch.
The main bug being addressed here is a problem introduced when two SIP
channels using SIP INFO dtmf have their media directly bridged. So, when a
DTMF END frame comes into Asterisk from an incoming INFO message, Asterisk
would try to emulate a digit of some length by first sending a DTMF BEGIN
frame and sending a DTMF END later timed off of incoming audio. However,
since there was no audio coming in, the DTMF_END was never generated. This
caused DTMF based features to no longer work.
To fix this, the core now knows when a channel doesn't care about DTMF BEGIN
frames (such as a SIP channel sending INFO dtmf). If this is the case, then
Asterisk will not emulate a digit of some length, and will instead just pass
through the single DTMF END event.
Channel drivers also now get passed the length of the digit to their digit_end
callback. This improves SIP INFO support even further by enabling us to put
the real digit duration in the INFO message instead of a hard coded 250ms.
Also, for an incoming INFO message, the duration is read from the frame and
passed into the core instead of just getting ignored.
(issue #8597, maybe others...)
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@51314 f38db490-d61c-443f-a65b-d21fe96a405b
2007-01-19 18:06:03 +00:00
|
|
|
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);
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_call(struct ast_channel *ast, char *dest, int timeout);
|
|
|
|
static int gtalk_hangup(struct ast_channel *ast);
|
|
|
|
static int gtalk_answer(struct ast_channel *ast);
|
2008-04-04 17:32:46 +00:00
|
|
|
static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action);
|
|
|
|
static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p);
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_newcall(struct gtalk *client, ikspak *pak);
|
|
|
|
static struct ast_frame *gtalk_read(struct ast_channel *ast);
|
|
|
|
static int gtalk_write(struct ast_channel *ast, struct ast_frame *f);
|
|
|
|
static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
|
|
|
|
static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
|
|
|
|
static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
|
|
|
|
static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid);
|
2010-10-11 21:39:37 +00:00
|
|
|
static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p);
|
|
|
|
/* static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); */
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
|
2010-11-02 15:14:12 +00:00
|
|
|
static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
|
2010-10-11 21:39:37 +00:00
|
|
|
static int gtalk_update_externip(void);
|
2010-12-09 21:33:22 +00:00
|
|
|
static int gtalk_parser(void *data, ikspak *pak);
|
|
|
|
static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to);
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
/*! \brief PBX interface structure for channel registration */
|
2011-02-03 16:22:10 +00:00
|
|
|
static struct ast_channel_tech gtalk_tech = {
|
2006-11-30 21:22:01 +00:00
|
|
|
.type = "Gtalk",
|
2006-09-18 16:36:14 +00:00
|
|
|
.description = "Gtalk Channel Driver",
|
|
|
|
.requester = gtalk_request,
|
2009-09-25 10:54:42 +00:00
|
|
|
.send_text = gtalk_sendtext,
|
Merged revisions 51311 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r51311 | russell | 2007-01-19 11:49:38 -0600 (Fri, 19 Jan 2007) | 23 lines
Merge the changes from the /team/group/vldtmf_fixup branch.
The main bug being addressed here is a problem introduced when two SIP
channels using SIP INFO dtmf have their media directly bridged. So, when a
DTMF END frame comes into Asterisk from an incoming INFO message, Asterisk
would try to emulate a digit of some length by first sending a DTMF BEGIN
frame and sending a DTMF END later timed off of incoming audio. However,
since there was no audio coming in, the DTMF_END was never generated. This
caused DTMF based features to no longer work.
To fix this, the core now knows when a channel doesn't care about DTMF BEGIN
frames (such as a SIP channel sending INFO dtmf). If this is the case, then
Asterisk will not emulate a digit of some length, and will instead just pass
through the single DTMF END event.
Channel drivers also now get passed the length of the digit to their digit_end
callback. This improves SIP INFO support even further by enabling us to put
the real digit duration in the INFO message instead of a hard coded 250ms.
Also, for an incoming INFO message, the duration is read from the frame and
passed into the core instead of just getting ignored.
(issue #8597, maybe others...)
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@51314 f38db490-d61c-443f-a65b-d21fe96a405b
2007-01-19 18:06:03 +00:00
|
|
|
.send_digit_begin = gtalk_digit_begin,
|
|
|
|
.send_digit_end = gtalk_digit_end,
|
2010-10-06 21:09:14 +00:00
|
|
|
/* XXX TODO native bridging is causing odd problems with DTMF pass-through with
|
|
|
|
* the gtalk servers. Enable native bridging once the source of this problem has
|
|
|
|
* been identified.
|
|
|
|
.bridge = ast_rtp_instance_bridge, */
|
2006-09-18 16:36:14 +00:00
|
|
|
.call = gtalk_call,
|
|
|
|
.hangup = gtalk_hangup,
|
|
|
|
.answer = gtalk_answer,
|
|
|
|
.read = gtalk_read,
|
|
|
|
.write = gtalk_write,
|
|
|
|
.exception = gtalk_read,
|
|
|
|
.indicate = gtalk_indicate,
|
|
|
|
.fixup = gtalk_fixup,
|
|
|
|
.send_html = gtalk_sendhtml,
|
|
|
|
.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */
|
|
|
|
|
2010-12-20 17:15:54 +00:00
|
|
|
static struct ast_sched_context *sched; /*!< The scheduling context */
|
2006-09-18 16:36:14 +00:00
|
|
|
static struct io_context *io; /*!< The IO context */
|
|
|
|
static struct in_addr __ourip;
|
|
|
|
|
|
|
|
static struct ast_cli_entry gtalk_cli[] = {
|
2010-10-11 21:39:37 +00:00
|
|
|
/* AST_CLI_DEFINE(gtalk_do_reload, "Reload GoogleTalk configuration"), XXX TODO reloads are not possible yet. */
|
2007-10-22 20:05:18 +00:00
|
|
|
AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"),
|
2010-11-02 15:14:12 +00:00
|
|
|
AST_CLI_DEFINE(gtalk_show_settings, "Show GoogleTalk global settings"),
|
2007-10-11 19:03:06 +00:00
|
|
|
};
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
static char externip[16];
|
2010-11-02 15:14:12 +00:00
|
|
|
static char global_context[AST_MAX_CONTEXT];
|
|
|
|
static char global_parkinglot[AST_MAX_CONTEXT];
|
|
|
|
static int global_allowguest;
|
2010-10-11 21:39:37 +00:00
|
|
|
static struct sockaddr_in stunaddr; /*!< the stun server we get the externip from */
|
2010-11-02 15:14:12 +00:00
|
|
|
static int global_stunaddr;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
static struct gtalk_container gtalk_list;
|
|
|
|
|
|
|
|
static void gtalk_member_destroy(struct gtalk *obj)
|
|
|
|
{
|
2011-02-03 16:22:10 +00:00
|
|
|
obj->cap = ast_format_cap_destroy(obj->cap);
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(obj);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct gtalk *find_gtalk(char *name, char *connection)
|
|
|
|
{
|
|
|
|
struct gtalk *gtalk = NULL;
|
|
|
|
char *domain = NULL , *s = NULL;
|
2007-02-21 20:30:54 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
if (strchr(connection, '@')) {
|
2007-02-20 16:56:58 +00:00
|
|
|
s = ast_strdupa(connection);
|
2006-09-18 16:36:14 +00:00
|
|
|
domain = strsep(&s, "@");
|
|
|
|
ast_verbose("OOOOH domain = %s\n", domain);
|
|
|
|
}
|
|
|
|
gtalk = ASTOBJ_CONTAINER_FIND(>alk_list, name);
|
|
|
|
if (!gtalk && strchr(name, '@'))
|
|
|
|
gtalk = ASTOBJ_CONTAINER_FIND_FULL(>alk_list, name, user,,, strcasecmp);
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
if (!gtalk) {
|
2008-06-02 14:35:24 +00:00
|
|
|
/* guest call */
|
2006-09-18 16:36:14 +00:00
|
|
|
ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
|
2007-02-21 20:30:54 +00:00
|
|
|
ASTOBJ_RDLOCK(iterator);
|
2006-09-18 16:36:14 +00:00
|
|
|
if (!strcasecmp(iterator->name, "guest")) {
|
2008-06-02 14:35:24 +00:00
|
|
|
gtalk = iterator;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
ASTOBJ_UNLOCK(iterator);
|
2007-02-21 20:30:54 +00:00
|
|
|
|
|
|
|
if (gtalk)
|
|
|
|
break;
|
2006-09-18 16:36:14 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
return gtalk;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-03 16:22:10 +00:00
|
|
|
static int add_codec_to_answer(const struct gtalk_pvt *p, struct ast_format *codec, iks *dcodecs)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
2008-04-04 17:32:46 +00:00
|
|
|
int res = 0;
|
2006-09-18 16:36:14 +00:00
|
|
|
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");
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
if(!payload_eg711u || !payload_pcmu) {
|
2008-02-29 14:15:03 +00:00
|
|
|
iks_delete(payload_pcmu);
|
|
|
|
iks_delete(payload_eg711u);
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_WARNING,"Failed to allocate iks node");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
iks_insert_attrib(payload_pcmu, "id", "0");
|
|
|
|
iks_insert_attrib(payload_pcmu, "name", "PCMU");
|
|
|
|
iks_insert_attrib(payload_pcmu, "clockrate","8000");
|
|
|
|
iks_insert_attrib(payload_pcmu, "bitrate","64000");
|
|
|
|
iks_insert_attrib(payload_eg711u, "id", "100");
|
|
|
|
iks_insert_attrib(payload_eg711u, "name", "EG711U");
|
|
|
|
iks_insert_attrib(payload_eg711u, "clockrate","8000");
|
|
|
|
iks_insert_attrib(payload_eg711u, "bitrate","64000");
|
|
|
|
iks_insert_node(dcodecs, payload_pcmu);
|
|
|
|
iks_insert_node(dcodecs, payload_eg711u);
|
2008-04-04 17:32:46 +00:00
|
|
|
res ++;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
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) {
|
2008-02-29 14:15:03 +00:00
|
|
|
iks_delete(payload_eg711a);
|
|
|
|
iks_delete(payload_pcma);
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_WARNING,"Failed to allocate iks node");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
iks_insert_attrib(payload_pcma, "id", "8");
|
|
|
|
iks_insert_attrib(payload_pcma, "name", "PCMA");
|
|
|
|
iks_insert_attrib(payload_pcma, "clockrate","8000");
|
|
|
|
iks_insert_attrib(payload_pcma, "bitrate","64000");
|
|
|
|
payload_eg711a = iks_new("payload-type");
|
|
|
|
iks_insert_attrib(payload_eg711a, "id", "101");
|
|
|
|
iks_insert_attrib(payload_eg711a, "name", "EG711A");
|
|
|
|
iks_insert_attrib(payload_eg711a, "clockrate","8000");
|
|
|
|
iks_insert_attrib(payload_eg711a, "bitrate","64000");
|
|
|
|
iks_insert_node(dcodecs, payload_pcma);
|
|
|
|
iks_insert_node(dcodecs, payload_eg711a);
|
2008-04-04 17:32:46 +00:00
|
|
|
res ++;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
if (!strcasecmp("ilbc", format)) {
|
|
|
|
iks *payload_ilbc = iks_new("payload-type");
|
|
|
|
if(!payload_ilbc) {
|
|
|
|
ast_log(LOG_WARNING,"Failed to allocate iks node");
|
|
|
|
return -1;
|
|
|
|
}
|
2007-06-27 22:58:53 +00:00
|
|
|
iks_insert_attrib(payload_ilbc, "id", "97");
|
2006-09-18 16:36:14 +00:00
|
|
|
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);
|
2008-04-04 17:32:46 +00:00
|
|
|
res ++;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
if (!strcasecmp("g723", format)) {
|
|
|
|
iks *payload_g723 = iks_new("payload-type");
|
|
|
|
if(!payload_g723) {
|
|
|
|
ast_log(LOG_WARNING,"Failed to allocate iks node");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
iks_insert_attrib(payload_g723, "id", "4");
|
|
|
|
iks_insert_attrib(payload_g723, "name", "G723");
|
|
|
|
iks_insert_attrib(payload_g723, "clockrate","8000");
|
|
|
|
iks_insert_attrib(payload_g723, "bitrate","6300");
|
|
|
|
iks_insert_node(dcodecs, payload_g723);
|
2008-04-04 17:32:46 +00:00
|
|
|
res ++;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
if (!strcasecmp("speex", format)) {
|
|
|
|
iks *payload_speex = iks_new("payload-type");
|
|
|
|
if(!payload_speex) {
|
|
|
|
ast_log(LOG_WARNING,"Failed to allocate iks node");
|
|
|
|
return -1;
|
|
|
|
}
|
2007-06-27 22:58:53 +00:00
|
|
|
iks_insert_attrib(payload_speex, "id", "110");
|
2006-09-18 16:36:14 +00:00
|
|
|
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);
|
2008-04-04 17:32:46 +00:00
|
|
|
res++;
|
|
|
|
}
|
|
|
|
if (!strcasecmp("gsm", format)) {
|
|
|
|
iks *payload_gsm = iks_new("payload-type");
|
|
|
|
if(!payload_gsm) {
|
|
|
|
ast_log(LOG_WARNING,"Failed to allocate iks node");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
iks_insert_attrib(payload_gsm, "id", "103");
|
|
|
|
iks_insert_attrib(payload_gsm, "name", "gsm");
|
|
|
|
iks_insert_node(dcodecs, payload_gsm);
|
|
|
|
res++;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
2009-04-02 17:20:52 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
return res;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_invite(struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator)
|
|
|
|
{
|
|
|
|
struct gtalk *client = p->parent;
|
|
|
|
iks *iq, *gtalk, *dcodecs, *payload_telephone, *transport;
|
|
|
|
int x;
|
2011-02-03 16:22:10 +00:00
|
|
|
struct ast_format_cap *alreadysent;
|
2008-04-04 17:32:46 +00:00
|
|
|
int codecs_num = 0;
|
2009-02-12 10:31:36 +00:00
|
|
|
char *lowerto = NULL;
|
2011-02-03 16:22:10 +00:00
|
|
|
struct ast_format tmpfmt;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
iq = iks_new("iq");
|
|
|
|
gtalk = iks_new("session");
|
|
|
|
dcodecs = iks_new("description");
|
|
|
|
transport = iks_new("transport");
|
|
|
|
payload_telephone = iks_new("payload-type");
|
2011-02-03 16:22:10 +00:00
|
|
|
if (!(iq && gtalk && dcodecs && transport && payload_telephone)) {
|
2008-02-29 14:15:03 +00:00
|
|
|
iks_delete(iq);
|
|
|
|
iks_delete(gtalk);
|
|
|
|
iks_delete(dcodecs);
|
|
|
|
iks_delete(transport);
|
|
|
|
iks_delete(payload_telephone);
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_ERROR, "Could not allocate iksemel nodes\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
iks_insert_attrib(dcodecs, "xmlns", GOOGLE_AUDIO_NS);
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_attrib(dcodecs, "xml:lang", "en");
|
|
|
|
|
2011-02-03 16:22:10 +00:00
|
|
|
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))) {
|
2006-09-18 16:36:14 +00:00
|
|
|
break;
|
2011-02-03 16:22:10 +00:00
|
|
|
}
|
|
|
|
if (!(ast_format_cap_iscompatible(client->cap, &tmpfmt))) {
|
2006-09-18 16:36:14 +00:00
|
|
|
continue;
|
2011-02-03 16:22:10 +00:00
|
|
|
}
|
|
|
|
if (ast_format_cap_iscompatible(alreadysent, &tmpfmt)) {
|
2006-09-18 16:36:14 +00:00
|
|
|
continue;
|
2011-02-03 16:22:10 +00:00
|
|
|
}
|
|
|
|
codecs_num = add_codec_to_answer(p, &tmpfmt, dcodecs);
|
|
|
|
ast_format_cap_add(alreadysent, &tmpfmt);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
2011-02-03 16:22:10 +00:00
|
|
|
alreadysent = ast_format_cap_destroy(alreadysent);
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
if (codecs_num) {
|
|
|
|
/* only propose DTMF within an audio session */
|
2010-10-06 21:09:14 +00:00
|
|
|
iks_insert_attrib(payload_telephone, "id", "101");
|
2008-04-04 17:32:46 +00:00
|
|
|
iks_insert_attrib(payload_telephone, "name", "telephone-event");
|
|
|
|
iks_insert_attrib(payload_telephone, "clockrate", "8000");
|
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
iks_insert_attrib(transport,"xmlns",GOOGLE_TRANSPORT_NS);
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
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);
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept");
|
2010-10-05 22:01:52 +00:00
|
|
|
/* put the initiator attribute to lower case if we receive the call
|
2009-02-12 10:31:36 +00:00
|
|
|
* 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);
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_attrib(gtalk, "id", sid);
|
|
|
|
iks_insert_node(iq, gtalk);
|
|
|
|
iks_insert_node(gtalk, dcodecs);
|
|
|
|
iks_insert_node(dcodecs, payload_telephone);
|
|
|
|
|
2007-11-06 18:44:19 +00:00
|
|
|
ast_aji_send(client->connection, iq);
|
2008-02-29 14:15:03 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_delete(payload_telephone);
|
|
|
|
iks_delete(transport);
|
|
|
|
iks_delete(dcodecs);
|
|
|
|
iks_delete(gtalk);
|
|
|
|
iks_delete(iq);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_ringing_ack(void *data, ikspak *pak)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = data;
|
2010-12-09 21:33:22 +00:00
|
|
|
struct ast_channel *owner;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2010-12-09 21:33:22 +00:00
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
|
|
|
|
if (p->ringrule) {
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
|
2010-12-09 21:33:22 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
p->ringrule = NULL;
|
2010-12-09 21:33:22 +00:00
|
|
|
|
|
|
|
/* this may be a redirect */
|
|
|
|
if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
|
|
|
|
char *name = NULL;
|
|
|
|
char *redirect = NULL;
|
|
|
|
iks *traversenodes = NULL;
|
|
|
|
traversenodes = pak->query;
|
|
|
|
while (traversenodes) {
|
|
|
|
if (!(name = iks_name(traversenodes))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(name, "error") &&
|
|
|
|
(redirect = iks_find_cdata(traversenodes, "redirect")) &&
|
|
|
|
(redirect = strstr(redirect, "xmpp:"))) {
|
|
|
|
redirect += 5;
|
2011-02-04 16:55:39 +00:00
|
|
|
ast_debug(1, "redirect %s\n", redirect);
|
2010-12-09 21:33:22 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
return IKS_FILTER_EAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_answer(struct ast_channel *ast)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = ast->tech_pvt;
|
|
|
|
int res = 0;
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "Answer!\n");
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
gtalk_invite(p, p->them, p->us,p->sid, 0);
|
2010-10-05 22:01:52 +00:00
|
|
|
manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
|
2007-11-27 19:50:12 +00:00
|
|
|
ast->name, "GTALK", p->sid);
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2009-04-02 17:20:52 +00:00
|
|
|
static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = chan->tech_pvt;
|
2009-04-02 17:20:52 +00:00
|
|
|
enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
if (!p)
|
|
|
|
return res;
|
|
|
|
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
if (p->rtp){
|
2009-04-02 17:20:52 +00:00
|
|
|
ao2_ref(p->rtp, +1);
|
|
|
|
*instance = p->rtp;
|
|
|
|
res = AST_RTP_GLUE_RESULT_LOCAL;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2011-02-03 16:22:10 +00:00
|
|
|
static void gtalk_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = chan->tech_pvt;
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
ast_format_cap_copy(result, p->peercap);
|
|
|
|
ast_mutex_unlock(&p->lock);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 16:22:10 +00:00
|
|
|
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)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
|
|
|
struct gtalk_pvt *p;
|
|
|
|
|
|
|
|
p = chan->tech_pvt;
|
|
|
|
if (!p)
|
|
|
|
return -1;
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
|
|
|
|
/* if (rtp)
|
|
|
|
ast_rtp_get_peer(rtp, &p->redirip);
|
|
|
|
else
|
|
|
|
memset(&p->redirip, 0, sizeof(p->redirip));
|
|
|
|
p->redircodecs = codecs; */
|
|
|
|
|
|
|
|
/* Reset lastrtprx timer */
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-04-02 17:20:52 +00:00
|
|
|
static struct ast_rtp_glue gtalk_rtp_glue = {
|
|
|
|
.type = "Gtalk",
|
|
|
|
.get_rtp_info = gtalk_get_rtp_peer,
|
|
|
|
.get_codec = gtalk_get_codec,
|
|
|
|
.update_peer = gtalk_set_rtp_peer,
|
|
|
|
};
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2)
|
|
|
|
{
|
|
|
|
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);
|
2010-10-14 21:29:04 +00:00
|
|
|
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"), ""));
|
2006-09-18 16:36:14 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2007-11-06 18:44:19 +00:00
|
|
|
ast_aji_send(client->connection, response);
|
2006-09-18 16:36:14 +00:00
|
|
|
res = 0;
|
|
|
|
}
|
2008-02-29 14:15:03 +00:00
|
|
|
|
|
|
|
iks_delete(reason);
|
|
|
|
iks_delete(error);
|
|
|
|
iks_delete(response);
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_is_answered(struct gtalk *client, ikspak *pak)
|
|
|
|
{
|
2010-10-05 22:01:52 +00:00
|
|
|
struct gtalk_pvt *tmp = NULL;
|
2006-09-18 16:36:14 +00:00
|
|
|
char *from;
|
2008-04-04 17:32:46 +00:00
|
|
|
iks *codec;
|
|
|
|
char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
|
|
|
|
int peernoncodeccapability;
|
|
|
|
|
2011-02-04 16:55:39 +00:00
|
|
|
ast_debug(1, "The client is %s\n", client->name);
|
2010-10-05 22:01:52 +00:00
|
|
|
|
|
|
|
/* Make sure our new call does exist */
|
2006-09-18 16:36:14 +00:00
|
|
|
for (tmp = client->p; tmp; tmp = tmp->next) {
|
2010-10-05 22:01:52 +00:00
|
|
|
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)) {
|
2006-09-18 16:36:14 +00:00
|
|
|
break;
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!tmp) {
|
2010-10-05 22:01:52 +00:00
|
|
|
ast_log(LOG_WARNING, "Could not find session in iq\n");
|
|
|
|
return -1;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
2008-04-04 17:32:46 +00:00
|
|
|
|
|
|
|
/* codec points to the first <payload-type/> tag */
|
Merged revisions 185362 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185362 | dbrooks | 2009-03-31 11:37:12 -0500 (Tue, 31 Mar 2009) | 35 lines
Fix incorrect parsing in chan_gtalk when xmpp contains extra whitespaces
To drill into the xmpp to find the capabilities between channels, chan_gtalk
calls iks_child() and iks_next(). iks_child() and iks_next() are functions in
the iksemel xml parsing library that traverse xml nodes. The bug here is that
both iks_child() and iks_next() will return the next iks_struct node
*regardless* of type. chan_gtalk expects the next node to be of type IKS_TAG,
which in most cases, it is, but in this case (a call being made from the
Empathy IM client), there exists iks_struct nodes which are not IKS_TAG data
(they are extraneous whitespaces), and chan_gtalk doesn't handle that case,
so capabilities don't match, and a call cannot be made.
iks_first_tag() and iks_next_tag(), on the other hand, will not return the
very next iks_struct, but will check to see if the next iks_struct is of
type IKS_TAG. If it isn't, it will be skipped, and the next struct of type
IKS_TAG it finds will be returned. This assures that chan_gtalk will find
the iks_struct it is looking for.
This fix simply changes all calls to iks_child() and iks_next() to become
calls to iks_first_tag() and iks_next_tag(), which resolves the capability
matching.
The following is a payload listing from Empathy, which, due to the extraneous
whitespace, will not be parsed correctly by iksemel:
<iq from='dbrooksjab@235-22-24-10/Telepathy' to='astjab@235-22-24-10/asterisk' type='set' id='542757715704'> <session xmlns='http://www.google.com/session' initiator='dbrooksjab@235-22-24-10/Telepathy' type='initiate' id='1837267342'> <description xmlns='http://www.google.com/session/phone'> <payload-type clockrate='16000' name='speex' id='96'/>
<payload-type clockrate='8000' name='PCMA' id='8'/>
<payload-type clockrate='8000' name='PCMU' id='0'/>
<payload-type clockrate='90000' name='MPA' id='97'/>
<payload-type clockrate='16000' name='SIREN' id='98'/>
<payload-type clockrate='8000' name='telephone-event' id='99'/>
</description>
</session>
</iq>
Review: http://reviewboard.digium.com/r/181/
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@185363 f38db490-d61c-443f-a65b-d21fe96a405b
2009-03-31 16:46:57 +00:00
|
|
|
codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
|
2008-04-04 17:32:46 +00:00
|
|
|
while (codec) {
|
2010-10-14 21:29:04 +00:00
|
|
|
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);
|
Merged revisions 185362 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185362 | dbrooks | 2009-03-31 11:37:12 -0500 (Tue, 31 Mar 2009) | 35 lines
Fix incorrect parsing in chan_gtalk when xmpp contains extra whitespaces
To drill into the xmpp to find the capabilities between channels, chan_gtalk
calls iks_child() and iks_next(). iks_child() and iks_next() are functions in
the iksemel xml parsing library that traverse xml nodes. The bug here is that
both iks_child() and iks_next() will return the next iks_struct node
*regardless* of type. chan_gtalk expects the next node to be of type IKS_TAG,
which in most cases, it is, but in this case (a call being made from the
Empathy IM client), there exists iks_struct nodes which are not IKS_TAG data
(they are extraneous whitespaces), and chan_gtalk doesn't handle that case,
so capabilities don't match, and a call cannot be made.
iks_first_tag() and iks_next_tag(), on the other hand, will not return the
very next iks_struct, but will check to see if the next iks_struct is of
type IKS_TAG. If it isn't, it will be skipped, and the next struct of type
IKS_TAG it finds will be returned. This assures that chan_gtalk will find
the iks_struct it is looking for.
This fix simply changes all calls to iks_child() and iks_next() to become
calls to iks_first_tag() and iks_next_tag(), which resolves the capability
matching.
The following is a payload listing from Empathy, which, due to the extraneous
whitespace, will not be parsed correctly by iksemel:
<iq from='dbrooksjab@235-22-24-10/Telepathy' to='astjab@235-22-24-10/asterisk' type='set' id='542757715704'> <session xmlns='http://www.google.com/session' initiator='dbrooksjab@235-22-24-10/Telepathy' type='initiate' id='1837267342'> <description xmlns='http://www.google.com/session/phone'> <payload-type clockrate='16000' name='speex' id='96'/>
<payload-type clockrate='8000' name='PCMA' id='8'/>
<payload-type clockrate='8000' name='PCMU' id='0'/>
<payload-type clockrate='90000' name='MPA' id='97'/>
<payload-type clockrate='16000' name='SIREN' id='98'/>
<payload-type clockrate='8000' name='telephone-event' id='99'/>
</description>
</session>
</iq>
Review: http://reviewboard.digium.com/r/181/
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@185363 f38db490-d61c-443f-a65b-d21fe96a405b
2009-03-31 16:46:57 +00:00
|
|
|
codec = iks_next_tag(codec);
|
2008-04-04 17:32:46 +00:00
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
/* Now gather all of the codecs that we are asked for */
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), tmp->peercap, &peernoncodeccapability);
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2011-02-03 16:22:10 +00:00
|
|
|
/* at this point, we received an answer from the remote Gtalk client,
|
2008-04-04 17:32:46 +00:00
|
|
|
which allows us to compare capabilities */
|
2011-02-03 16:22:10 +00:00
|
|
|
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));
|
2008-04-04 17:32:46 +00:00
|
|
|
/* close session if capabilities don't match */
|
2008-05-22 16:29:54 +00:00
|
|
|
ast_queue_hangup(tmp->owner);
|
2008-04-04 17:32:46 +00:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
from = iks_find_attrib(pak->x, "to");
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!from) {
|
2006-09-18 16:36:14 +00:00
|
|
|
from = client->connection->jid->full;
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
if (tmp->owner) {
|
|
|
|
ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
|
|
|
|
}
|
|
|
|
gtalk_update_stun(tmp->parent, tmp);
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_response(client, from, pak, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-08-24 11:49:36 +00:00
|
|
|
static int gtalk_is_accepted(struct gtalk *client, ikspak *pak)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *tmp;
|
|
|
|
char *from;
|
|
|
|
|
2011-02-04 16:55:39 +00:00
|
|
|
ast_debug(1, "The client is %s\n", client->name);
|
2007-08-24 11:49:36 +00:00
|
|
|
/* find corresponding call */
|
|
|
|
for (tmp = client->p; tmp; tmp = tmp->next) {
|
2010-10-11 21:39:37 +00:00
|
|
|
if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
|
2007-08-24 11:49:36 +00:00
|
|
|
break;
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2007-08-24 11:49:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
from = iks_find_attrib(pak->x, "to");
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!from) {
|
2007-08-24 11:49:36 +00:00
|
|
|
from = client->connection->jid->full;
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2007-08-24 11:49:36 +00:00
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
if (tmp) {
|
|
|
|
gtalk_update_stun(tmp->parent, tmp);
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_NOTICE, "Whoa, didn't find call during accept?!\n");
|
|
|
|
}
|
2007-08-24 11:49:36 +00:00
|
|
|
|
|
|
|
/* answer 'iq' packet to let the remote peer know that we're alive */
|
|
|
|
gtalk_response(client, from, pak, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_handle_dtmf(struct gtalk *client, ikspak *pak)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *tmp;
|
2007-09-13 11:54:56 +00:00
|
|
|
iks *dtmfnode = NULL, *dtmfchild = NULL;
|
2006-09-18 16:36:14 +00:00
|
|
|
char *dtmf;
|
|
|
|
char *from;
|
|
|
|
/* Make sure our new call doesn't exist yet */
|
|
|
|
for (tmp = client->p; tmp; tmp = tmp->next) {
|
2007-09-13 11:54:56 +00:00
|
|
|
if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid))
|
2006-09-18 16:36:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
from = iks_find_attrib(pak->x, "to");
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!from) {
|
2006-09-18 16:36:14 +00:00
|
|
|
from = client->connection->jid->full;
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
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, };
|
2009-11-04 14:05:12 +00:00
|
|
|
f.subclass.integer = dtmf[0];
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_queue_frame(tmp->owner, &f);
|
2009-11-04 14:05:12 +00:00
|
|
|
ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
|
2006-09-18 16:36:14 +00:00
|
|
|
} else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
|
|
|
|
struct ast_frame f = {AST_FRAME_DTMF_END, };
|
2009-11-04 14:05:12 +00:00
|
|
|
f.subclass.integer = dtmf[0];
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_queue_frame(tmp->owner, &f);
|
2009-11-04 14:05:12 +00:00
|
|
|
ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
|
2006-09-18 16:36:14 +00:00
|
|
|
} else if(iks_find_attrib(pak->x, "dtmf")) { /* 250 millasecond default */
|
|
|
|
struct ast_frame f = {AST_FRAME_DTMF, };
|
2009-11-04 14:05:12 +00:00
|
|
|
f.subclass.integer = dtmf[0];
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_queue_frame(tmp->owner, &f);
|
2009-11-04 14:05:12 +00:00
|
|
|
ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
}
|
2007-09-13 11:54:56 +00:00
|
|
|
} 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, };
|
2009-11-04 14:05:12 +00:00
|
|
|
f.subclass.integer = dtmf[0];
|
2007-09-13 11:54:56 +00:00
|
|
|
ast_queue_frame(tmp->owner, &f);
|
2009-11-04 14:05:12 +00:00
|
|
|
ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
|
2007-09-13 11:54:56 +00:00
|
|
|
} else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
|
|
|
|
struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
|
2009-11-04 14:05:12 +00:00
|
|
|
f.subclass.integer = dtmf[0];
|
2007-09-13 11:54:56 +00:00
|
|
|
ast_queue_frame(tmp->owner, &f);
|
2009-11-04 14:05:12 +00:00
|
|
|
ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
|
2007-09-13 11:54:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
gtalk_response(client, from, pak, NULL, NULL);
|
|
|
|
return 1;
|
2010-10-11 21:39:37 +00:00
|
|
|
} else {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
gtalk_response(client, from, pak, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *tmp;
|
|
|
|
char *from;
|
|
|
|
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "The client is %s\n", client->name);
|
2006-09-18 16:36:14 +00:00
|
|
|
/* Make sure our new call doesn't exist yet */
|
|
|
|
for (tmp = client->p; tmp; tmp = tmp->next) {
|
2010-10-13 22:47:35 +00:00
|
|
|
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))) {
|
2006-09-18 16:36:14 +00:00
|
|
|
break;
|
2010-10-13 22:47:35 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
from = iks_find_attrib(pak->x, "to");
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!from) {
|
2006-09-18 16:36:14 +00:00
|
|
|
from = client->connection->jid->full;
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
if (tmp) {
|
|
|
|
tmp->alreadygone = 1;
|
2010-10-11 21:39:37 +00:00
|
|
|
if (tmp->owner) {
|
2008-05-22 16:29:54 +00:00
|
|
|
ast_queue_hangup(tmp->owner);
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_NOTICE, "Whoa, didn't find call during hangup!\n");
|
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_response(client, from, pak, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-10-14 21:29:04 +00:00
|
|
|
static int gtalk_get_local_ip(struct ast_sockaddr *ourip)
|
|
|
|
{
|
|
|
|
struct ast_sockaddr root;
|
|
|
|
struct ast_sockaddr bindaddr_tmp;
|
|
|
|
struct ast_sockaddr *addrs;
|
|
|
|
int addrs_cnt;
|
|
|
|
|
|
|
|
/* If bind address is not 0.0.0.0, then bindaddr is our local ip. */
|
|
|
|
ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
|
|
|
|
if (!ast_sockaddr_is_any(&bindaddr_tmp)) {
|
|
|
|
ast_sockaddr_copy(ourip, &bindaddr_tmp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If no bind address was provided, lets see what ip we would use to connect to google.com and use that.
|
|
|
|
* If you can't resolve google.com from your network, then this module is useless for you anyway. */
|
|
|
|
if ((addrs_cnt = ast_sockaddr_resolve(&addrs, "google.com", PARSE_PORT_FORBID, AF_INET)) > 0) {
|
|
|
|
ast_sockaddr_copy(&root, &addrs[0]);
|
|
|
|
ast_free(addrs);
|
|
|
|
if (!ast_ouraddrfor(&root, ourip)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* As a last resort, use this function to find our local address. */
|
|
|
|
return ast_find_ourip(ourip, &bindaddr_tmp, AF_INET);
|
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to)
|
|
|
|
{
|
|
|
|
struct gtalk_candidate *tmp;
|
|
|
|
struct aji_client *c = client->connection;
|
|
|
|
struct gtalk_candidate *ours1 = NULL, *ours2 = NULL;
|
2009-04-10 18:02:44 +00:00
|
|
|
struct sockaddr_in sin = { 0, };
|
2010-07-08 22:08:07 +00:00
|
|
|
struct ast_sockaddr sin_tmp;
|
|
|
|
struct ast_sockaddr us;
|
2006-09-18 16:36:14 +00:00
|
|
|
iks *iq, *gtalk, *candidate, *transport;
|
|
|
|
char user[17], pass[17], preference[5], port[7];
|
2009-02-12 10:31:36 +00:00
|
|
|
char *lowerfrom = NULL;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
iks_insert_attrib(transport, "xmlns",GOOGLE_TRANSPORT_NS);
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_node(iq, gtalk);
|
2010-10-14 21:29:04 +00:00
|
|
|
iks_insert_node(gtalk,candidate);
|
|
|
|
iks_insert_node(gtalk,transport);
|
2010-10-11 21:39:37 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-07-08 22:08:07 +00:00
|
|
|
ast_rtp_instance_get_local_address(p->rtp, &sin_tmp);
|
|
|
|
ast_sockaddr_to_sin(&sin_tmp, &sin);
|
2010-10-14 21:29:04 +00:00
|
|
|
|
|
|
|
gtalk_get_local_ip(&us);
|
|
|
|
|
2010-07-08 22:08:07 +00:00
|
|
|
if (!strcmp(ast_sockaddr_stringify_addr(&us), "127.0.0.1")) {
|
2009-02-12 14:25:03 +00:00
|
|
|
ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute.");
|
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
/* 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));
|
2010-07-08 22:08:07 +00:00
|
|
|
ast_copy_string(ours1->ip, ast_sockaddr_stringify_addr(&us),
|
|
|
|
sizeof(ours1->ip));
|
2006-09-18 16:36:14 +00:00
|
|
|
ours1->protocol = AJI_PROTOCOL_UDP;
|
|
|
|
ours1->type = AJI_CONNECT_LOCAL;
|
|
|
|
ours1->generation = 0;
|
|
|
|
p->ourcandidates = ours1;
|
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
/* 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();
|
2006-09-18 16:36:14 +00:00
|
|
|
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);
|
2010-10-14 21:29:04 +00:00
|
|
|
iks_insert_attrib(gtalk, "type", "candidates");
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_attrib(gtalk, "id", sid);
|
2010-10-05 22:01:52 +00:00
|
|
|
/* put the initiator attribute to lower case if we receive the call
|
2009-02-12 10:31:36 +00:00
|
|
|
* otherwise GoogleTalk won't establish the session */
|
|
|
|
if (!p->initiator) {
|
|
|
|
char c;
|
|
|
|
char *t = lowerfrom = ast_strdupa(from);
|
|
|
|
while (((c = *t) != '/') && (*t++ = tolower(c)));
|
|
|
|
}
|
|
|
|
iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : lowerfrom);
|
2006-09-18 16:36:14 +00:00
|
|
|
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");
|
2007-11-06 18:44:19 +00:00
|
|
|
ast_aji_send(c, iq);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
p->laststun = 0;
|
|
|
|
|
|
|
|
safeout:
|
|
|
|
if (ours1)
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(ours1);
|
2006-09-18 16:36:14 +00:00
|
|
|
if (ours2)
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(ours2);
|
2008-02-29 14:15:03 +00:00
|
|
|
iks_delete(iq);
|
|
|
|
iks_delete(gtalk);
|
|
|
|
iks_delete(candidate);
|
|
|
|
iks_delete(transport);
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *tmp = NULL;
|
|
|
|
struct aji_resource *resources = NULL;
|
|
|
|
struct aji_buddy *buddy;
|
|
|
|
char idroster[200];
|
|
|
|
char *data, *exten = NULL;
|
2010-07-08 22:08:07 +00:00
|
|
|
struct ast_sockaddr bindaddr_tmp;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "The client is %s for alloc\n", client->name);
|
2006-09-18 16:36:14 +00:00
|
|
|
if (!sid && !strchr(them, '/')) { /* I started call! */
|
|
|
|
if (!strcasecmp(client->name, "guest")) {
|
|
|
|
buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them);
|
2010-10-05 22:01:52 +00:00
|
|
|
if (buddy) {
|
2006-09-18 16:36:14 +00:00
|
|
|
resources = buddy->resources;
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
|
|
|
} else if (client->buddy) {
|
2006-09-18 16:36:14 +00:00
|
|
|
resources = client->buddy->resources;
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
while (resources) {
|
|
|
|
if (resources->cap->jingle) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
resources = resources->next;
|
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
if (resources) {
|
2006-09-18 16:36:14 +00:00
|
|
|
snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource);
|
2010-10-08 20:45:49 +00:00
|
|
|
} else if ((*them == '+') || (strstr(them, "@voice.google.com"))) {
|
2010-12-09 21:33:22 +00:00
|
|
|
snprintf(idroster, sizeof(idroster), "%s", them);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-02-03 16:22:10 +00:00
|
|
|
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;
|
|
|
|
}
|
2007-04-03 22:33:03 +00:00
|
|
|
|
|
|
|
memcpy(&tmp->prefs, &client->prefs, sizeof(struct ast_codec_pref));
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-04-04 17:32:46 +00:00
|
|
|
/* clear codecs */
|
2010-10-05 22:01:52 +00:00
|
|
|
bindaddr.sin_family = AF_INET;
|
2010-07-08 23:46:20 +00:00
|
|
|
ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
|
2010-07-08 22:08:07 +00:00
|
|
|
if (!(tmp->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL))) {
|
2009-05-22 20:01:11 +00:00
|
|
|
ast_log(LOG_ERROR, "Failed to create a new RTP instance (possibly an invalid bindaddr?)\n");
|
|
|
|
ast_free(tmp);
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-04-02 17:20:52 +00:00
|
|
|
ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1);
|
2010-10-13 22:47:35 +00:00
|
|
|
ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_STUN, 1);
|
2010-10-06 21:09:14 +00:00
|
|
|
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);
|
2009-04-02 17:20:52 +00:00
|
|
|
ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp);
|
2008-04-04 17:32:46 +00:00
|
|
|
|
|
|
|
/* add user configured codec capabilites */
|
2011-02-03 16:22:10 +00:00
|
|
|
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);
|
2010-10-14 21:29:04 +00:00
|
|
|
}
|
2008-04-04 17:32:46 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
tmp->parent = client;
|
|
|
|
if (!tmp->rtp) {
|
|
|
|
ast_log(LOG_WARNING, "Out of RTP sessions?\n");
|
2007-06-03 06:10:27 +00:00
|
|
|
ast_free(tmp);
|
2006-09-18 16:36:14 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-01-09 16:59:09 +00:00
|
|
|
/* Set CALLERID(name) to the full JID of the remote peer */
|
|
|
|
ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name));
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
if(strchr(tmp->us, '/')) {
|
2007-02-20 16:56:58 +00:00
|
|
|
data = ast_strdupa(tmp->us);
|
2006-09-18 16:36:14 +00:00
|
|
|
exten = strsep(&data, "/");
|
2010-10-14 21:29:04 +00:00
|
|
|
} else {
|
2006-10-03 22:36:51 +00:00
|
|
|
exten = tmp->us;
|
2010-10-14 21:29:04 +00:00
|
|
|
}
|
2006-10-03 22:36:51 +00:00
|
|
|
ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_mutex_init(&tmp->lock);
|
|
|
|
ast_mutex_lock(>alklock);
|
|
|
|
tmp->next = client->p;
|
|
|
|
client->p = tmp;
|
|
|
|
ast_mutex_unlock(>alklock);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Start new gtalk channel */
|
2009-06-26 15:28:53 +00:00
|
|
|
static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
|
|
|
struct ast_channel *tmp;
|
2006-11-08 07:21:45 +00:00
|
|
|
const char *n2;
|
2011-02-03 16:22:10 +00:00
|
|
|
struct ast_format_cap *what; /* used as SHALLOW COPY DO NOT DESTROY */
|
|
|
|
struct ast_format tmpfmt;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2006-11-07 21:47:49 +00:00
|
|
|
if (title)
|
|
|
|
n2 = title;
|
|
|
|
else
|
|
|
|
n2 = i->us;
|
2009-06-26 15:28:53 +00:00
|
|
|
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);
|
2006-09-18 16:36:14 +00:00
|
|
|
if (!tmp) {
|
|
|
|
ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
tmp->tech = >alk_tech;
|
|
|
|
|
|
|
|
/* Select our native format based on codec preference until we receive
|
|
|
|
something from another device to the contrary. */
|
2011-02-03 16:22:10 +00:00
|
|
|
if (!(ast_format_cap_is_empty(i->jointcap))) {
|
|
|
|
what = i->jointcap;
|
|
|
|
} else if (i->cap) {
|
|
|
|
what = i->cap;
|
|
|
|
} else {
|
2006-09-18 16:36:14 +00:00
|
|
|
what = global_capability;
|
2011-02-03 16:22:10 +00:00
|
|
|
}
|
2007-04-03 22:33:03 +00:00
|
|
|
|
|
|
|
/* Set Frame packetization */
|
2010-10-13 22:47:35 +00:00
|
|
|
if (i->rtp) {
|
2009-04-02 17:20:52 +00:00
|
|
|
ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
|
2010-10-13 22:47:35 +00:00
|
|
|
}
|
2007-04-03 22:33:03 +00:00
|
|
|
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_codec_choose(&i->prefs, what, 1, &tmpfmt);
|
|
|
|
ast_format_cap_add(tmp->nativeformats, &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(tmp->nativeformats, &tmpfmt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast_format_cap_iter_end(i->jointcap);
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
if (i->rtp) {
|
2009-04-02 17:20:52 +00:00
|
|
|
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));
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
if (i->vrtp) {
|
2009-04-02 17:20:52 +00:00
|
|
|
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));
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
if (state == AST_STATE_RING)
|
|
|
|
tmp->rings = 1;
|
|
|
|
tmp->adsicpe = AST_ADSI_UNAVAILABLE;
|
2011-02-03 16:22:10 +00:00
|
|
|
|
|
|
|
ast_best_codec(tmp->nativeformats, &tmpfmt);
|
|
|
|
ast_format_copy(&tmp->writeformat, &tmpfmt);
|
|
|
|
ast_format_copy(&tmp->rawwriteformat, &tmpfmt);
|
|
|
|
ast_format_copy(&tmp->readformat, &tmpfmt);
|
|
|
|
ast_format_copy(&tmp->rawreadformat, &tmpfmt);
|
2006-09-18 16:36:14 +00:00
|
|
|
tmp->tech_pvt = i;
|
|
|
|
|
|
|
|
tmp->callgroup = client->callgroup;
|
|
|
|
tmp->pickupgroup = client->pickupgroup;
|
2010-07-14 15:48:36 +00:00
|
|
|
tmp->caller.id.name.presentation = client->callingpres;
|
|
|
|
tmp->caller.id.number.presentation = client->callingpres;
|
2006-09-18 16:36:14 +00:00
|
|
|
if (!ast_strlen_zero(client->accountcode))
|
|
|
|
ast_string_field_set(tmp, accountcode, client->accountcode);
|
|
|
|
if (client->amaflags)
|
|
|
|
tmp->amaflags = client->amaflags;
|
|
|
|
if (!ast_strlen_zero(client->language))
|
|
|
|
ast_string_field_set(tmp, language, client->language);
|
|
|
|
if (!ast_strlen_zero(client->musicclass))
|
|
|
|
ast_string_field_set(tmp, musicclass, client->musicclass);
|
2008-04-21 23:42:45 +00:00
|
|
|
if (!ast_strlen_zero(client->parkinglot))
|
|
|
|
ast_string_field_set(tmp, parkinglot, client->parkinglot);
|
2006-09-18 16:36:14 +00:00
|
|
|
i->owner = tmp;
|
2007-01-23 22:59:55 +00:00
|
|
|
ast_module_ref(ast_module_info->self);
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
|
|
|
|
ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
|
2008-01-09 16:59:09 +00:00
|
|
|
|
2010-07-14 15:48:36 +00:00
|
|
|
if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
|
|
|
|
tmp->dialed.number.str = ast_strdup(i->exten);
|
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
tmp->priority = 1;
|
|
|
|
if (i->rtp)
|
|
|
|
ast_jb_configure(tmp, &global_jbconf);
|
|
|
|
if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
|
|
|
|
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
|
|
|
|
tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
|
|
|
|
ast_hangup(tmp);
|
|
|
|
tmp = NULL;
|
2007-11-27 19:50:12 +00:00
|
|
|
} else {
|
|
|
|
manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
|
|
|
|
"Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
|
|
|
|
i->owner ? i->owner->name : "", "Gtalk", i->sid);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
|
|
|
|
{
|
|
|
|
iks *request, *session = NULL;
|
|
|
|
int res = -1;
|
2009-02-12 10:31:36 +00:00
|
|
|
char *lowerthem = NULL;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
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);
|
2010-10-05 22:01:52 +00:00
|
|
|
/* put the initiator attribute to lower case if we receive the call
|
2009-02-12 10:31:36 +00:00
|
|
|
* 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);
|
2010-10-05 22:01:52 +00:00
|
|
|
iks_insert_attrib(session, "xmlns", GOOGLE_NS);
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_node(request, session);
|
2007-11-06 18:44:19 +00:00
|
|
|
ast_aji_send(client->connection, request);
|
2006-09-18 16:36:14 +00:00
|
|
|
res = 0;
|
|
|
|
}
|
|
|
|
}
|
2008-02-29 14:15:03 +00:00
|
|
|
|
|
|
|
iks_delete(session);
|
|
|
|
iks_delete(request);
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gtalk_free_candidates(struct gtalk_candidate *candidate)
|
|
|
|
{
|
|
|
|
struct gtalk_candidate *last;
|
|
|
|
while (candidate) {
|
|
|
|
last = candidate;
|
|
|
|
candidate = candidate->next;
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(last);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
|
|
|
|
{
|
|
|
|
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)
|
2009-04-02 17:20:52 +00:00
|
|
|
ast_rtp_instance_destroy(p->rtp);
|
2006-09-18 16:36:14 +00:00
|
|
|
if (p->vrtp)
|
2009-04-02 17:20:52 +00:00
|
|
|
ast_rtp_instance_destroy(p->vrtp);
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_free_candidates(p->theircandidates);
|
2011-02-03 16:22:10 +00:00
|
|
|
p->cap = ast_format_cap_destroy(p->cap);
|
|
|
|
p->jointcap = ast_format_cap_destroy(p->jointcap);
|
|
|
|
p->peercap = ast_format_cap_destroy(p->peercap);
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(p);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int gtalk_newcall(struct gtalk *client, ikspak *pak)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *p, *tmp = client->p;
|
|
|
|
struct ast_channel *chan;
|
|
|
|
int res;
|
|
|
|
iks *codec;
|
|
|
|
char *from = NULL;
|
2008-04-04 17:32:46 +00:00
|
|
|
char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
|
|
|
|
int peernoncodeccapability;
|
2010-10-13 22:47:35 +00:00
|
|
|
char *sid;
|
2008-04-04 17:32:46 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
/* Make sure our new call doesn't exist yet */
|
|
|
|
from = iks_find_attrib(pak->x,"to");
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!from) {
|
2006-09-18 16:36:14 +00:00
|
|
|
from = client->connection->jid->full;
|
2010-10-08 20:45:49 +00:00
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
while (tmp) {
|
2010-10-13 22:47:35 +00:00
|
|
|
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))) {
|
2006-09-18 16:36:14 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
if (!strcasecmp(client->name, "guest")){
|
|
|
|
/* the guest account is not tied to any configured XMPP client,
|
|
|
|
let's set it now */
|
|
|
|
client->connection = ast_aji_get_client(from);
|
|
|
|
if (!client->connection) {
|
|
|
|
ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2008-06-02 14:35:24 +00:00
|
|
|
|
2010-10-13 22:47:35 +00:00
|
|
|
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);
|
2006-09-18 16:36:14 +00:00
|
|
|
if (!p) {
|
|
|
|
ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2008-04-04 17:32:46 +00:00
|
|
|
|
2009-06-26 15:28:53 +00:00
|
|
|
chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
|
2008-04-04 17:32:46 +00:00
|
|
|
if (!chan) {
|
|
|
|
gtalk_free_pvt(client, p);
|
|
|
|
return -1;
|
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
ast_copy_string(p->them, pak->from->full, sizeof(p->them));
|
2010-10-13 22:47:35 +00:00
|
|
|
ast_copy_string(p->sid, sid, sizeof(p->sid));
|
2008-04-04 17:32:46 +00:00
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
/* codec points to the first <payload-type/> tag */
|
Merged revisions 185362 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185362 | dbrooks | 2009-03-31 11:37:12 -0500 (Tue, 31 Mar 2009) | 35 lines
Fix incorrect parsing in chan_gtalk when xmpp contains extra whitespaces
To drill into the xmpp to find the capabilities between channels, chan_gtalk
calls iks_child() and iks_next(). iks_child() and iks_next() are functions in
the iksemel xml parsing library that traverse xml nodes. The bug here is that
both iks_child() and iks_next() will return the next iks_struct node
*regardless* of type. chan_gtalk expects the next node to be of type IKS_TAG,
which in most cases, it is, but in this case (a call being made from the
Empathy IM client), there exists iks_struct nodes which are not IKS_TAG data
(they are extraneous whitespaces), and chan_gtalk doesn't handle that case,
so capabilities don't match, and a call cannot be made.
iks_first_tag() and iks_next_tag(), on the other hand, will not return the
very next iks_struct, but will check to see if the next iks_struct is of
type IKS_TAG. If it isn't, it will be skipped, and the next struct of type
IKS_TAG it finds will be returned. This assures that chan_gtalk will find
the iks_struct it is looking for.
This fix simply changes all calls to iks_child() and iks_next() to become
calls to iks_first_tag() and iks_next_tag(), which resolves the capability
matching.
The following is a payload listing from Empathy, which, due to the extraneous
whitespace, will not be parsed correctly by iksemel:
<iq from='dbrooksjab@235-22-24-10/Telepathy' to='astjab@235-22-24-10/asterisk' type='set' id='542757715704'> <session xmlns='http://www.google.com/session' initiator='dbrooksjab@235-22-24-10/Telepathy' type='initiate' id='1837267342'> <description xmlns='http://www.google.com/session/phone'> <payload-type clockrate='16000' name='speex' id='96'/>
<payload-type clockrate='8000' name='PCMA' id='8'/>
<payload-type clockrate='8000' name='PCMU' id='0'/>
<payload-type clockrate='90000' name='MPA' id='97'/>
<payload-type clockrate='16000' name='SIREN' id='98'/>
<payload-type clockrate='8000' name='telephone-event' id='99'/>
</description>
</session>
</iq>
Review: http://reviewboard.digium.com/r/181/
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@185363 f38db490-d61c-443f-a65b-d21fe96a405b
2009-03-31 16:46:57 +00:00
|
|
|
codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
while (codec) {
|
2010-10-13 22:47:35 +00:00
|
|
|
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;
|
|
|
|
}
|
2010-10-14 21:29:04 +00:00
|
|
|
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);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else {
|
2010-10-14 21:29:04 +00:00
|
|
|
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);
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
Merged revisions 185362 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185362 | dbrooks | 2009-03-31 11:37:12 -0500 (Tue, 31 Mar 2009) | 35 lines
Fix incorrect parsing in chan_gtalk when xmpp contains extra whitespaces
To drill into the xmpp to find the capabilities between channels, chan_gtalk
calls iks_child() and iks_next(). iks_child() and iks_next() are functions in
the iksemel xml parsing library that traverse xml nodes. The bug here is that
both iks_child() and iks_next() will return the next iks_struct node
*regardless* of type. chan_gtalk expects the next node to be of type IKS_TAG,
which in most cases, it is, but in this case (a call being made from the
Empathy IM client), there exists iks_struct nodes which are not IKS_TAG data
(they are extraneous whitespaces), and chan_gtalk doesn't handle that case,
so capabilities don't match, and a call cannot be made.
iks_first_tag() and iks_next_tag(), on the other hand, will not return the
very next iks_struct, but will check to see if the next iks_struct is of
type IKS_TAG. If it isn't, it will be skipped, and the next struct of type
IKS_TAG it finds will be returned. This assures that chan_gtalk will find
the iks_struct it is looking for.
This fix simply changes all calls to iks_child() and iks_next() to become
calls to iks_first_tag() and iks_next_tag(), which resolves the capability
matching.
The following is a payload listing from Empathy, which, due to the extraneous
whitespace, will not be parsed correctly by iksemel:
<iq from='dbrooksjab@235-22-24-10/Telepathy' to='astjab@235-22-24-10/asterisk' type='set' id='542757715704'> <session xmlns='http://www.google.com/session' initiator='dbrooksjab@235-22-24-10/Telepathy' type='initiate' id='1837267342'> <description xmlns='http://www.google.com/session/phone'> <payload-type clockrate='16000' name='speex' id='96'/>
<payload-type clockrate='8000' name='PCMA' id='8'/>
<payload-type clockrate='8000' name='PCMU' id='0'/>
<payload-type clockrate='90000' name='MPA' id='97'/>
<payload-type clockrate='16000' name='SIREN' id='98'/>
<payload-type clockrate='8000' name='telephone-event' id='99'/>
</description>
</session>
</iq>
Review: http://reviewboard.digium.com/r/181/
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@185363 f38db490-d61c-443f-a65b-d21fe96a405b
2009-03-31 16:46:57 +00:00
|
|
|
codec = iks_next_tag(codec);
|
2008-04-04 17:32:46 +00:00
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
/* Now gather all of the codecs that we are asked for */
|
2011-02-03 16:22:10 +00:00
|
|
|
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);
|
2008-04-04 17:32:46 +00:00
|
|
|
ast_mutex_unlock(&p->lock);
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
ast_setstate(chan, AST_STATE_RING);
|
2011-02-03 16:22:10 +00:00
|
|
|
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));
|
2008-04-04 19:28:49 +00:00
|
|
|
/* close session if capabilities don't match */
|
2008-04-04 17:32:46 +00:00
|
|
|
gtalk_action(client, p, "reject");
|
|
|
|
p->alreadygone = 1;
|
|
|
|
gtalk_hangup(chan);
|
Convert the ast_channel data structure over to the astobj2 framework.
There is a lot that could be said about this, but the patch is a big
improvement for performance, stability, code maintainability,
and ease of future code development.
The channel list is no longer an unsorted linked list. The main container
for channels is an astobj2 hash table. All of the code related to searching
for channels or iterating active channels has been rewritten. Let n be
the number of active channels. Iterating the channel list has gone from
O(n^2) to O(n). Searching for a channel by name went from O(n) to O(1).
Searching for a channel by extension is still O(n), but uses a new method
for doing so, which is more efficient.
The ast_channel object is now a reference counted object. The benefits
here are plentiful. Some benefits directly related to issues in the
previous code include:
1) When threads other than the channel thread owning a channel wanted
access to a channel, it had to hold the lock on it to ensure that it didn't
go away. This is no longer a requirement. Holding a reference is
sufficient.
2) There are places that now require less dealing with channel locks.
3) There are places where channel locks are held for much shorter periods
of time.
4) There are places where dealing with more than one channel at a time becomes
_MUCH_ easier. ChanSpy is a great example of this. Writing code in the
future that deals with multiple channels will be much easier.
Some additional information regarding channel locking and reference count
handling can be found in channel.h, where a new section has been added that
discusses some of the rules associated with it.
Mark Michelson also assisted with the development of this patch. He did the
conversion of ChanSpy and introduced a new API, ast_autochan, which makes it
much easier to deal with holding on to a channel pointer for an extended period
of time and having it get automatically updated if the channel gets masqueraded.
Mark was also a huge help in the code review process.
Thanks to David Vossel for his assistance with this branch, as well. David
did the conversion of the DAHDIScan application by making it become a wrapper
for ChanSpy internally.
The changes come from the svn/asterisk/team/russell/ast_channel_ao2 branch.
Review: http://reviewboard.digium.com/r/203/
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@190423 f38db490-d61c-443f-a65b-d21fe96a405b
2009-04-24 14:04:26 +00:00
|
|
|
ast_channel_release(chan);
|
2008-04-04 17:32:46 +00:00
|
|
|
return -1;
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
res = ast_pbx_start(chan);
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2008-04-04 17:32:46 +00:00
|
|
|
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;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
2008-04-04 17:32:46 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
static int gtalk_update_externip(void)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
|
|
|
|
{
|
|
|
|
struct gtalk_candidate *tmp;
|
|
|
|
struct hostent *hp;
|
|
|
|
struct ast_hostent ahp;
|
2009-04-10 18:02:44 +00:00
|
|
|
struct sockaddr_in sin = { 0, };
|
|
|
|
struct sockaddr_in aux = { 0, };
|
2010-07-08 22:08:07 +00:00
|
|
|
struct ast_sockaddr sin_tmp;
|
|
|
|
struct ast_sockaddr aux_tmp;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
if (time(NULL) == p->laststun)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
tmp = p->theircandidates;
|
|
|
|
p->laststun = time(NULL);
|
|
|
|
while (tmp) {
|
|
|
|
char username[256];
|
2007-05-24 15:23:04 +00:00
|
|
|
|
|
|
|
/* Find the IP address of the host */
|
2010-10-13 22:47:35 +00:00
|
|
|
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;
|
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
|
|
|
|
sin.sin_port = htons(tmp->port);
|
2010-10-05 22:01:52 +00:00
|
|
|
snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);
|
|
|
|
|
2007-05-24 15:23:04 +00:00
|
|
|
/* Find out the result of the STUN */
|
2010-07-08 22:08:07 +00:00
|
|
|
ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
|
|
|
|
ast_sockaddr_to_sin(&aux_tmp, &aux);
|
2007-05-24 15:23:04 +00:00
|
|
|
|
|
|
|
/* If the STUN result is different from the IP of the hostname,
|
2010-10-11 21:39:37 +00:00
|
|
|
* lock on the stun IP of the hostname advertised by the
|
|
|
|
* remote client */
|
2010-10-05 22:01:52 +00:00
|
|
|
if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
|
2010-07-08 22:08:07 +00:00
|
|
|
ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else {
|
|
|
|
ast_sockaddr_from_sin(&sin_tmp, &sin);
|
2010-07-08 22:08:07 +00:00
|
|
|
ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
2007-06-14 19:39:12 +00:00
|
|
|
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);
|
2007-05-24 15:23:04 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
tmp = tmp->next;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
|
|
|
|
{
|
|
|
|
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");
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!from) {
|
2006-09-18 16:36:14 +00:00
|
|
|
from = c->jid->full;
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
for (tmp = client->p; tmp; tmp = tmp->next) {
|
2010-10-13 22:47:35 +00:00
|
|
|
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))) {
|
2006-09-18 16:36:14 +00:00
|
|
|
p = tmp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
if (!p) {
|
2006-09-18 16:36:14 +00:00
|
|
|
return -1;
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
traversenodes = pak->query;
|
|
|
|
while(traversenodes) {
|
2010-10-14 21:29:04 +00:00
|
|
|
if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
|
Merged revisions 185362 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185362 | dbrooks | 2009-03-31 11:37:12 -0500 (Tue, 31 Mar 2009) | 35 lines
Fix incorrect parsing in chan_gtalk when xmpp contains extra whitespaces
To drill into the xmpp to find the capabilities between channels, chan_gtalk
calls iks_child() and iks_next(). iks_child() and iks_next() are functions in
the iksemel xml parsing library that traverse xml nodes. The bug here is that
both iks_child() and iks_next() will return the next iks_struct node
*regardless* of type. chan_gtalk expects the next node to be of type IKS_TAG,
which in most cases, it is, but in this case (a call being made from the
Empathy IM client), there exists iks_struct nodes which are not IKS_TAG data
(they are extraneous whitespaces), and chan_gtalk doesn't handle that case,
so capabilities don't match, and a call cannot be made.
iks_first_tag() and iks_next_tag(), on the other hand, will not return the
very next iks_struct, but will check to see if the next iks_struct is of
type IKS_TAG. If it isn't, it will be skipped, and the next struct of type
IKS_TAG it finds will be returned. This assures that chan_gtalk will find
the iks_struct it is looking for.
This fix simply changes all calls to iks_child() and iks_next() to become
calls to iks_first_tag() and iks_next_tag(), which resolves the capability
matching.
The following is a payload listing from Empathy, which, due to the extraneous
whitespace, will not be parsed correctly by iksemel:
<iq from='dbrooksjab@235-22-24-10/Telepathy' to='astjab@235-22-24-10/asterisk' type='set' id='542757715704'> <session xmlns='http://www.google.com/session' initiator='dbrooksjab@235-22-24-10/Telepathy' type='initiate' id='1837267342'> <description xmlns='http://www.google.com/session/phone'> <payload-type clockrate='16000' name='speex' id='96'/>
<payload-type clockrate='8000' name='PCMA' id='8'/>
<payload-type clockrate='8000' name='PCMU' id='0'/>
<payload-type clockrate='90000' name='MPA' id='97'/>
<payload-type clockrate='16000' name='SIREN' id='98'/>
<payload-type clockrate='8000' name='telephone-event' id='99'/>
</description>
</session>
</iq>
Review: http://reviewboard.digium.com/r/181/
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@185363 f38db490-d61c-443f-a65b-d21fe96a405b
2009-03-31 16:46:57 +00:00
|
|
|
traversenodes = iks_first_tag(traversenodes);
|
2006-09-18 16:36:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
2010-10-14 21:29:04 +00:00
|
|
|
if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
|
2010-10-05 22:01:52 +00:00
|
|
|
traversenodes = iks_child(traversenodes);
|
|
|
|
continue;
|
|
|
|
}
|
2010-10-14 21:29:04 +00:00
|
|
|
if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate = ast_calloc(1, sizeof(*newcandidate));
|
|
|
|
if (!newcandidate)
|
|
|
|
return 0;
|
2010-10-13 22:47:35 +00:00
|
|
|
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));
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
|
2010-10-13 22:47:35 +00:00
|
|
|
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));
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
|
2010-10-13 22:47:35 +00:00
|
|
|
if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->protocol = AJI_PROTOCOL_UDP;
|
2010-10-13 22:47:35 +00:00
|
|
|
if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2010-10-13 22:47:35 +00:00
|
|
|
if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->type = AJI_CONNECT_STUN;
|
2010-10-13 22:47:35 +00:00
|
|
|
if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->type = AJI_CONNECT_LOCAL;
|
2010-10-13 22:47:35 +00:00
|
|
|
if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->type = AJI_CONNECT_RELAY;
|
2010-10-13 22:47:35 +00:00
|
|
|
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"));
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->next = NULL;
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
newcandidate->next = p->theircandidates;
|
|
|
|
p->theircandidates = newcandidate;
|
|
|
|
p->laststun = 0;
|
|
|
|
gtalk_update_stun(p->parent, p);
|
|
|
|
newcandidate = NULL;
|
|
|
|
}
|
Merged revisions 185362 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185362 | dbrooks | 2009-03-31 11:37:12 -0500 (Tue, 31 Mar 2009) | 35 lines
Fix incorrect parsing in chan_gtalk when xmpp contains extra whitespaces
To drill into the xmpp to find the capabilities between channels, chan_gtalk
calls iks_child() and iks_next(). iks_child() and iks_next() are functions in
the iksemel xml parsing library that traverse xml nodes. The bug here is that
both iks_child() and iks_next() will return the next iks_struct node
*regardless* of type. chan_gtalk expects the next node to be of type IKS_TAG,
which in most cases, it is, but in this case (a call being made from the
Empathy IM client), there exists iks_struct nodes which are not IKS_TAG data
(they are extraneous whitespaces), and chan_gtalk doesn't handle that case,
so capabilities don't match, and a call cannot be made.
iks_first_tag() and iks_next_tag(), on the other hand, will not return the
very next iks_struct, but will check to see if the next iks_struct is of
type IKS_TAG. If it isn't, it will be skipped, and the next struct of type
IKS_TAG it finds will be returned. This assures that chan_gtalk will find
the iks_struct it is looking for.
This fix simply changes all calls to iks_child() and iks_next() to become
calls to iks_first_tag() and iks_next_tag(), which resolves the capability
matching.
The following is a payload listing from Empathy, which, due to the extraneous
whitespace, will not be parsed correctly by iksemel:
<iq from='dbrooksjab@235-22-24-10/Telepathy' to='astjab@235-22-24-10/asterisk' type='set' id='542757715704'> <session xmlns='http://www.google.com/session' initiator='dbrooksjab@235-22-24-10/Telepathy' type='initiate' id='1837267342'> <description xmlns='http://www.google.com/session/phone'> <payload-type clockrate='16000' name='speex' id='96'/>
<payload-type clockrate='8000' name='PCMA' id='8'/>
<payload-type clockrate='8000' name='PCMU' id='0'/>
<payload-type clockrate='90000' name='MPA' id='97'/>
<payload-type clockrate='16000' name='SIREN' id='98'/>
<payload-type clockrate='8000' name='telephone-event' id='99'/>
</description>
</session>
</iq>
Review: http://reviewboard.digium.com/r/181/
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@185363 f38db490-d61c-443f-a65b-d21fe96a405b
2009-03-31 16:46:57 +00:00
|
|
|
traversenodes = iks_next_tag(traversenodes);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
receipt = iks_new("iq");
|
|
|
|
iks_insert_attrib(receipt, "type", "result");
|
|
|
|
iks_insert_attrib(receipt, "from", from);
|
2010-10-13 22:47:35 +00:00
|
|
|
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"), ""));
|
2007-11-06 18:44:19 +00:00
|
|
|
ast_aji_send(c, receipt);
|
2008-02-29 14:15:03 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_delete(receipt);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
|
|
|
|
{
|
|
|
|
struct ast_frame *f;
|
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!p->rtp) {
|
2006-09-18 16:36:14 +00:00
|
|
|
return &ast_null_frame;
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2009-04-02 17:20:52 +00:00
|
|
|
f = ast_rtp_instance_read(p->rtp, 0);
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_update_stun(p->parent, p);
|
|
|
|
if (p->owner) {
|
|
|
|
/* We already hold the channel lock */
|
|
|
|
if (f->frametype == AST_FRAME_VOICE) {
|
2011-02-03 16:22:10 +00:00
|
|
|
if (!ast_format_cap_iscompatible(p->owner->nativeformats, &f->subclass.format)) {
|
|
|
|
ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
|
|
|
|
ast_format_cap_remove_bytype(p->owner->nativeformats, AST_FORMAT_TYPE_AUDIO);
|
|
|
|
ast_format_cap_add(p->owner->nativeformats, &f->subclass.format);
|
|
|
|
ast_set_read_format(p->owner, &p->owner->readformat);
|
|
|
|
ast_set_write_format(p->owner, &p->owner->writeformat);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
2010-10-11 21:39:37 +00:00
|
|
|
/* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
|
2006-09-18 16:36:14 +00:00
|
|
|
f = ast_dsp_process(p->owner, p->vad, f);
|
|
|
|
if (option_debug && f && (f->frametype == AST_FRAME_DTMF))
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
|
2010-10-11 21:39:37 +00:00
|
|
|
} */
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ast_frame *gtalk_read(struct ast_channel *ast)
|
|
|
|
{
|
|
|
|
struct ast_frame *fr;
|
|
|
|
struct gtalk_pvt *p = ast->tech_pvt;
|
|
|
|
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
fr = gtalk_rtp_read(ast, p);
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
return fr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Send frame to media channel (rtp) */
|
|
|
|
static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = ast->tech_pvt;
|
|
|
|
int res = 0;
|
2009-11-04 14:05:12 +00:00
|
|
|
char buf[256];
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
switch (frame->frametype) {
|
|
|
|
case AST_FRAME_VOICE:
|
2011-02-03 16:22:10 +00:00
|
|
|
if (!(ast_format_cap_iscompatible(ast->nativeformats, &frame->subclass.format))) {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_WARNING,
|
2009-11-04 14:05:12 +00:00
|
|
|
"Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_getformatname(&frame->subclass.format),
|
2009-11-04 14:05:12 +00:00
|
|
|
ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_getformatname(&ast->readformat),
|
|
|
|
ast_getformatname(&ast->writeformat));
|
2006-09-18 16:36:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (p) {
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
if (p->rtp) {
|
2009-04-02 17:20:52 +00:00
|
|
|
res = ast_rtp_instance_write(p->rtp, frame);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AST_FRAME_VIDEO:
|
|
|
|
if (p) {
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
if (p->vrtp) {
|
2009-04-02 17:20:52 +00:00
|
|
|
res = ast_rtp_instance_write(p->vrtp, frame);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
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 gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = newchan->tech_pvt;
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
|
|
|
|
if ((p->owner != oldchan)) {
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (p->owner == oldchan)
|
|
|
|
p->owner = newchan;
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
|
|
|
|
{
|
|
|
|
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:
|
2010-10-14 21:29:04 +00:00
|
|
|
ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
|
2006-09-18 16:36:14 +00:00
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2009-09-25 10:54:42 +00:00
|
|
|
static int gtalk_sendtext(struct ast_channel *chan, const char *text)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
struct aji_client *client = NULL;
|
|
|
|
struct gtalk_pvt *p = chan->tech_pvt;
|
|
|
|
|
|
|
|
if (!p->parent) {
|
|
|
|
ast_log(LOG_ERROR, "Parent channel not found\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!p->parent->connection) {
|
|
|
|
ast_log(LOG_ERROR, "XMPP client not found\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
client = p->parent->connection;
|
|
|
|
res = ast_aji_send_chat(client, p->them, text);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
Merged revisions 51311 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r51311 | russell | 2007-01-19 11:49:38 -0600 (Fri, 19 Jan 2007) | 23 lines
Merge the changes from the /team/group/vldtmf_fixup branch.
The main bug being addressed here is a problem introduced when two SIP
channels using SIP INFO dtmf have their media directly bridged. So, when a
DTMF END frame comes into Asterisk from an incoming INFO message, Asterisk
would try to emulate a digit of some length by first sending a DTMF BEGIN
frame and sending a DTMF END later timed off of incoming audio. However,
since there was no audio coming in, the DTMF_END was never generated. This
caused DTMF based features to no longer work.
To fix this, the core now knows when a channel doesn't care about DTMF BEGIN
frames (such as a SIP channel sending INFO dtmf). If this is the case, then
Asterisk will not emulate a digit of some length, and will instead just pass
through the single DTMF END event.
Channel drivers also now get passed the length of the digit to their digit_end
callback. This improves SIP INFO support even further by enabling us to put
the real digit duration in the INFO message instead of a hard coded 250ms.
Also, for an incoming INFO message, the duration is read from the frame and
passed into the core instead of just getting ignored.
(issue #8597, maybe others...)
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@51314 f38db490-d61c-443f-a65b-d21fe96a405b
2007-01-19 18:06:03 +00:00
|
|
|
static int gtalk_digit_begin(struct ast_channel *chan, char digit)
|
|
|
|
{
|
2010-10-06 21:09:14 +00:00
|
|
|
struct gtalk_pvt *p = chan->tech_pvt;
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
if (p->rtp) {
|
|
|
|
ast_rtp_instance_dtmf_begin(p->rtp, digit);
|
|
|
|
} else {
|
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
|
|
|
|
return res;
|
Merged revisions 51311 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r51311 | russell | 2007-01-19 11:49:38 -0600 (Fri, 19 Jan 2007) | 23 lines
Merge the changes from the /team/group/vldtmf_fixup branch.
The main bug being addressed here is a problem introduced when two SIP
channels using SIP INFO dtmf have their media directly bridged. So, when a
DTMF END frame comes into Asterisk from an incoming INFO message, Asterisk
would try to emulate a digit of some length by first sending a DTMF BEGIN
frame and sending a DTMF END later timed off of incoming audio. However,
since there was no audio coming in, the DTMF_END was never generated. This
caused DTMF based features to no longer work.
To fix this, the core now knows when a channel doesn't care about DTMF BEGIN
frames (such as a SIP channel sending INFO dtmf). If this is the case, then
Asterisk will not emulate a digit of some length, and will instead just pass
through the single DTMF END event.
Channel drivers also now get passed the length of the digit to their digit_end
callback. This improves SIP INFO support even further by enabling us to put
the real digit duration in the INFO message instead of a hard coded 250ms.
Also, for an incoming INFO message, the duration is read from the frame and
passed into the core instead of just getting ignored.
(issue #8597, maybe others...)
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@51314 f38db490-d61c-443f-a65b-d21fe96a405b
2007-01-19 18:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
|
|
|
|
{
|
2010-10-06 21:09:14 +00:00
|
|
|
struct gtalk_pvt *p = chan->tech_pvt;
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
if (p->rtp) {
|
|
|
|
ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
|
|
|
|
} else {
|
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
|
|
|
|
return res;
|
Merged revisions 51311 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r51311 | russell | 2007-01-19 11:49:38 -0600 (Fri, 19 Jan 2007) | 23 lines
Merge the changes from the /team/group/vldtmf_fixup branch.
The main bug being addressed here is a problem introduced when two SIP
channels using SIP INFO dtmf have their media directly bridged. So, when a
DTMF END frame comes into Asterisk from an incoming INFO message, Asterisk
would try to emulate a digit of some length by first sending a DTMF BEGIN
frame and sending a DTMF END later timed off of incoming audio. However,
since there was no audio coming in, the DTMF_END was never generated. This
caused DTMF based features to no longer work.
To fix this, the core now knows when a channel doesn't care about DTMF BEGIN
frames (such as a SIP channel sending INFO dtmf). If this is the case, then
Asterisk will not emulate a digit of some length, and will instead just pass
through the single DTMF END event.
Channel drivers also now get passed the length of the digit to their digit_end
callback. This improves SIP INFO support even further by enabling us to put
the real digit duration in the INFO message instead of a hard coded 250ms.
Also, for an incoming INFO message, the duration is read from the frame and
passed into the core instead of just getting ignored.
(issue #8597, maybe others...)
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@51314 f38db490-d61c-443f-a65b-d21fe96a405b
2007-01-19 18:06:03 +00:00
|
|
|
}
|
|
|
|
|
2010-10-06 21:09:14 +00:00
|
|
|
/* This function is of not in use at the moment, but I am choosing to leave this
|
|
|
|
* within the code base as a reference to how DTMF is possible through
|
2010-10-06 21:23:29 +00:00
|
|
|
* jingle signaling. However, google currently does DTMF through the RTP. */
|
|
|
|
#if 0
|
Merged revisions 51311 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r51311 | russell | 2007-01-19 11:49:38 -0600 (Fri, 19 Jan 2007) | 23 lines
Merge the changes from the /team/group/vldtmf_fixup branch.
The main bug being addressed here is a problem introduced when two SIP
channels using SIP INFO dtmf have their media directly bridged. So, when a
DTMF END frame comes into Asterisk from an incoming INFO message, Asterisk
would try to emulate a digit of some length by first sending a DTMF BEGIN
frame and sending a DTMF END later timed off of incoming audio. However,
since there was no audio coming in, the DTMF_END was never generated. This
caused DTMF based features to no longer work.
To fix this, the core now knows when a channel doesn't care about DTMF BEGIN
frames (such as a SIP channel sending INFO dtmf). If this is the case, then
Asterisk will not emulate a digit of some length, and will instead just pass
through the single DTMF END event.
Channel drivers also now get passed the length of the digit to their digit_end
callback. This improves SIP INFO support even further by enabling us to put
the real digit duration in the INFO message instead of a hard coded 250ms.
Also, for an incoming INFO message, the duration is read from the frame and
passed into the core instead of just getting ignored.
(issue #8597, maybe others...)
........
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@51314 f38db490-d61c-443f-a65b-d21fe96a405b
2007-01-19 18:06:03 +00:00
|
|
|
static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = ast->tech_pvt;
|
|
|
|
struct gtalk *client = p->parent;
|
|
|
|
iks *iq, *gtalk, *dtmf;
|
|
|
|
char buffer[2] = {digit, '\0'};
|
2009-02-12 10:31:36 +00:00
|
|
|
char *lowerthem = NULL;
|
2006-09-18 16:36:14 +00:00
|
|
|
iq = iks_new("iq");
|
|
|
|
gtalk = iks_new("gtalk");
|
|
|
|
dtmf = iks_new("dtmf");
|
|
|
|
if(!iq || !gtalk || !dtmf) {
|
2008-02-29 14:15:03 +00:00
|
|
|
iks_delete(iq);
|
|
|
|
iks_delete(gtalk);
|
|
|
|
iks_delete(dtmf);
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
iks_insert_attrib(iq, "type", "set");
|
|
|
|
iks_insert_attrib(iq, "to", p->them);
|
|
|
|
iks_insert_attrib(iq, "from", p->us);
|
|
|
|
iks_insert_attrib(iq, "id", client->connection->mid);
|
|
|
|
ast_aji_increment_mid(client->connection->mid);
|
|
|
|
iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
|
2007-09-13 11:54:56 +00:00
|
|
|
iks_insert_attrib(gtalk, "action", "session-info");
|
2010-10-11 21:39:37 +00:00
|
|
|
// put the initiator attribute to lower case if we receive the call
|
|
|
|
// otherwise GoogleTalk won't establish the session
|
2009-02-12 10:31:36 +00:00
|
|
|
if (!p->initiator) {
|
|
|
|
char c;
|
|
|
|
char *t = lowerthem = ast_strdupa(p->them);
|
|
|
|
while (((c = *t) != '/') && (*t++ = tolower(c)));
|
|
|
|
}
|
|
|
|
iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: lowerthem);
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_attrib(gtalk, "sid", p->sid);
|
|
|
|
iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
|
|
|
|
iks_insert_attrib(dtmf, "code", buffer);
|
|
|
|
iks_insert_node(iq, gtalk);
|
|
|
|
iks_insert_node(gtalk, dtmf);
|
|
|
|
|
|
|
|
ast_mutex_lock(&p->lock);
|
2007-09-13 11:54:56 +00:00
|
|
|
if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_attrib(dtmf, "action", "button-down");
|
2007-09-13 11:54:56 +00:00
|
|
|
} else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_insert_attrib(dtmf, "action", "button-up");
|
|
|
|
}
|
2007-11-06 18:44:19 +00:00
|
|
|
ast_aji_send(client->connection, iq);
|
2008-02-29 14:15:03 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
iks_delete(iq);
|
|
|
|
iks_delete(gtalk);
|
|
|
|
iks_delete(dtmf);
|
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-10-06 21:23:29 +00:00
|
|
|
#endif
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
|
|
|
|
{
|
|
|
|
ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
/*!\brief Initiate new call, part of PBX interface
|
|
|
|
* dest is the dial string */
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_call(struct ast_channel *ast, char *dest, int timeout)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = ast->tech_pvt;
|
|
|
|
|
|
|
|
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
|
|
|
|
ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_setstate(ast, AST_STATE_RING);
|
|
|
|
if (!p->ringrule) {
|
|
|
|
ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
|
|
|
|
p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
|
|
|
|
IKS_RULE_ID, p->ring, IKS_RULE_DONE);
|
2010-10-14 21:29:04 +00:00
|
|
|
} else {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
|
2010-10-14 21:29:04 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
gtalk_invite(p, p->them, p->us, p->sid, 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Hangup a call through the gtalk proxy channel */
|
|
|
|
static int gtalk_hangup(struct ast_channel *ast)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = ast->tech_pvt;
|
|
|
|
struct gtalk *client;
|
|
|
|
|
|
|
|
ast_mutex_lock(&p->lock);
|
|
|
|
client = p->parent;
|
|
|
|
p->owner = NULL;
|
|
|
|
ast->tech_pvt = NULL;
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!p->alreadygone) {
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_action(client, p, "terminate");
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_mutex_unlock(&p->lock);
|
|
|
|
|
|
|
|
gtalk_free_pvt(client, p);
|
2007-01-23 22:59:55 +00:00
|
|
|
ast_module_unref(ast_module_info->self);
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-05 22:01:52 +00:00
|
|
|
/*!\brief Part of PBX interface */
|
2011-02-03 16:22:10 +00:00
|
|
|
static struct ast_channel *gtalk_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int *cause)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
|
|
|
struct gtalk_pvt *p = NULL;
|
|
|
|
struct gtalk *client = NULL;
|
|
|
|
char *sender = NULL, *to = NULL, *s = NULL;
|
|
|
|
struct ast_channel *chan = NULL;
|
|
|
|
|
|
|
|
if (data) {
|
2007-02-20 16:56:58 +00:00
|
|
|
s = ast_strdupa(data);
|
2006-09-18 16:36:14 +00:00
|
|
|
if (s) {
|
|
|
|
sender = strsep(&s, "/");
|
2010-10-14 21:29:04 +00:00
|
|
|
if (sender && (sender[0] != '\0')) {
|
2006-09-18 16:36:14 +00:00
|
|
|
to = strsep(&s, "/");
|
2010-10-14 21:29:04 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
if (!to) {
|
|
|
|
ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-06-02 14:35:24 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
client = find_gtalk(to, sender);
|
|
|
|
if (!client) {
|
|
|
|
ast_log(LOG_WARNING, "Could not find recipient.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-06-02 14:35:24 +00:00
|
|
|
if (!strcasecmp(client->name, "guest")){
|
|
|
|
/* the guest account is not tied to any configured XMPP client,
|
|
|
|
let's set it now */
|
|
|
|
client->connection = ast_aji_get_client(sender);
|
|
|
|
if (!client->connection) {
|
|
|
|
ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
|
2008-08-21 09:55:31 +00:00
|
|
|
ASTOBJ_UNREF(client, gtalk_member_destroy);
|
2008-06-02 14:35:24 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2010-10-05 22:01:52 +00:00
|
|
|
|
2007-02-21 20:30:54 +00:00
|
|
|
ASTOBJ_WRLOCK(client);
|
2006-09-18 16:36:14 +00:00
|
|
|
p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
|
2010-10-13 22:47:35 +00:00
|
|
|
if (p) {
|
2009-06-26 15:28:53 +00:00
|
|
|
chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
|
2010-10-13 22:47:35 +00:00
|
|
|
}
|
2007-02-21 20:30:54 +00:00
|
|
|
ASTOBJ_UNLOCK(client);
|
2006-09-18 16:36:14 +00:00
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief CLI command "gtalk show channels" */
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
2007-08-31 17:43:50 +00:00
|
|
|
#define FORMAT "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
|
|
|
|
struct gtalk_pvt *p;
|
|
|
|
struct ast_channel *chan;
|
|
|
|
int numchans = 0;
|
2007-09-06 17:00:58 +00:00
|
|
|
char them[AJI_MAX_JIDLEN];
|
2007-08-31 17:43:50 +00:00
|
|
|
char *jid = NULL;
|
|
|
|
char *resource = NULL;
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
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;
|
2007-08-31 17:43:50 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_mutex_lock(>alklock);
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
|
2007-08-31 17:43:50 +00:00
|
|
|
ASTOBJ_CONTAINER_TRAVERSE(>alk_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)
|
2010-10-05 22:01:52 +00:00
|
|
|
ast_cli(a->fd, FORMAT,
|
2007-08-31 17:43:50 +00:00
|
|
|
chan->name,
|
|
|
|
jid,
|
|
|
|
resource,
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_getformatname(&chan->readformat),
|
|
|
|
ast_getformatname(&chan->writeformat)
|
2007-08-31 17:43:50 +00:00
|
|
|
);
|
2010-10-05 22:01:52 +00:00
|
|
|
else
|
2007-08-31 17:43:50 +00:00
|
|
|
ast_log(LOG_WARNING, "No available channel\n");
|
|
|
|
numchans ++;
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
ASTOBJ_UNLOCK(iterator);
|
|
|
|
});
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_mutex_unlock(>alklock);
|
2007-08-31 17:43:50 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
|
|
|
|
return CLI_SUCCESS;
|
2007-08-31 17:43:50 +00:00
|
|
|
#undef FORMAT
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
|
2010-11-02 15:14:12 +00:00
|
|
|
/*! \brief List global settings for the GoogleTalk channel */
|
|
|
|
static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
/*! \brief CLI command "gtalk reload"
|
|
|
|
* \todo XXX TODO make this work. */
|
|
|
|
#if 0
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2006-09-18 16:36:14 +00:00
|
|
|
{
|
2007-10-11 19:03:06 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "gtalk reload";
|
|
|
|
e->usage =
|
|
|
|
"Usage: gtalk reload\n"
|
|
|
|
" Reload gtalk channel driver.\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
2010-10-05 22:01:52 +00:00
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_verbose("IT DOES WORK!\n");
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SUCCESS;
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
2010-10-11 21:39:37 +00:00
|
|
|
#endif
|
2006-10-03 22:36:51 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_parser(void *data, ikspak *pak)
|
|
|
|
{
|
|
|
|
struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
|
2010-10-05 22:01:52 +00:00
|
|
|
int res;
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2010-10-13 22:47:35 +00:00
|
|
|
if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
|
2008-05-23 10:33:21 +00:00
|
|
|
ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
|
2010-10-13 22:47:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
|
|
|
|
ast_log(LOG_NOTICE, "No attribute \"type\" found. Ignoring message.\n");
|
2010-10-05 22:01:52 +00:00
|
|
|
} else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
|
2006-09-18 16:36:14 +00:00
|
|
|
/* New call */
|
|
|
|
gtalk_newcall(client, pak);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(3, "About to add candidate!\n");
|
2010-10-05 22:01:52 +00:00
|
|
|
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")) {
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_is_answered(client, pak);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
|
2007-08-24 11:49:36 +00:00
|
|
|
gtalk_is_accepted(client, pak);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_handle_dtmf(client, pak);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_hangup_farend(client, pak);
|
2010-10-05 22:01:52 +00:00
|
|
|
} else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_hangup_farend(client, pak);
|
|
|
|
}
|
|
|
|
ASTOBJ_UNREF(client, gtalk_member_destroy);
|
|
|
|
return IKS_FILTER_EAT;
|
|
|
|
}
|
2006-10-03 22:36:51 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
|
|
|
|
struct ast_codec_pref prefs, char *context,
|
|
|
|
struct gtalk *member)
|
|
|
|
{
|
|
|
|
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"))
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_parse_allow_disallow(&member->prefs, member->cap, var->value, 0);
|
2006-09-18 16:36:14 +00:00
|
|
|
else if (!strcasecmp(var->name, "allow"))
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_parse_allow_disallow(&member->prefs, member->cap, var->value, 1);
|
2006-09-18 16:36:14 +00:00
|
|
|
else if (!strcasecmp(var->name, "context"))
|
|
|
|
ast_copy_string(member->context, var->value, sizeof(member->context));
|
2008-04-21 23:42:45 +00:00
|
|
|
else if (!strcasecmp(var->name, "parkinglot"))
|
|
|
|
ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
|
2006-09-18 16:36:14 +00:00
|
|
|
else if (!strcasecmp(var->name, "connection")) {
|
|
|
|
if ((client = ast_aji_get_client(var->value))) {
|
|
|
|
member->connection = client;
|
2010-10-05 22:01:52 +00:00
|
|
|
iks_filter_add_rule(client->f, gtalk_parser, member,
|
|
|
|
IKS_RULE_TYPE, IKS_PAK_IQ,
|
2007-09-13 11:54:56 +00:00
|
|
|
IKS_RULE_FROM_PARTIAL, member->user,
|
2010-10-05 22:01:52 +00:00
|
|
|
IKS_RULE_NS, GOOGLE_NS,
|
2007-09-13 11:54:56 +00:00
|
|
|
IKS_RULE_DONE);
|
2006-09-18 16:36:14 +00:00
|
|
|
} 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_load_config(void)
|
|
|
|
{
|
|
|
|
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;
|
2006-11-01 20:38:05 +00:00
|
|
|
struct hostent *hp;
|
|
|
|
struct ast_hostent ahp;
|
2007-08-16 21:09:46 +00:00
|
|
|
struct ast_flags config_flags = { 0 };
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2007-08-16 21:09:46 +00:00
|
|
|
cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
|
2008-09-12 23:30:03 +00:00
|
|
|
if (!cfg) {
|
2006-09-18 16:36:14 +00:00
|
|
|
return 0;
|
2008-09-12 23:30:03 +00:00
|
|
|
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
|
|
|
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", GOOGLE_CONFIG);
|
|
|
|
return 0;
|
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
/* Copy the default jb config over global_jbconf */
|
|
|
|
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
|
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
/* set defaults */
|
2011-02-03 16:22:10 +00:00
|
|
|
memset(&prefs, 0, sizeof(prefs));
|
2010-10-11 21:39:37 +00:00
|
|
|
memset(&stunaddr, 0, sizeof(stunaddr));
|
2010-11-02 15:14:12 +00:00
|
|
|
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));
|
2010-10-11 21:39:37 +00:00
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
cat = ast_category_browse(cfg, NULL);
|
|
|
|
for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
|
|
|
|
/* handle jb conf */
|
2010-11-02 15:14:12 +00:00
|
|
|
if (!ast_jb_read_conf(&global_jbconf, var->name, var->value)) {
|
2006-09-18 16:36:14 +00:00
|
|
|
continue;
|
2010-11-02 15:14:12 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!strcasecmp(var->name, "allowguest")) {
|
2010-11-02 15:14:12 +00:00
|
|
|
global_allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "disallow")) {
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_parse_allow_disallow(&prefs, global_capability, var->value, 0);
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "allow")) {
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_parse_allow_disallow(&prefs, global_capability, var->value, 1);
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "context")) {
|
2010-11-02 15:14:12 +00:00
|
|
|
ast_copy_string(global_context, var->value, sizeof(global_context));
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "externip")) {
|
|
|
|
ast_copy_string(externip, var->value, sizeof(externip));
|
|
|
|
} else if (!strcasecmp(var->name, "parkinglot")) {
|
2010-11-02 15:14:12 +00:00
|
|
|
ast_copy_string(global_parkinglot, var->value, sizeof(global_parkinglot));
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "bindaddr")) {
|
2006-11-01 20:38:05 +00:00
|
|
|
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));
|
|
|
|
}
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "stunaddr")) {
|
|
|
|
stunaddr.sin_port = htons(STANDARD_STUN_PORT);
|
2010-11-02 15:14:12 +00:00
|
|
|
global_stunaddr = 1;
|
2010-10-11 21:39:37 +00:00
|
|
|
if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
|
|
|
|
ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (cat) {
|
|
|
|
if (strcasecmp(cat, "general")) {
|
|
|
|
var = ast_variable_browse(cfg, cat);
|
2007-06-03 06:10:27 +00:00
|
|
|
member = ast_calloc(1, sizeof(*member));
|
2006-09-18 16:36:14 +00:00
|
|
|
ASTOBJ_INIT(member);
|
|
|
|
ASTOBJ_WRLOCK(member);
|
2011-02-03 16:22:10 +00:00
|
|
|
member->cap = ast_format_cap_alloc_nolock();
|
2006-09-18 16:36:14 +00:00
|
|
|
if (!strcasecmp(cat, "guest")) {
|
|
|
|
ast_copy_string(member->name, "guest", sizeof(member->name));
|
|
|
|
ast_copy_string(member->user, "guest", sizeof(member->user));
|
2010-11-02 15:14:12 +00:00
|
|
|
ast_copy_string(member->context, global_context, sizeof(member->context));
|
|
|
|
ast_copy_string(member->parkinglot, global_parkinglot, sizeof(member->parkinglot));
|
|
|
|
member->allowguest = global_allowguest;
|
2006-09-18 16:36:14 +00:00
|
|
|
member->prefs = prefs;
|
|
|
|
while (var) {
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!strcasecmp(var->name, "disallow")) {
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_parse_allow_disallow(&member->prefs, member->cap,
|
2006-09-18 16:36:14 +00:00
|
|
|
var->value, 0);
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "allow")) {
|
2011-02-03 16:22:10 +00:00
|
|
|
ast_parse_allow_disallow(&member->prefs, member->cap,
|
2006-09-18 16:36:14 +00:00
|
|
|
var->value, 1);
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "context")) {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_copy_string(member->context, var->value,
|
|
|
|
sizeof(member->context));
|
2010-10-11 21:39:37 +00:00
|
|
|
} else if (!strcasecmp(var->name, "parkinglot")) {
|
2008-04-21 23:42:45 +00:00
|
|
|
ast_copy_string(member->parkinglot, var->value,
|
|
|
|
sizeof(member->parkinglot));
|
2006-09-18 16:36:14 +00:00
|
|
|
}
|
|
|
|
var = var->next;
|
|
|
|
}
|
|
|
|
ASTOBJ_UNLOCK(member);
|
|
|
|
clients = ast_aji_get_clients();
|
|
|
|
if (clients) {
|
|
|
|
ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
|
|
|
|
ASTOBJ_WRLOCK(iterator);
|
|
|
|
ASTOBJ_WRLOCK(member);
|
2008-06-02 14:35:24 +00:00
|
|
|
member->connection = NULL;
|
2010-10-05 22:01:52 +00:00
|
|
|
iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
|
2007-09-13 11:54:56 +00:00
|
|
|
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);
|
2006-09-18 16:36:14 +00:00
|
|
|
ASTOBJ_UNLOCK(member);
|
|
|
|
ASTOBJ_UNLOCK(iterator);
|
|
|
|
});
|
2008-06-02 14:35:24 +00:00
|
|
|
ASTOBJ_CONTAINER_LINK(>alk_list, member);
|
2008-08-21 09:55:31 +00:00
|
|
|
ASTOBJ_UNREF(member, gtalk_member_destroy);
|
2006-09-18 16:36:14 +00:00
|
|
|
} else {
|
|
|
|
ASTOBJ_UNLOCK(member);
|
|
|
|
ASTOBJ_UNREF(member, gtalk_member_destroy);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ASTOBJ_UNLOCK(member);
|
2010-11-02 15:14:12 +00:00
|
|
|
if (gtalk_create_member(cat, var, global_allowguest, prefs, global_context, member)) {
|
2006-09-18 16:36:14 +00:00
|
|
|
ASTOBJ_CONTAINER_LINK(>alk_list, member);
|
2010-11-02 15:14:12 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
ASTOBJ_UNREF(member, gtalk_member_destroy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cat = ast_category_browse(cfg, cat);
|
|
|
|
}
|
2010-10-11 21:39:37 +00:00
|
|
|
|
|
|
|
gtalk_update_externip();
|
2006-09-18 16:36:14 +00:00
|
|
|
gtalk_free_candidates(global_candidates);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Load module into PBX, register channel */
|
|
|
|
static int load_module(void)
|
|
|
|
{
|
2010-07-08 22:08:07 +00:00
|
|
|
struct ast_sockaddr bindaddr_tmp;
|
|
|
|
struct ast_sockaddr ourip_tmp;
|
2008-03-11 20:53:48 +00:00
|
|
|
char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
|
2011-02-03 16:22:10 +00:00
|
|
|
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));
|
|
|
|
|
2008-03-11 20:53:48 +00:00
|
|
|
free(jabber_loaded);
|
|
|
|
if (!jabber_loaded) {
|
2008-04-28 04:53:20 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2008-03-11 20:53:48 +00:00
|
|
|
}
|
|
|
|
|
2006-09-18 16:36:14 +00:00
|
|
|
ASTOBJ_CONTAINER_INIT(>alk_list);
|
|
|
|
if (!gtalk_load_config()) {
|
|
|
|
ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-20 17:15:54 +00:00
|
|
|
sched = ast_sched_context_create();
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!sched) {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to create schedule context\n");
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
io = io_context_create();
|
2010-10-11 21:39:37 +00:00
|
|
|
if (!io) {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to create I/O context\n");
|
2010-10-11 21:39:37 +00:00
|
|
|
}
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2010-07-08 23:46:20 +00:00
|
|
|
ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
|
2010-10-14 21:29:04 +00:00
|
|
|
if (gtalk_get_local_ip(&ourip_tmp)) {
|
2006-09-18 16:36:14 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2010-07-08 22:08:07 +00:00
|
|
|
__ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));
|
2006-09-18 16:36:14 +00:00
|
|
|
|
2009-04-02 17:20:52 +00:00
|
|
|
ast_rtp_glue_register(>alk_rtp_glue);
|
2008-07-11 18:09:35 +00:00
|
|
|
ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
/* Make sure we can register our channel type */
|
|
|
|
if (ast_channel_register(>alk_tech)) {
|
2006-11-30 21:22:01 +00:00
|
|
|
ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
|
2006-09-18 16:36:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-11 21:39:37 +00:00
|
|
|
/*! \brief Reload module
|
|
|
|
* \todo XXX TODO make this work. */
|
|
|
|
#if 0
|
2006-09-18 16:36:14 +00:00
|
|
|
static int reload(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2010-10-11 21:39:37 +00:00
|
|
|
#endif
|
2006-09-18 16:36:14 +00:00
|
|
|
/*! \brief Unload the gtalk channel from Asterisk */
|
|
|
|
static int unload_module(void)
|
|
|
|
{
|
|
|
|
struct gtalk_pvt *privates = NULL;
|
2008-07-11 18:09:35 +00:00
|
|
|
ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
|
2006-09-18 16:36:14 +00:00
|
|
|
/* First, take us out of the channel loop */
|
|
|
|
ast_channel_unregister(>alk_tech);
|
2009-04-02 17:20:52 +00:00
|
|
|
ast_rtp_glue_unregister(>alk_rtp_glue);
|
2006-09-18 16:36:14 +00:00
|
|
|
|
|
|
|
if (!ast_mutex_lock(>alklock)) {
|
|
|
|
/* Hangup all interfaces if they have an owner */
|
|
|
|
ASTOBJ_CONTAINER_TRAVERSE(>alk_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(>alklock);
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_WARNING, "Unable to lock the monitor\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ASTOBJ_CONTAINER_DESTROYALL(>alk_list, gtalk_member_destroy);
|
|
|
|
ASTOBJ_CONTAINER_DESTROY(>alk_list);
|
2011-02-03 16:22:10 +00:00
|
|
|
global_capability = ast_format_cap_destroy(global_capability);
|
|
|
|
gtalk_tech.capabilities = ast_format_cap_destroy(gtalk_tech.capabilities);
|
2006-09-18 16:36:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-20 19:35:02 +00:00
|
|
|
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gtalk Channel Driver",
|
2006-09-18 16:36:14 +00:00
|
|
|
.load = load_module,
|
|
|
|
.unload = unload_module,
|
2010-10-11 21:39:37 +00:00
|
|
|
/* .reload = reload, */
|
2010-07-20 19:35:02 +00:00
|
|
|
.load_pri = AST_MODPRI_CHANNEL_DRIVER,
|
2010-10-14 21:29:04 +00:00
|
|
|
);
|