Fix queue URL passing (bug #3543)
[asterisk/asterisk.git] / channels / chan_local.c
index 7567c81..288a418 100755 (executable)
@@ -12,7 +12,6 @@
  */
 
 #include <stdio.h>
-#include <pthread.h>
 #include <string.h>
 #include <asterisk/lock.h>
 #include <asterisk/channel.h>
@@ -49,54 +48,72 @@ static char *tdesc = "Local Proxy Channel Driver";
 static int capability = -1;
 
 static int usecnt =0;
-static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
+AST_MUTEX_DEFINE_STATIC(usecnt_lock);
 
 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
 
 /* Protect the interface list (of sip_pvt's) */
-static pthread_mutex_t locallock = AST_MUTEX_INITIALIZER;
+AST_MUTEX_DEFINE_STATIC(locallock);
 
 static struct local_pvt {
-       pthread_mutex_t lock;                           /* Channel private lock */
+       ast_mutex_t lock;                               /* Channel private lock */
        char context[AST_MAX_EXTENSION];        /* Context to call */
        char exten[AST_MAX_EXTENSION];          /* Extension to call */
        int reqformat;                                          /* Requested format */
        int glaredetect;                                        /* Detect glare on hangup */
        int cancelqueue;                                        /* Cancel queue */
        int alreadymasqed;                                      /* Already masqueraded */
+       int launchedpbx;                                        /* Did we launch the PBX */
+       int nooptimization;
        struct ast_channel *owner;                      /* Master Channel */
        struct ast_channel *chan;                       /* Outbound channel */
        struct local_pvt *next;                         /* Next entity */
 } *locals = NULL;
 
-static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f)
+static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us)
 {
        struct ast_channel *other;
-       if (isoutbound)
+retrylock:             
+       /* Recalculate outbound channel */
+       if (isoutbound) {
                other = p->owner;
-       else
+       } else {
                other = p->chan;
-       if (!other)
-               return 0;
+       }
+       /* Set glare detection */
        p->glaredetect = 1;
-retrylock:             
        if (p->cancelqueue) {
                /* We had a glare on the hangup.  Forget all this business,
                return and destroy p.  */
-               ast_pthread_mutex_unlock(&p->lock);
+               ast_mutex_unlock(&p->lock);
+               ast_mutex_destroy(&p->lock);
                free(p);
                return -1;
        }
-       if (pthread_mutex_trylock(&other->lock)) {
+       if (!other) {
+               p->glaredetect = 0;
+               return 0;
+       }
+       if (ast_mutex_trylock(&other->lock)) {
                /* Failed to lock.  Release main lock and try again */
-               ast_pthread_mutex_unlock(&p->lock);
+               ast_mutex_unlock(&p->lock);
+               if (us) {
+                       if (ast_mutex_unlock(&us->lock)) {
+                               ast_log(LOG_WARNING, "%s wasn't locked while sending %d/%d\n",
+                                       us->name, f->frametype, f->subclass);
+                               us = NULL;
+                       }
+               }
                /* Wait just a bit */
                usleep(1);
-               ast_pthread_mutex_lock(&p->lock);
+               /* Only we can destroy ourselves, so we can't disappear here */
+               if (us)
+                       ast_mutex_lock(&us->lock);
+               ast_mutex_lock(&p->lock);
                goto retrylock;
        }
-       ast_queue_frame(other, f, 0);
-       ast_pthread_mutex_unlock(&other->lock);
+       ast_queue_frame(other, f);
+       ast_mutex_unlock(&other->lock);
        p->glaredetect = 0;
        return 0;
 }
@@ -104,31 +121,47 @@ retrylock:
 static int local_answer(struct ast_channel *ast)
 {
        struct local_pvt *p = ast->pvt->pvt;
-       int isoutbound = IS_OUTBOUND(ast, p);
+       int isoutbound;
        int res = -1;
-       ast_pthread_mutex_lock(&p->lock);
+       ast_mutex_lock(&p->lock);
+       isoutbound = IS_OUTBOUND(ast, p);
        if (isoutbound) {
                /* Pass along answer since somebody answered us */
                struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
-               res = local_queue_frame(p, isoutbound, &answer);
+               res = local_queue_frame(p, isoutbound, &answer, ast);
        } else
                ast_log(LOG_WARNING, "Huh?  Local is being asked to answer?\n");
-       ast_pthread_mutex_unlock(&p->lock);
+       ast_mutex_unlock(&p->lock);
        return res;
 }
 
 static void check_bridge(struct local_pvt *p, int isoutbound)
 {
-       if (p->alreadymasqed)
+       if (p->alreadymasqed || p->nooptimization)
                return;
-       if (isoutbound && p->chan && p->chan->bridge && p->owner) {
+       if (isoutbound && p->chan && p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && p->owner && !p->owner->pvt->readq) {
                /* Masquerade bridged channel into owner */
-               ast_channel_masquerade(p->owner, p->chan->bridge);
-               p->alreadymasqed = 1;
-       } else if (!isoutbound && p->owner && p->owner->bridge && p->chan) {
+               /* Lock everything we need, one by one, and give up if
+                  we can't get everything.  Remember, we'll get another
+                  chance in just a little bit */
+               if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
+                       if (!ast_mutex_trylock(&p->owner->lock)) {
+                               ast_channel_masquerade(p->owner, p->chan->_bridge);
+                               p->alreadymasqed = 1;
+                               ast_mutex_unlock(&p->owner->lock);
+                       }
+                       ast_mutex_unlock(&(p->chan->_bridge)->lock);
+               }
+       } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && !p->chan->pvt->readq) {
                /* Masquerade bridged channel into chan */
-               ast_channel_masquerade(p->chan, p->owner->bridge);
-               p->alreadymasqed = 1;
+               if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
+                       if (!ast_mutex_trylock(&p->chan->lock)) {
+                               ast_channel_masquerade(p->chan, p->owner->_bridge);
+                               p->alreadymasqed = 1;
+                               ast_mutex_unlock(&p->chan->lock);
+                       }
+                       ast_mutex_unlock(&(p->owner->_bridge)->lock);
+               }
        }
 }
 
@@ -142,31 +175,32 @@ static int local_write(struct ast_channel *ast, struct ast_frame *f)
 {
        struct local_pvt *p = ast->pvt->pvt;
        int res = -1;
-       int isoutbound = IS_OUTBOUND(ast, p);
+       int isoutbound;
 
 
        /* Just queue for delivery to the other side */
-       ast_pthread_mutex_lock(&p->lock);
-       res = local_queue_frame(p, isoutbound, f);
+       ast_mutex_lock(&p->lock);
+       isoutbound = IS_OUTBOUND(ast, p);
+       res = local_queue_frame(p, isoutbound, f, ast);
        check_bridge(p, isoutbound);
-       ast_pthread_mutex_unlock(&p->lock);
+       ast_mutex_unlock(&p->lock);
        return res;
 }
 
 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
        struct local_pvt *p = newchan->pvt->pvt;
-       ast_pthread_mutex_lock(&p->lock);
+       ast_mutex_lock(&p->lock);
        if ((p->owner != oldchan) && (p->chan != oldchan)) {
                ast_log(LOG_WARNING, "old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
-               ast_pthread_mutex_unlock(&p->lock);
+               ast_mutex_unlock(&p->lock);
                return -1;
        }
        if (p->owner == oldchan)
                p->owner = newchan;
        else
                p->chan = newchan;      
-       ast_pthread_mutex_unlock(&p->lock);
+       ast_mutex_unlock(&p->lock);
        return 0;
 }
 
@@ -175,12 +209,13 @@ static int local_indicate(struct ast_channel *ast, int condition)
        struct local_pvt *p = ast->pvt->pvt;
        int res = -1;
        struct ast_frame f = { AST_FRAME_CONTROL, };
-       int isoutbound = IS_OUTBOUND(ast, p);
+       int isoutbound;
        /* Queue up a frame representing the indication as a control frame */
-       ast_pthread_mutex_lock(&p->lock);
+       ast_mutex_lock(&p->lock);
+       isoutbound = IS_OUTBOUND(ast, p);
        f.subclass = condition;
-       res = local_queue_frame(p, isoutbound, &f);
-       ast_pthread_mutex_unlock(&p->lock);
+       res = local_queue_frame(p, isoutbound, &f, ast);
+       ast_mutex_unlock(&p->lock);
        return res;
 }
 
@@ -189,34 +224,73 @@ static int local_digit(struct ast_channel *ast, char digit)
        struct local_pvt *p = ast->pvt->pvt;
        int res = -1;
        struct ast_frame f = { AST_FRAME_DTMF, };
-       int isoutbound = IS_OUTBOUND(ast, p);
-       ast_pthread_mutex_lock(&p->lock);
+       int isoutbound;
+       ast_mutex_lock(&p->lock);
+       isoutbound = IS_OUTBOUND(ast, p);
        f.subclass = digit;
-       res = local_queue_frame(p, isoutbound, &f);
-       ast_pthread_mutex_unlock(&p->lock);
+       res = local_queue_frame(p, isoutbound, &f, ast);
+       ast_mutex_unlock(&p->lock);
        return res;
 }
 
-static int local_call(struct ast_channel *ast, char *dest, int timeout)
+static int local_sendhtml(struct ast_channel *ast, int subclass, char *data, int datalen)
 {
        struct local_pvt *p = ast->pvt->pvt;
+       int res = -1;
+       struct ast_frame f = { AST_FRAME_HTML, };
+       int isoutbound;
+       ast_mutex_lock(&p->lock);
+       isoutbound = IS_OUTBOUND(ast, p);
+       f.subclass = subclass;
+       f.data = data;
+       f.datalen = datalen;
+       res = local_queue_frame(p, isoutbound, &f, ast);
+       ast_mutex_unlock(&p->lock);
+       return res;
+}
 
-       if (p->owner->callerid)
-               p->chan->callerid = strdup(p->owner->callerid);
+static int local_call(struct ast_channel *ast, char *dest, int timeout)
+{
+       struct local_pvt *p = ast->pvt->pvt;
+       int res;
+       
+       ast_mutex_lock(&p->lock);
+       if (p->owner->cid.cid_num)
+               p->chan->cid.cid_num = strdup(p->owner->cid.cid_num);
+       else 
+               p->chan->cid.cid_num = NULL;
+
+       if (p->owner->cid.cid_name)
+               p->chan->cid.cid_name = strdup(p->owner->cid.cid_name);
+       else 
+               p->chan->cid.cid_name = NULL;
+
+       if (p->owner->cid.cid_rdnis)
+               p->chan->cid.cid_rdnis = strdup(p->owner->cid.cid_rdnis);
        else
-               p->chan->callerid = NULL;
-       if (p->owner->ani)
-               p->chan->ani = strdup(p->owner->ani);
+               p->chan->cid.cid_rdnis = NULL;
+
+       if (p->owner->cid.cid_ani)
+               p->chan->cid.cid_ani = strdup(p->owner->cid.cid_ani);
        else
-               p->chan->ani = NULL;
+               p->chan->cid.cid_ani = NULL;
+
+       strncpy(p->chan->language, p->owner->language, sizeof(p->chan->language) - 1);
+       strncpy(p->chan->accountcode, p->owner->accountcode, sizeof(p->chan->accountcode) - 1);
+       p->chan->cdrflags = p->owner->cdrflags;
+       
+       p->launchedpbx = 1;
        /* Start switch on sub channel */
-       return ast_pbx_start(p->chan);
+       res = ast_pbx_start(p->chan);
+       ast_mutex_unlock(&p->lock);
+       return res;
 }
 
+/*
 static void local_destroy(struct local_pvt *p)
 {
        struct local_pvt *cur, *prev = NULL;
-       ast_pthread_mutex_lock(&locallock);
+       ast_mutex_lock(&locallock);
        cur = locals;
        while(cur) {
                if (cur == p) {
@@ -224,32 +298,40 @@ static void local_destroy(struct local_pvt *p)
                                prev->next = cur->next;
                        else
                                locals = cur->next;
+                       ast_mutex_destroy(cur);
                        free(cur);
                        break;
                }
                prev = cur;
                cur = cur->next;
        }
-       ast_pthread_mutex_unlock(&locallock);
+       ast_mutex_unlock(&locallock);
        if (!cur)
                ast_log(LOG_WARNING, "Unable ot find local '%s@%s' in local list\n", p->exten, p->context);
 }
+*/
 
 static int local_hangup(struct ast_channel *ast)
 {
        struct local_pvt *p = ast->pvt->pvt;
-       int isoutbound = IS_OUTBOUND(ast, p);
+       int isoutbound;
        struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
        struct local_pvt *cur, *prev=NULL;
        struct ast_channel *ochan = NULL;
        int glaredetect;
-       ast_pthread_mutex_lock(&p->lock);
-       if (isoutbound)
+       ast_mutex_lock(&p->lock);
+       isoutbound = IS_OUTBOUND(ast, p);
+       if (isoutbound) {
                p->chan = NULL;
-       else
+               p->launchedpbx = 0;
+       } else
                p->owner = NULL;
        ast->pvt->pvt = NULL;
        
+       ast_mutex_lock(&usecnt_lock);
+       usecnt--;
+       ast_mutex_unlock(&usecnt_lock);
+       
        if (!p->owner && !p->chan) {
                /* Okay, done with the private part now, too. */
                glaredetect = p->glaredetect;
@@ -257,9 +339,9 @@ static int local_hangup(struct ast_channel *ast)
                   let local_queue do it. */
                if (p->glaredetect)
                        p->cancelqueue = 1;
-               ast_pthread_mutex_unlock(&p->lock);
+               ast_mutex_unlock(&p->lock);
                /* Remove from list */
-               ast_pthread_mutex_lock(&locallock);
+               ast_mutex_lock(&locallock);
                cur = locals;
                while(cur) {
                        if (cur == p) {
@@ -272,18 +354,23 @@ static int local_hangup(struct ast_channel *ast)
                        prev = cur;
                        cur = cur->next;
                }
-               ast_pthread_mutex_unlock(&locallock);
+               ast_mutex_unlock(&locallock);
+               /* Grab / release lock just in case */
+               ast_mutex_lock(&p->lock);
+               ast_mutex_unlock(&p->lock);
                /* And destroy */
-               if (!glaredetect)
+               if (!glaredetect) {
+                       ast_mutex_destroy(&p->lock);
                        free(p);
+               }
                return 0;
        }
-       if (p->chan && !p->chan->pbx)
+       if (p->chan && !p->launchedpbx)
                /* Need to actually hangup since there is no PBX */
                ochan = p->chan;
        else
-               local_queue_frame(p, isoutbound, &f);
-       ast_pthread_mutex_unlock(&p->lock);
+               local_queue_frame(p, isoutbound, &f, NULL);
+       ast_mutex_unlock(&p->lock);
        if (ochan)
                ast_hangup(ochan);
        return 0;
@@ -293,11 +380,19 @@ static struct local_pvt *local_alloc(char *data, int format)
 {
        struct local_pvt *tmp;
        char *c;
+       char *opts;
        tmp = malloc(sizeof(struct local_pvt));
        if (tmp) {
                memset(tmp, 0, sizeof(struct local_pvt));
-               ast_pthread_mutex_init(&tmp->lock);
+               ast_mutex_init(&tmp->lock);
                strncpy(tmp->exten, data, sizeof(tmp->exten) - 1);
+               opts = strchr(tmp->exten, '/');
+               if (opts) {
+                       *opts='\0';
+                       opts++;
+                       if (strchr(opts, 'n'))
+                               tmp->nooptimization = 1;
+               }
                c = strchr(tmp->exten, '@');
                if (c) {
                        *c = '\0';
@@ -307,15 +402,16 @@ static struct local_pvt *local_alloc(char *data, int format)
                        strncpy(tmp->context, "default", sizeof(tmp->context) - 1);
                tmp->reqformat = format;
                if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
-                       ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->context, tmp->exten);
+                       ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
+                       ast_mutex_destroy(&tmp->lock);
                        free(tmp);
                        tmp = NULL;
                } else {
                        /* Add to list */
-                       ast_pthread_mutex_lock(&locallock);
+                       ast_mutex_lock(&locallock);
                        tmp->next = locals;
                        locals = tmp;
-                       ast_pthread_mutex_unlock(&locallock);
+                       ast_mutex_unlock(&locallock);
                }
                
        }
@@ -338,7 +434,7 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
        if (tmp) {
                tmp->nativeformats = p->reqformat;
                tmp2->nativeformats = p->reqformat;
-               snprintf(tmp->name, sizeof(tmp->name), "Local/%s@%s-%04x.1", p->exten, p->context, randnum);
+               snprintf(tmp->name, sizeof(tmp->name), "Local/%s@%s-%04x,1", p->exten, p->context, randnum);
                snprintf(tmp2->name, sizeof(tmp2->name), "Local/%s@%s-%04x,2", p->exten, p->context, randnum);
                tmp->type = type;
                tmp2->type = type;
@@ -356,6 +452,8 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
                tmp2->pvt->pvt = p;
                tmp->pvt->send_digit = local_digit;
                tmp2->pvt->send_digit = local_digit;
+               tmp->pvt->send_html = local_sendhtml;
+               tmp2->pvt->send_html = local_sendhtml;
                tmp->pvt->call = local_call;
                tmp2->pvt->call = local_call;
                tmp->pvt->hangup = local_hangup;
@@ -374,9 +472,10 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
                tmp2->pvt->fixup = local_fixup;
                p->owner = tmp;
                p->chan = tmp2;
-               ast_pthread_mutex_lock(&usecnt_lock);
+               ast_mutex_lock(&usecnt_lock);
+               usecnt++;
                usecnt++;
-               ast_pthread_mutex_unlock(&usecnt_lock);
+               ast_mutex_unlock(&usecnt_lock);
                ast_update_use_count();
                strncpy(tmp->context, p->context, sizeof(tmp->context)-1);
                strncpy(tmp2->context, p->context, sizeof(tmp2->context)-1);
@@ -389,7 +488,7 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
 }
 
 
-static struct ast_channel *local_request(char *type, int format, void *data)
+static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
 {
        struct local_pvt *p;
        struct ast_channel *chan = NULL;
@@ -403,28 +502,28 @@ static int locals_show(int fd, int argc, char **argv)
 {
        struct local_pvt *p;
 
-       if (argc != 2)
+       if (argc != 3)
                return RESULT_SHOWUSAGE;
-       ast_pthread_mutex_lock(&locallock);
+       ast_mutex_lock(&locallock);
        p = locals;
        while(p) {
-               ast_pthread_mutex_lock(&p->lock);
+               ast_mutex_lock(&p->lock);
                ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
-               ast_pthread_mutex_unlock(&p->lock);
+               ast_mutex_unlock(&p->lock);
                p = p->next;
        }
        if (!locals)
                ast_cli(fd, "No local channels in use\n");
-       ast_pthread_mutex_unlock(&locallock);
+       ast_mutex_unlock(&locallock);
        return RESULT_SUCCESS;
 }
 
 static char show_locals_usage[] = 
-"Usage: show locals\n"
-"       Provides summary information on locals.\n";
+"Usage: local show channels\n"
+"       Provides summary information on local channels.\n";
 
 static struct ast_cli_entry cli_show_locals = {
-       { "show", "locals", NULL }, locals_show, 
+       { "local", "show", "channels", NULL }, locals_show, 
        "Show status of local channels", show_locals_usage, NULL };
 
 int load_module()
@@ -449,7 +548,7 @@ int unload_module()
        /* First, take us out of the channel loop */
        ast_cli_unregister(&cli_show_locals);
        ast_channel_unregister(type);
-       if (!ast_pthread_mutex_lock(&locallock)) {
+       if (!ast_mutex_lock(&locallock)) {
                /* Hangup all interfaces if they have an owner */
                p = locals;
                while(p) {
@@ -458,7 +557,7 @@ int unload_module()
                        p = p->next;
                }
                locals = NULL;
-               ast_pthread_mutex_unlock(&locallock);
+               ast_mutex_unlock(&locallock);
        } else {
                ast_log(LOG_WARNING, "Unable to lock the monitor\n");
                return -1;
@@ -469,9 +568,9 @@ int unload_module()
 int usecount()
 {
        int res;
-       ast_pthread_mutex_lock(&usecnt_lock);
+       ast_mutex_lock(&usecnt_lock);
        res = usecnt;
-       ast_pthread_mutex_unlock(&usecnt_lock);
+       ast_mutex_unlock(&usecnt_lock);
        return res;
 }