aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xCHANGES6
-rwxr-xr-xMakefile2
-rwxr-xr-xapps/Makefile3
-rwxr-xr-xapps/app_authenticate.c154
-rwxr-xr-xchannels/chan_mgcp.c61
-rwxr-xr-xchannels/chan_sip.c227
-rwxr-xr-xconfigs/sip.conf.sample2
-rwxr-xr-xframe.c33
-rwxr-xr-xinclude/asterisk/rtp.h15
-rwxr-xr-xrtp.c204
-rwxr-xr-xsounds.txt2
-rwxr-xr-xsounds/auth-incorrect.gsmbin0 -> 7524 bytes
12 files changed, 660 insertions, 49 deletions
diff --git a/CHANGES b/CHANGES
index 55cd0d7d5..d190dd4bf 100755
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+ -- Make HOLD on SIP make use of asterisk MOH
+ -- Add supervised transfer (tested with Pingtel only)
+ -- Allow maxexpirey and defaultexpirey to be runtime configurable for SIP
+ -- Preliminary codec 13 support (RFC3389)
+ -- Add app_authenticate for general purpose authentication
+ -- Optimize RTP and smoother
-- Create special variable "EXTEN-n" where it is extension stripped by n MSD
-- Fix uninitialized frame pointer in channel.c
-- Add global variables support under [globals] of extensions.conf
diff --git a/Makefile b/Makefile
index 1d2bb9993..d62c6a079 100755
--- a/Makefile
+++ b/Makefile
@@ -144,7 +144,7 @@ datafiles: all
exit 1; \
fi; \
done
- for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-* sounds/conf-* sounds/agent-* sounds/invalid* sounds/tt-*; do \
+ for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-* sounds/conf-* sounds/agent-* sounds/invalid* sounds/tt-* sounds/auth-*; do \
if grep -q "^%`basename $$x`%" sounds.txt; then \
install $$x $(ASTVARLIBDIR)/sounds ; \
else \
diff --git a/apps/Makefile b/apps/Makefile
index 95f727531..1d3ad29ad 100755
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -17,7 +17,8 @@ APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_intercom.
app_agi.so app_qcall.so app_adsiprog.so app_getcpeid.so app_milliwatt.so \
app_zapateller.so app_datetime.so app_setcallerid.so app_festival.so \
app_queue.so app_senddtmf.so app_parkandannounce.so app_striplsd.so \
- app_setcidname.so app_lookupcidname.so app_substring.so app_macro.so
+ app_setcidname.so app_lookupcidname.so app_substring.so app_macro.so \
+ app_authenticate.so
#APPS+=app_sql_postgres.so
#APPS+=app_sql_odbc.so
diff --git a/apps/app_authenticate.c b/apps/app_authenticate.c
new file mode 100755
index 000000000..69d244351
--- /dev/null
+++ b/apps/app_authenticate.c
@@ -0,0 +1,154 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Execute arbitrary authenticate commands
+ *
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <asterisk/lock.h>
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <asterisk/app.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <pthread.h>
+
+
+static char *tdesc = "Authentication Application";
+
+static char *app = "Authenticate";
+
+static char *synopsis = "Authenticate a user";
+
+static char *descrip =
+" Authenticate(password[|options]): Requires a user to enter a"
+"given password in order to continue execution. If the\n"
+"password begins with the '/' character, it is interpreted as\n"
+"a file which contains a list of valid passwords (1 per line).\n"
+"an optional set of opions may be provided by concatenating any\n"
+"of the following letters:\n"
+" a - Set account code to the password that is entered\n"
+"\n"
+"Returns 0 if the user enters a valid password within three\n"
+"tries, or -1 otherwise (or on hangup).\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int auth_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int retries;
+ struct localuser *u;
+ char password[256]="";
+ char passwd[256];
+ char *opts;
+ char *prompt;
+ if (!data || !strlen(data)) {
+ ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
+ return -1;
+ }
+ LOCAL_USER_ADD(u);
+ if (chan->_state != AST_STATE_UP) {
+ res = ast_answer(chan);
+ if (res) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+ strncpy(password, data, sizeof(password) - 1);
+ opts=strchr(password, '|');
+ if (opts) {
+ *opts = 0;
+ opts++;
+ } else
+ opts = "";
+ /* Start asking for password */
+ prompt = "agent-pass";
+ for (retries = 0; retries < 3; retries++) {
+ res = ast_app_getdata(chan, prompt, passwd, sizeof(passwd) - 2, 0);
+ if (res < 0)
+ break;
+ res = 0;
+ if (password[0] == '/') {
+ /* Compare against a file */
+ char tmp[80];
+ FILE *f;
+ f = fopen(password, "r");
+ if (f) {
+ char buf[256] = "";
+ while(!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!feof(f) && strlen(buf)) {
+ buf[strlen(buf) - 1] = '\0';
+ if (strlen(buf) && !strcmp(passwd, buf))
+ break;
+ }
+ }
+ fclose(f);
+ if (strlen(buf) && !strcmp(passwd, buf))
+ break;
+ } else
+ ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", password, strerror(errno));
+ } else {
+ /* Compare against a fixed password */
+ if (!strcmp(passwd, password))
+ break;
+ }
+ prompt="auth-incorrect";
+ }
+ if ((retries < 3) && !res) {
+ if (strchr(opts, 'a'))
+ ast_cdr_setaccount(chan, passwd);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "vm-goodbye", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ res = -1;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ STANDARD_HANGUP_LOCALUSERS;
+ return ast_unregister_application(app);
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, auth_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 88a0d2cf0..08ef81499 100755
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -131,6 +131,7 @@ struct mgcp_endpoint {
int outgoing;
struct ast_channel *owner;
struct ast_rtp *rtp;
+ struct sockaddr_in tmpdest;
struct mgcp_endpoint *next;
struct mgcp_gateway *parent;
};
@@ -317,6 +318,7 @@ static int mgcp_hangup(struct ast_channel *ast)
p->owner = NULL;
if (strlen(p->cxident))
transmit_connection_del(p);
+ strcpy(p->cxident, "");
if (!p->alreadygone && (!p->outgoing || (ast->_state == AST_STATE_UP)))
transmit_notify_request(p, "ro", 1);
else
@@ -324,8 +326,9 @@ static int mgcp_hangup(struct ast_channel *ast)
ast->pvt->pvt = NULL;
p->alreadygone = 0;
p->outgoing = 0;
- strcpy(p->cxident, "");
strcpy(p->callid, "");
+ /* Reset temporary destination */
+ memset(&p->tmpdest, 0, sizeof(p->tmpdest));
if (p->rtp) {
ast_rtp_destroy(p->rtp);
p->rtp = NULL;
@@ -515,6 +518,7 @@ static struct ast_channel *mgcp_new(struct mgcp_endpoint *i, int state)
tmp->pvt->indicate = mgcp_indicate;
tmp->pvt->fixup = mgcp_fixup;
tmp->pvt->send_digit = mgcp_senddigit;
+ tmp->pvt->bridge = ast_rtp_bridge;
if (strlen(i->language))
strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
i->owner = tmp;
@@ -951,8 +955,15 @@ static int add_sdp(struct mgcp_request *resp, struct mgcp_endpoint *p, struct as
if (rtp) {
ast_rtp_get_peer(rtp, &dest);
} else {
- dest.sin_addr = p->parent->ourip;
- dest.sin_port = sin.sin_port;
+ if (p->tmpdest.sin_addr.s_addr) {
+ dest.sin_addr = p->tmpdest.sin_addr;
+ dest.sin_port = p->tmpdest.sin_port;
+ /* Reset temporary destination */
+ memset(&p->tmpdest, 0, sizeof(p->tmpdest));
+ } else {
+ dest.sin_addr = p->parent->ourip;
+ dest.sin_port = sin.sin_port;
+ }
}
printf("We're at %s port %d\n", inet_ntoa(p->parent->ourip), ntohs(sin.sin_port));
snprintf(v, sizeof(v), "v=0\r\n");
@@ -991,6 +1002,12 @@ static int transmit_modify_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rtp
char local[256];
char tmp[80];
int x;
+ if (!strlen(p->cxident) && rtp) {
+ /* We don't have a CXident yet, store the destination and
+ wait a bit */
+ ast_rtp_get_peer(rtp, &p->tmpdest);
+ return 0;
+ }
snprintf(local, sizeof(local), "p:20");
for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
if (p->capability & x) {
@@ -1003,6 +1020,7 @@ static int transmit_modify_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rtp
add_header(&resp, "L", local);
add_header(&resp, "M", "sendrecv");
add_header(&resp, "X", p->txident);
+ add_header(&resp, "I", p->cxident);
add_header(&resp, "S", "");
add_sdp(&resp, p, rtp);
p->lastout = oseq;
@@ -1278,8 +1296,14 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore)
p = find_endpoint(NULL, ident, &sin);
if (p) {
handle_response(p, result, ident);
- if ((c = get_header(&req, "I")))
- strncpy(p->cxident, c, sizeof(p->cxident) - 1);
+ if ((c = get_header(&req, "I"))) {
+ if (strlen(c)) {
+ strncpy(p->cxident, c, sizeof(p->cxident) - 1);
+ if (p->tmpdest.sin_addr.s_addr) {
+ transmit_modify_with_sdp(p, NULL);
+ }
+ }
+ }
if (req.lines)
process_sdp(p, &req);
}
@@ -1483,6 +1507,31 @@ struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
return gw;
}
+static struct ast_rtp *mgcp_get_rtp_peer(struct ast_channel *chan)
+{
+ struct mgcp_endpoint *p;
+ p = chan->pvt->pvt;
+ if (p && p->rtp)
+ return p->rtp;
+ return NULL;
+}
+
+static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp)
+{
+ struct mgcp_endpoint *p;
+ p = chan->pvt->pvt;
+ if (p) {
+ transmit_modify_with_sdp(p, rtp);
+ return 0;
+ }
+ return -1;
+}
+
+static struct ast_rtp_protocol mgcp_rtp = {
+ get_rtp_info: mgcp_get_rtp_peer,
+ set_rtp_peer: mgcp_set_rtp_peer,
+};
+
int load_module()
{
struct ast_config *cfg;
@@ -1590,6 +1639,8 @@ int load_module()
ast_destroy(cfg);
return -1;
}
+ mgcp_rtp.type = type;
+ ast_rtp_proto_register(&mgcp_rtp);
ast_cli_register(&cli_show_endpoints);
/* And start the monitor for the first time */
restart_monitor();
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index b9f235c3c..a057eb89e 100755
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -32,6 +32,7 @@
#include <asterisk/cli.h>
#include <asterisk/md5.h>
#include <asterisk/app.h>
+#include <asterisk/musiconhold.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
@@ -47,8 +48,12 @@
/* #define VOCAL_DATA_HACK */
#define SIPDUMPER
-#define DEFAULT_EXPIREY 120
-#define MAX_EXPIREY 3600
+#define DEFAULT_DEFAULT_EXPIREY 120
+#define DEFAULT_MAX_EXPIREY 3600
+
+static int max_expirey = DEFAULT_MAX_EXPIREY;
+static int default_expirey = DEFAULT_DEFAULT_EXPIREY;
+
#define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */
#define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */
@@ -142,6 +147,7 @@ static struct sip_pvt {
char refer_to[AST_MAX_EXTENSION]; /* Place to store REFER-TO extension */
char referred_by[AST_MAX_EXTENSION];/* Place to store REFERRED-BY extension */
char refer_contact[AST_MAX_EXTENSION];/* Place to store Contact info from a REFER extension */
+ struct sip_pvt *refer_call; /* Call we are referring */
char record_route[256];
char record_route_info[256];
char remote_party_id[256];
@@ -728,6 +734,7 @@ static int sip_indicate(struct ast_channel *ast, int condition)
}
+#if 0
static int sip_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
{
struct sip_pvt *p0, *p1;
@@ -742,6 +749,7 @@ static int sip_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags,
ast_pthread_mutex_lock(&c1->lock);
p0 = c0->pvt->pvt;
p1 = c1->pvt->pvt;
+ ast_log(LOG_DEBUG, "Reinvite? %s: %s, %s: %s\n", c0->name, p0->canreinvite ? "yes" : "no", c1->name, p1->canreinvite ? "yes" : "no");
if (!p0->canreinvite || !p1->canreinvite) {
/* Not gonna support reinvite */
ast_pthread_mutex_unlock(&c0->lock);
@@ -796,6 +804,7 @@ static int sip_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags,
}
return -1;
}
+#endif
static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title)
{
@@ -808,7 +817,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title)
tmp->nativeformats = capability;
fmt = ast_best_codec(tmp->nativeformats);
if (title)
- snprintf(tmp->name, sizeof(tmp->name), "SIP/%s", title);
+ snprintf(tmp->name, sizeof(tmp->name), "SIP/%s-%04x", title, rand() & 0xffff);
else
snprintf(tmp->name, sizeof(tmp->name), "SIP/%s:%d", inet_ntoa(i->sa.sin_addr), ntohs(i->sa.sin_port));
tmp->type = type;
@@ -830,7 +839,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title)
tmp->pvt->indicate = sip_indicate;
tmp->pvt->fixup = sip_fixup;
tmp->pvt->send_digit = sip_senddigit;
- tmp->pvt->bridge = sip_bridge;
+ tmp->pvt->bridge = ast_rtp_bridge;
if (strlen(i->language))
strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
i->owner = tmp;
@@ -1087,7 +1096,7 @@ static int sip_register(char *value, int lineno)
if (secret)
strncpy(reg->secret, secret, sizeof(reg->secret)-1);
reg->expire = -1;
- reg->refresh = DEFAULT_EXPIREY;
+ reg->refresh = default_expirey;
reg->addr.sin_family = AF_INET;
memcpy(&reg->addr.sin_addr, hp->h_addr, sizeof(&reg->addr.sin_addr));
reg->addr.sin_port = porta ? htons(atoi(porta)) : htons(DEFAULT_SIP_PORT);
@@ -1237,11 +1246,21 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
ast_log(LOG_WARNING, "No compatible codecs!\n");
return -1;
}
- if (p->owner && !(p->owner->nativeformats & p->capability)) {
- ast_log(LOG_DEBUG, "Oooh, we need to change our formats since our peer supports only %d and not %d\n", p->capability, p->owner->nativeformats);
- p->owner->nativeformats = p->capability;
- ast_set_read_format(p->owner, p->owner->readformat);
- ast_set_write_format(p->owner, p->owner->writeformat);
+ if (p->owner) {
+ if (p->owner->nativeformats & p->capability) {
+ ast_log(LOG_DEBUG, "Oooh, we need to change our formats since our peer supports only %d and not %d\n", p->capability, p->owner->nativeformats);
+ p->owner->nativeformats = p->capability;
+ ast_set_read_format(p->owner, p->owner->readformat);
+ ast_set_write_format(p->owner, p->owner->writeformat);
+ }
+ if (p->owner->bridge) {
+ /* Turn on/off music on hold if we are holding/unholding */
+ if (sin.sin_addr.s_addr) {
+ ast_moh_stop(p->owner->bridge);
+ } else {
+ ast_moh_start(p->owner->bridge, NULL);
+ }
+ }
}
return 0;
@@ -1830,7 +1849,7 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth)
if (auth)
add_header(&req, "Authorization", auth);
- snprintf(tmp, sizeof(tmp), "%d", DEFAULT_EXPIREY);
+ snprintf(tmp, sizeof(tmp), "%d", default_expirey);
add_header(&req, "Expires", tmp);
add_header(&req, "Event", "registration");
copy_request(&p->initreq, &req);
@@ -1933,8 +1952,8 @@ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_req
strcpy(p->username, "");
if (p->expire > -1)
ast_sched_del(sched, p->expire);
- if ((expirey < 1) || (expirey > MAX_EXPIREY))
- expirey = DEFAULT_EXPIREY;
+ if ((expirey < 1) || (expirey > max_expirey))
+ expirey = max_expirey;
p->expire = ast_sched_add(sched, expirey * 1000, expire_register, p);
pvt->expirey = expirey;
if (memcmp(&p->addr, &oldsin, sizeof(oldsin))) {
@@ -2130,7 +2149,9 @@ static int get_refer_info(struct sip_pvt *p, struct sip_request *oreq)
char tmp2[256] = "", *c2, *a2;
char tmp3[256];
char tmp4[256];
+ char tmp5[256] = ""; /* CallID to replace */
struct sip_request *req;
+ struct sip_pvt *p2;
req = oreq;
if (!req)
@@ -2151,32 +2172,81 @@ static int get_refer_info(struct sip_pvt *p, struct sip_request *oreq)
}
c += 4;
c2 += 4;
- if ((a = strchr(c, '@')) || (a = strchr(c, ';'))) {
+ if ((a = strchr(c, '?'))) {
+ /* Search for arguemnts */
*a = '\0';
+ a++;
+ if (!strncasecmp(a, "REPLACES=", strlen("REPLACES="))) {
+ strncpy(tmp5, a + strlen("REPLACES="), sizeof(tmp5) - 1);
+ if ((a = strchr(tmp5, '%'))) {
+ /* Yuck! Pingtel converts the '@' to a %40, icky icky! Convert
+ back to an '@' */
+ if ((a[1] == '4') && (a[2] == '0')) {
+ *a = '@';
+ memmove(a + 1, a+3, strlen(a + 3));
+ }
+ }
+ if ((a = strchr(tmp5, '%')))
+ *a = '\0';
+ }
}
- if ((a2 = strchr(c2, '@')) || (a2 = strchr(c2, ';'))) {
+
+ if ((a = strchr(c, '@')))
+ *a = '\0';
+ if ((a = strchr(c, ';')))
+ *a = '\0';
+
+
+ if ((a2 = strchr(c2, '@')))
*a2 = '\0';
- }
+
+ if ((a2 = strchr(c2, ';')))
+ *a2 = '\0';
+
if (sipdebug)
ast_verbose("Looking for %s in %s\n", c, p->context);
ast_verbose("Looking for %s in %s\n", c2, p->context);
-
- if (ast_exists_extension(NULL, p->context, c, 1, NULL) && ast_exists_extension(NULL, p->context, c2, 1, NULL)) {
- if (!oreq)
- ast_log(LOG_DEBUG,"Something is wrong with this line.\n"); //This line is ignored for some reason....
- ast_log(LOG_DEBUG,"Assigning Extension %s to REFER-TO\n", c);
- ast_log(LOG_DEBUG,"Assigning Extension %s to REFERRED-BY\n", c2);
- ast_log(LOG_DEBUG,"Assigning Contact Info %s to REFER_CONTACT\n", tmp3);
- ast_log(LOG_DEBUG,"Assigning Remote-Party-ID Info %s to REMOTE_PARTY_ID\n",tmp4);
- strncpy(p->refer_to, c, sizeof(p->refer_to) - 1);
- strncpy(p->referred_by, c2, sizeof(p->referred_by) - 1);
- strncpy(p->refer_contact, tmp3, sizeof(p->refer_contact) - 1);
- strncpy(p->remote_party_id, tmp4, sizeof(p->remote_party_id) - 1);
+
+ if (strlen(tmp5)) {
+ /* This is a supervised transfer */
+ ast_log(LOG_DEBUG,"Assigning Replace-Call-ID Info %s to REPLACE_CALL_ID\n",tmp5);
+
+ strncpy(p->refer_to, "", sizeof(p->refer_to) - 1);
+ strncpy(p->referred_by, "", sizeof(p->referred_by) - 1);
+ strncpy(p->refer_contact, "", sizeof(p->refer_contact) - 1);
+ strncpy(p->remote_party_id, "", sizeof(p->remote_party_id) - 1);
+ p->refer_call = NULL;
+ ast_pthread_mutex_lock(&iflock);
+ /* Search interfaces and find the match */
+ p2 = iflist;
+ while(p2) {
+ if (!strcmp(p2->callid, tmp5)) {
+ /* Go ahead and lock it before returning */
+ ast_pthread_mutex_lock(&p2->lock);
+ p->refer_call = p2;
+ break;
+ }
+ p2 = p2->next;
+ }
+ ast_pthread_mutex_unlock(&iflock);
+ if (p->refer_call)
return 0;
- }
-
- if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
+ else
+ ast_log(LOG_NOTICE, "Supervised transfer requested, but unable to find callid '%s'\n", tmp5);
+ } else if (ast_exists_extension(NULL, p->context, c, 1, NULL) && ast_exists_extension(NULL, p->context, c2, 1, NULL)) {
+ /* This is an unsupervised transfer */
+ ast_log(LOG_DEBUG,"Assigning Extension %s to REFER-TO\n", c);
+ ast_log(LOG_DEBUG,"Assigning Extension %s to REFERRED-BY\n", c2);
+ ast_log(LOG_DEBUG,"Assigning Contact Info %s to REFER_CONTACT\n", tmp3);
+ ast_log(LOG_DEBUG,"Assigning Remote-Party-ID Info %s to REMOTE_PARTY_ID\n",tmp4);
+ strncpy(p->refer_to, c, sizeof(p->refer_to) - 1);
+ strncpy(p->referred_by, c2, sizeof(p->referred_by) - 1);
+ strncpy(p->refer_contact, tmp3, sizeof(p->refer_contact) - 1);
+ strncpy(p->remote_party_id, tmp4, sizeof(p->remote_party_id) - 1);
+ p->refer_call = NULL;
+ return 0;
+ } else if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
return 1;
}
@@ -2735,7 +2805,7 @@ retrylock:
if (r->expire != -1)
ast_sched_del(sched, r->expire);
expires=atoi(get_header(req, "expires"));
- if (!expires) expires=DEFAULT_EXPIREY;
+ if (!expires) expires=default_expirey;
r->expire=ast_sched_add(sched, (expires-2)*1000, sip_reregister, r);
}
@@ -2879,6 +2949,37 @@ static int determine_firstline_parts( struct sip_request *req ) {
return 1;
}
+static int attempt_transfer(struct sip_pvt *p1, struct sip_pvt *p2)
+{
+ if (!p1->owner || !p2->owner) {
+ ast_log(LOG_WARNING, "Transfer attempted without dual ownership?\n");
+ return -1;
+ }
+ if (p1->owner->bridge) {
+ if (p2->owner->bridge)
+ ast_moh_stop(p2->owner->bridge);
+ ast_moh_stop(p1->owner->bridge);
+ ast_moh_stop(p1->owner);
+ ast_moh_stop(p2->owner);
+ if (ast_channel_masquerade(p2->owner, p1->owner->bridge)) {
+ ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", p2->owner->name, p1->owner->bridge->name);
+ return -1;
+ }
+ } else if (p2->owner->bridge) {
+ ast_moh_stop(p2->owner->bridge);
+ ast_moh_stop(p2->owner);
+ ast_moh_stop(p1->owner);
+ if (ast_channel_masquerade(p1->owner, p2->owner->bridge)) {
+ ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", p1->owner->name, p2->owner->bridge->name);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_NOTICE, "Transfer attempted with no bridged calls to transfer\n");
+ return -1;
+ }
+ return 0;
+}
+
static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin)
{
struct sip_request resp;
@@ -3048,16 +3149,23 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
transmit_response_with_allow(p, "404 Not Found", req);
else if (res > 0)
transmit_response_with_allow(p, "484 Address Incomplete", req);
- else
+ else {
transmit_response(p, "202 Accepted", req);
- ast_log(LOG_DEBUG,"202 Accepted\n");
- c = p->owner;
- if (c) {
- transfer_to = c->bridge;
- if (transfer_to)
- ast_async_goto(transfer_to,"", p->refer_to,1, 1);
+ if (p->refer_call) {
+ ast_log(LOG_DEBUG,"202 Accepted (supervised)\n");
+ attempt_transfer(p, p->refer_call);
+ ast_pthread_mutex_unlock(&p->refer_call->lock);
+ p->refer_call = NULL;
+ } else {
+ ast_log(LOG_DEBUG,"202 Accepted (blind)\n");
+ c = p->owner;
+ if (c) {
+ transfer_to = c->bridge;
+ if (transfer_to)
+ ast_async_goto(transfer_to,"", p->refer_to,1, 1);
+ }
+ }
}
-
} else if (!strcasecmp(cmd, "CANCEL") || !strcasecmp(cmd, "BYE")) {
copy_request(&p->initreq, req);
p->alreadygone = 1;
@@ -3140,7 +3248,7 @@ static int sipsock_read(int *id, int fd, short events, void *ignore)
/* Must have at least two headers */
return 1;
}
- /* Process request, with iflock held */
+ /* Process request, with netlock held */
ast_pthread_mutex_lock(&netlock);
p = find_call(&req, &sin);
if (p) {
@@ -3495,6 +3603,8 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v)
peer->expirey = expirey;
}
peer->capability = capability;
+ /* Assume can reinvite */
+ peer->canreinvite = 1;
while(v) {
if (!strcasecmp(v->name, "secret"))
strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
@@ -3619,6 +3729,14 @@ static int reload_config()
strncpy(context, v->value, sizeof(context)-1);
} else if (!strcasecmp(v->name, "language")) {
strncpy(language, v->value, sizeof(language)-1);
+ } else if (!strcasecmp(v->name, "maxexpirey")) {
+ max_expirey = atoi(v->value);
+ if (max_expirey < 1)
+ max_expirey = DEFAULT_MAX_EXPIREY;
+ } else if (!strcasecmp(v->name, "defaultexpirey")) {
+ default_expirey = atoi(v->value);
+ if (default_expirey < 1)
+ default_expirey = DEFAULT_DEFAULT_EXPIREY;
} else if (!strcasecmp(v->name, "bindaddr")) {
if (!(hp = gethostbyname(v->value))) {
ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
@@ -3743,6 +3861,31 @@ static int reload_config()
return 0;
}
+static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan)
+{
+ struct sip_pvt *p;
+ p = chan->pvt->pvt;
+ if (p && p->rtp && p->canreinvite)
+ return p->rtp;
+ return NULL;
+}
+
+static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp)
+{
+ struct sip_pvt *p;
+ p = chan->pvt->pvt;
+ if (p) {
+ transmit_reinvite_with_sdp(p, rtp);
+ return 0;
+ }
+ return -1;
+}
+
+static struct ast_rtp_protocol sip_rtp = {
+ get_rtp_info: sip_get_rtp_peer,
+ set_rtp_peer: sip_set_rtp_peer,
+};
+
int load_module()
{
int res;
@@ -3761,6 +3904,8 @@ int load_module()
ast_cli_register(&cli_show_registry);
ast_cli_register(&cli_debug);
ast_cli_register(&cli_no_debug);
+ sip_rtp.type = type;
+ ast_rtp_proto_register(&sip_rtp);
sched = sched_context_create();
if (!sched) {
ast_log(LOG_WARNING, "Unable to create schedule context\n");
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index ed0ed6f93..dad7c1501 100755
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -7,6 +7,8 @@ bindaddr = 0.0.0.0 ; Address to bind to
context = default ; Default for incoming calls
;tos=lowdelay
;tos=184
+;maxexpirey=3600 ; Max length of incoming registration we allow
+;defaultexpirey=120 ; Default length of incoming/outoing registration
;[snomsip]
;type=friend
diff --git a/frame.c b/frame.c
index e37e816b3..fb255d83b 100755
--- a/frame.c
+++ b/frame.c
@@ -35,10 +35,12 @@ struct ast_smoother {
int size;
int format;
int readdata;
+ int optimizablestream;
float samplesperbyte;
struct ast_frame f;
char data[SMOOTHER_SIZE];
char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
+ struct ast_frame *opt;
int len;
};
@@ -76,6 +78,28 @@ int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
ast_log(LOG_WARNING, "Out of smoother space\n");
return -1;
}
+ if ((f->datalen == s->size) && !s->opt) {
+ if (!s->len) {
+ /* Optimize by sending the frame we just got
+ on the next read, thus eliminating the douple
+ copy */
+ s->opt = f;
+ return 0;
+ } else {
+ s->optimizablestream++;
+ if (s->optimizablestream > 10) {
+ /* For the past 10 rounds, we have input and output
+ frames of the correct size for this smoother, yet
+ we were unable to optimize because there was still
+ some cruft left over. Lets just drop the cruft so
+ we can move to a fully optimized path */
+ s->len = 0;
+ s->opt = f;
+ return 0;
+ }
+ }
+ } else
+ s->optimizablestream = 0;
memcpy(s->data + s->len, f->data, f->datalen);
s->len += f->datalen;
return 0;
@@ -83,6 +107,15 @@ int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
struct ast_frame *ast_smoother_read(struct ast_smoother *s)
{
+ struct ast_frame *opt;
+
+ /* IF we have an optimization frame, send it */
+ if (s->opt) {
+ opt = s->opt;
+ s->opt = NULL;
+ return opt;
+ }
+
/* Make sure we have enough data */
if (s->len < s->size) {
return NULL;
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index 8137eb38d..30639a5cd 100755
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -17,6 +17,7 @@
#include <asterisk/frame.h>
#include <asterisk/io.h>
#include <asterisk/sched.h>
+#include <asterisk/channel.h>
#include <netinet/in.h>
@@ -24,6 +25,14 @@
extern "C" {
#endif
+struct ast_rtp_protocol {
+ struct ast_rtp *(*get_rtp_info)(struct ast_channel *chan); /* Get RTP struct, or NULL if unwilling to transfer */
+ int (*set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer); /* Set RTP peer */
+ int (*get_rtp_willing)(struct ast_channel *chan); /* Willing to native bridge */
+ char *type;
+ struct ast_rtp_protocol *next;
+};
+
struct ast_rtp;
typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data);
@@ -58,6 +67,12 @@ int rtp2ast(int id);
char *ast2rtpn(int id);
+int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc);
+
+int ast_rtp_proto_register(struct ast_rtp_protocol *proto);
+
+void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/rtp.c b/rtp.c
index 21bee6980..ca5d7a1f2 100755
--- a/rtp.c
+++ b/rtp.c
@@ -30,6 +30,7 @@
#include <asterisk/logger.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
+#include <asterisk/channel_pvt.h>
#define TYPE_SILENCE 0x2
#define TYPE_HIGH 0x0
@@ -47,6 +48,7 @@ struct ast_rtp {
unsigned int lastts;
unsigned int lastrxts;
int lasttxformat;
+ int lastrxformat;
int dtmfcount;
struct sockaddr_in us;
struct sockaddr_in them;
@@ -61,6 +63,8 @@ struct ast_rtp {
ast_rtp_callback callback;
};
+static struct ast_rtp_protocol *protos = NULL;
+
int ast_rtp_fd(struct ast_rtp *rtp)
{
return rtp->s;
@@ -151,6 +155,49 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat
return f;
}
+static struct ast_frame *process_rfc3389(struct ast_rtp *rtp, unsigned char *data, int len)
+{
+ struct ast_frame *f = NULL;
+ /* Convert comfort noise into audio with various codecs. Unfortunately this doesn't
+ totally help us out becuase we don't have an engine to keep it going and we are not
+ guaranteed to have it every 20ms or anything */
+#if 0
+ printf("RFC3389: %d bytes, format is %d\n", len, rtp->lastrxformat);
+#endif
+ ast_log(LOG_NOTICE, "RFC3389 support incomplete. Turn off on client if possible\n");
+ if (!rtp->lastrxformat)
+ return NULL;
+ switch(rtp->lastrxformat) {
+ case AST_FORMAT_ULAW:
+ rtp->f.frametype = AST_FRAME_VOICE;
+ rtp->f.subclass = AST_FORMAT_ULAW;
+ rtp->f.datalen = 160;
+ rtp->f.samples = 160;
+ memset(rtp->f.data, 0x7f, rtp->f.datalen);
+ f = &rtp->f;
+ break;
+ case AST_FORMAT_ALAW:
+ rtp->f.frametype = AST_FRAME_VOICE;
+ rtp->f.subclass = AST_FORMAT_ALAW;
+ rtp->f.datalen = 160;
+ rtp->f.samples = 160;
+ memset(rtp->f.data, 0x7e, rtp->f.datalen); /* XXX Is this right? XXX */
+ f = &rtp->f;
+ break;
+ case AST_FORMAT_SLINEAR:
+ rtp->f.frametype = AST_FRAME_VOICE;
+ rtp->f.subclass = AST_FORMAT_SLINEAR;
+ rtp->f.datalen = 320;
+ rtp->f.samples = 160;
+ memset(rtp->f.data, 0x00, rtp->f.datalen);
+ f = &rtp->f;
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Don't know how to handle RFC3389 for receive codec %d\n", rtp->lastrxformat);
+ }
+ return f;
+}
+
static struct ast_frame *process_type121(struct ast_rtp *rtp, unsigned char *data, int len)
{
char resp = 0;
@@ -247,6 +294,8 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
} else if (payloadtype == 100) {
/* CISCO's notso proprietary DTMF bridge */
f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
+ } else if (payloadtype == 13) {
+ f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
} else {
ast_log(LOG_NOTICE, "Unknown RTP codec %d received\n", payloadtype);
}
@@ -254,7 +303,8 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
return f;
else
return &null_frame;
- }
+ } else
+ rtp->lastrxformat = rtp->f.subclass;
if (!rtp->lastrxts)
rtp->lastrxts = timestamp;
@@ -651,3 +701,155 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
return 0;
}
+
+void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto)
+{
+ struct ast_rtp_protocol *cur, *prev;
+ cur = protos;
+ prev = NULL;
+ while(cur) {
+ if (cur == proto) {
+ if (prev)
+ prev->next = proto->next;
+ else
+ protos = proto->next;
+ return;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+}
+
+int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
+{
+ struct ast_rtp_protocol *cur;
+ cur = protos;
+ while(cur) {
+ if (cur->type == proto->type) {
+ ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
+ return -1;
+ }
+ cur = cur->next;
+ }
+ proto->next = protos;
+ protos = proto;
+ return 0;
+}
+
+static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
+{
+ struct ast_rtp_protocol *cur;
+ cur = protos;
+ while(cur) {
+ if (cur->type == chan->type) {
+ return cur;
+ }
+ cur = cur->next;
+ }
+ return NULL;
+}
+
+int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
+{
+ struct ast_frame *f;
+ struct ast_channel *who, *cs[3];
+ struct ast_rtp *p0, *p1;
+ struct ast_rtp_protocol *pr0, *pr1;
+ void *pvt0, *pvt1;
+ int to;
+
+ /* XXX Wait a half a second for things to settle up
+ this really should be fixed XXX */
+ ast_autoservice_start(c0);
+ ast_autoservice_start(c1);
+ usleep(500000);
+ ast_autoservice_stop(c0);
+ ast_autoservice_stop(c1);
+
+ /* if need DTMF, cant native bridge */
+ if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
+ return -2;
+ ast_pthread_mutex_lock(&c0->lock);
+ ast_pthread_mutex_lock(&c1->lock);
+ pr0 = get_proto(c0);
+ pr1 = get_proto(c1);
+ if (!pr0) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
+ ast_pthread_mutex_unlock(&c0->lock);
+ ast_pthread_mutex_unlock(&c1->lock);
+ return -1;
+ }
+ if (!pr1) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
+ ast_pthread_mutex_unlock(&c0->lock);
+ ast_pthread_mutex_unlock(&c1->lock);
+ return -1;
+ }
+ pvt0 = c0->pvt->pvt;
+ pvt1 = c1->pvt->pvt;
+ p0 = pr0->get_rtp_info(c0);
+ p1 = pr1->get_rtp_info(c1);
+ if (!p0 || !p1) {
+ /* Somebody doesn't want to play... */
+ ast_pthread_mutex_unlock(&c0->lock);
+ ast_pthread_mutex_unlock(&c1->lock);
+ return -2;
+ }
+ if (pr0->set_rtp_peer(c0, p1))
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
+ if (pr1->set_rtp_peer(c1, p0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
+ ast_pthread_mutex_unlock(&c0->lock);
+ ast_pthread_mutex_unlock(&c1->lock);
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ if ((c0->pvt->pvt != pvt0) ||
+ (c1->pvt->pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
+ ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
+ if (c0->pvt->pvt == pvt0) {
+ if (pr0->set_rtp_peer(c0, NULL))
+ ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
+ }
+ if (c1->pvt->pvt == pvt1) {
+ if (pr1->set_rtp_peer(c1, NULL))
+ ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
+ }
+ /* Tell it to try again later */
+ return -3;
+ }
+ to = -1;
+ who = ast_waitfor_n(cs, 2, &to);
+ if (!who) {
+ ast_log(LOG_DEBUG, "Ooh, empty read...\n");
+ continue;
+ }
+ f = ast_read(who);
+ if (!f || ((f->frametype == AST_FRAME_DTMF) &&
+ (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
+ *fo = f;
+ *rc = who;
+ ast_log(LOG_DEBUG, "Oooh, got a %s\n", f ? "digit" : "hangup");
+ if ((c0->pvt->pvt == pvt0) && (!c0->_softhangup)) {
+ if (pr0->set_rtp_peer(c0, NULL))
+ ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
+ }
+ if ((c1->pvt->pvt == pvt1) && (!c1->_softhangup)) {
+ if (pr1->set_rtp_peer(c1, NULL))
+ ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
+ }
+ /* That's all we needed */
+ return 0;
+ } else
+ ast_frfree(f);
+ /* Swap priority not that it's a big deal at this point */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+
+ }
+ return -1;
+}
diff --git a/sounds.txt b/sounds.txt
index bb08559bf..33f57be4a 100755
--- a/sounds.txt
+++ b/sounds.txt
@@ -14,6 +14,8 @@
%agent-user.gsm%Agent login. Please enter your agent number followed by the pound key.
+%auth-incorrect.gsm%Login incorrect. Please enter your password followed by the pound key.
+
%beep.gsm%(this is a simple beep tone)
%conf-getconfno.gsm%Please enter your conference number followed by the pound key.
diff --git a/sounds/auth-incorrect.gsm b/sounds/auth-incorrect.gsm
new file mode 100755
index 000000000..d8ef6e156
--- /dev/null
+++ b/sounds/auth-incorrect.gsm
Binary files differ