From 175c755fefa75d02deeff70541cd7821ce670678 Mon Sep 17 00:00:00 2001 From: matteo Date: Sun, 16 Feb 2003 06:00:12 +0000 Subject: Sun Feb 16 07:00:01 CET 2003 git-svn-id: http://svn.digium.com/svn/asterisk/trunk@616 f38db490-d61c-443f-a65b-d21fe96a405b --- CHANGES | 6 ++ Makefile | 2 +- apps/Makefile | 3 +- apps/app_authenticate.c | 154 +++++++++++++++++++++++++++++++ channels/chan_mgcp.c | 61 ++++++++++++- channels/chan_sip.c | 227 +++++++++++++++++++++++++++++++++++++--------- configs/sip.conf.sample | 2 + frame.c | 33 +++++++ include/asterisk/rtp.h | 15 +++ rtp.c | 204 ++++++++++++++++++++++++++++++++++++++++- sounds.txt | 2 + sounds/auth-incorrect.gsm | Bin 0 -> 7524 bytes 12 files changed, 660 insertions(+), 49 deletions(-) create mode 100755 apps/app_authenticate.c create mode 100755 sounds/auth-incorrect.gsm 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 + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +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 #include #include +#include #include #include #include @@ -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(®->addr.sin_addr, hp->h_addr, sizeof(®->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 #include #include +#include #include @@ -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 #include #include +#include #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 Binary files /dev/null and b/sounds/auth-incorrect.gsm differ -- cgit v1.2.3