Sun Feb 16 07:00:01 CET 2003
authorMatteo Brancaleoni <mbrancaleoni@espia.it>
Sun, 16 Feb 2003 06:00:12 +0000 (06:00 +0000)
committerMatteo Brancaleoni <mbrancaleoni@espia.it>
Sun, 16 Feb 2003 06:00:12 +0000 (06:00 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@616 65c4cc65-6c06-0410-ace0-fbb531ad65f3

12 files changed:
CHANGES
Makefile
apps/Makefile
apps/app_authenticate.c [new file with mode: 0755]
channels/chan_mgcp.c
channels/chan_sip.c
configs/sip.conf.sample
frame.c
include/asterisk/rtp.h
rtp.c
sounds.txt
sounds/auth-incorrect.gsm [new file with mode: 0755]

diff --git a/CHANGES b/CHANGES
index 55cd0d7..d190dd4 100755 (executable)
--- 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
index 1d2bb99..d62c6a0 100755 (executable)
--- 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 \
index 95f7275..1d3ad29 100755 (executable)
@@ -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 (executable)
index 0000000..69d2443
--- /dev/null
@@ -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;
+}
index 88a0d2c..08ef814 100755 (executable)
@@ -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();
index b9f235c..a057eb8 100755 (executable)
@@ -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>
 /* #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");
index ed0ed6f..dad7c15 100755 (executable)
@@ -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 e37e816..fb255d8 100755 (executable)
--- 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;
index 8137eb3..30639a5 100755 (executable)
@@ -17,6 +17,7 @@
 #include <asterisk/frame.h>
 #include <asterisk/io.h>
 #include <asterisk/sched.h>
+#include <asterisk/channel.h>
 
 #include <netinet/in.h>
 
 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 21bee69..ca5d7a1 100755 (executable)
--- 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;
+}
index bb08559..33f57be 100755 (executable)
@@ -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 (executable)
index 0000000..d8ef6e1
Binary files /dev/null and b/sounds/auth-incorrect.gsm differ