Try immediately logged in agents first, then callbacklogin ones
[asterisk/asterisk.git] / channels / chan_agent.c
index e6c82d0..466181c 100755 (executable)
@@ -76,6 +76,9 @@ static char moh[80] = "default";
 static int capability = -1;
 
 static unsigned int group;
+static int autologoff;
+static int wrapuptime;
+static int ackcall;
 
 static int usecnt =0;
 static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
@@ -88,6 +91,11 @@ static struct agent_pvt {
        int dead;                                                       /* Poised for destruction? */
        int pending;                                            /* Not a real agent -- just pending a match */
        int abouttograb;                                        /* About to grab */
+       int autologoff;                                 /* Auto timeout time */
+       int ackcall;                                    /* ackcall */
+       time_t start;                                           /* When call started */
+       struct timeval lastdisc;                        /* When last disconnected */
+       int wrapuptime;                                         /* Wrapup time in ms */
        unsigned int group;                                     /* Group memberships */
        int acknowledged;                                       /* Acknowledged */
        char moh[80];                                           /* Which music on hold */
@@ -179,6 +187,9 @@ static struct agent_pvt *add_agent(char *agent, int pending)
        strncpy(p->password, password ? password : "", sizeof(p->password) - 1);
        strncpy(p->name, name ? name : "", sizeof(p->name) - 1);
        strncpy(p->moh, moh, sizeof(p->moh) - 1);
+       p->ackcall = ackcall;
+       p->autologoff = autologoff;
+       p->wrapuptime = wrapuptime;
        if (pending)
                p->dead = 1;
        else
@@ -221,17 +232,32 @@ static struct ast_frame  *agent_read(struct ast_channel *ast)
        else
                f = &null_frame;
        if (!f) {
-               /* If there's a channel, make it NULL */
-               if (p->chan)
+               /* If there's a channel, hang it up  (if it's on a callback) make it NULL */
+               if (p->chan) {
+                       if (strlen(p->loginchan))
+                               ast_hangup(p->chan);
                        p->chan = NULL;
+                       p->acknowledged = 0;
+               }
        }
        if (f && (f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) {
+/* TC */
+       if (p->ackcall) {
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
                /* Don't pass answer along */
                ast_frfree(f);
                f = &null_frame;
+        }
+        else {
+               p->acknowledged = 1;
+               f = &answer_frame;
+        }
        }
        if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
                if (!p->acknowledged) {
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
                        p->acknowledged = 1;
                        ast_frfree(f);
                        f = &answer_frame;
@@ -318,6 +344,7 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout)
                ast_pthread_mutex_unlock(&p->lock);
                return res;
        } else if (strlen(p->loginchan)) {
+               time(&p->start);
                /* Call on this agent */
                if (option_verbose > 2)
                        ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
@@ -363,18 +390,28 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout)
 static int agent_hangup(struct ast_channel *ast)
 {
        struct agent_pvt *p = ast->pvt->pvt;
+       int howlong = 0;
        ast_pthread_mutex_lock(&p->lock);
        p->owner = NULL;
        ast->pvt->pvt = NULL;
        p->app_sleep_cond = 1;
+       if (p->start && (ast->_state != AST_STATE_UP))
+               howlong = time(NULL) - p->start;
+       time(&p->start);
        if (p->chan) {
                /* If they're dead, go ahead and hang up on the agent now */
                if (strlen(p->loginchan)) {
+                       p->acknowledged = 0;
                        if (p->chan) {
                                /* Recognize the hangup and pass it along immediately */
                                ast_hangup(p->chan);
                                p->chan = NULL;
                        }
+                       ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
+                       if (howlong  && p->autologoff && (howlong > p->autologoff)) {
+                               ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
+                               strcpy(p->loginchan, "");
+                       }
                } else if (p->dead) {
                        ast_pthread_mutex_lock(&p->chan->lock);
                        ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
@@ -418,7 +455,9 @@ static int agent_hangup(struct ast_channel *ast)
        } else if (p->chan) {
                /* Not dead -- check availability now */
                ast_pthread_mutex_lock(&p->lock);
-               check_availability(p, 1);
+               /* check_availability(p, 1); */
+               /* Store last disconnect time */
+               gettimeofday(&p->lastdisc, NULL);
                ast_pthread_mutex_unlock(&p->lock);
        }
        return 0;
@@ -427,12 +466,19 @@ static int agent_hangup(struct ast_channel *ast)
 static int agent_cont_sleep( void *data )
 {
        struct agent_pvt *p;
+       struct timeval tv;
        int res;
 
        p = (struct agent_pvt *)data;
 
        ast_pthread_mutex_lock(&p->lock);
        res = p->app_sleep_cond;
+       if (p->lastdisc.tv_sec) {
+               gettimeofday(&tv, NULL);
+               if ((tv.tv_sec - p->lastdisc.tv_sec) * 1000 + 
+                       (tv.tv_usec - p->lastdisc.tv_usec) / 1000 > p->wrapuptime) 
+                       res = 1;
+       }
        ast_pthread_mutex_unlock(&p->lock);
 #if 0
        if( !res )
@@ -538,6 +584,9 @@ static int read_agent_config(void)
        struct ast_variable *v;
        struct agent_pvt *p, *pl, *pn;
        group = 0;
+       autologoff = 0;
+       wrapuptime = 0;
+       ackcall = 1;
        cfg = ast_load(config);
        if (!cfg) {
                ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
@@ -557,6 +606,16 @@ static int read_agent_config(void)
                        add_agent(v->value, 0);
                } else if (!strcasecmp(v->name, "group")) {
                        group = ast_get_group(v->value);
+               } else if (!strcasecmp(v->name, "autologoff")) {
+                       autologoff = atoi(v->value);
+                       if (autologoff < 0)
+                               autologoff = 0;
+               } else if (!strcasecmp(v->name, "ackcall")) {
+                        ackcall = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "wrapuptime")) {
+                       wrapuptime = atoi(v->value);
+                       if (wrapuptime < 0)
+                               wrapuptime = 0;
                } else if (!strcasecmp(v->name, "musiconhold")) {
                        strncpy(moh, v->value, sizeof(moh) - 1);
                }
@@ -605,7 +664,7 @@ static int check_availability(struct agent_pvt *newlyavailable, int needlock)
                        continue;
                }
                ast_pthread_mutex_lock(&p->lock);
-               if (p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
+               if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
                        ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
                        /* We found a pending call, time to merge */
                        chan = agent_new(newlyavailable, AST_STATE_DOWN);
@@ -634,8 +693,10 @@ static int check_availability(struct agent_pvt *newlyavailable, int needlock)
                                ast_setstate(chan, AST_STATE_UP);
                                /* Go ahead and mark the channel as a zombie so that masquerade will
                                   destroy it for us, and we need not call ast_hangup */
+                               ast_pthread_mutex_lock(&parent->lock);
                                chan->zombie = 1;
                                ast_channel_masquerade(parent, chan);
+                               ast_pthread_mutex_unlock(&parent->lock);
                                p->abouttograb = 0;
                        } else {
                                ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
@@ -665,12 +726,15 @@ static struct ast_channel *agent_request(char *type, int format, void *data)
        } else {
                groupmatch = 0;
        }
+
+       /* Check actual logged in agents first */
        ast_pthread_mutex_lock(&agentlock);
        p = agents;
        while(p) {
                ast_pthread_mutex_lock(&p->lock);
-               if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
-                       /* Agent must be registered, but not have any active call */
+               if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
+                               !p->lastdisc.tv_sec && !strlen(p->loginchan)) {
+                       /* Agent must be registered, but not have any active call, and not be in a waiting state */
                        if (!p->owner && p->chan) {
                                /* Fixed agent */
                                chan = agent_new(p, AST_STATE_DOWN);
@@ -688,6 +752,32 @@ static struct ast_channel *agent_request(char *type, int format, void *data)
                ast_pthread_mutex_unlock(&p->lock);
                p = p->next;
        }
+       if (!p) {
+               p = agents;
+               while(p) {
+                       ast_pthread_mutex_lock(&p->lock);
+                       if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
+                                       !p->lastdisc.tv_sec) {
+                               /* Agent must be registered, but not have any active call, and not be in a waiting state */
+                               if (!p->owner && p->chan) {
+                                       /* Fixed agent */
+                                       chan = agent_new(p, AST_STATE_DOWN);
+                               } else if (!p->owner && strlen(p->loginchan)) {
+                                       /* Adjustable agent */
+                                       p->chan = ast_request("Local", format, p->loginchan);
+                                       if (p->chan)
+                                               chan = agent_new(p, AST_STATE_DOWN);
+                               }
+                               if (chan) {
+                                       ast_pthread_mutex_unlock(&p->lock);
+                                       break;
+                               }
+                       }
+                       ast_pthread_mutex_unlock(&p->lock);
+                       p = p->next;
+               }
+       }
+
        if (!chan && waitforagent) {
                /* No agent available -- but we're requesting to wait for one.
                   Allocate a place holder */
@@ -718,6 +808,7 @@ static int agents_show(int fd, int argc, char **argv)
        char username[256];
        char location[256];
        char talkingto[256];
+       char moh[256];
 
        if (argc != 2)
                return RESULT_SHOWUSAGE;
@@ -751,8 +842,10 @@ static int agents_show(int fd, int argc, char **argv)
                                strcpy(location, "not logged in");
                                strcpy(talkingto, "");
                        }
-                       ast_cli(fd, "%-12.12s %s%s%s\n", p->agent, 
-                                       username, location, talkingto);
+                       if (strlen(p->moh))
+                               snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
+                       ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, 
+                                       username, location, talkingto, moh);
                }
                ast_pthread_mutex_unlock(&p->lock);
                p = p->next;
@@ -778,6 +871,7 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
        int tries = 0;
        struct agent_pvt *p;
        struct localuser *u;
+       struct timeval tv;
        char user[AST_MAX_AGENT];
        char pass[AST_MAX_AGENT];
        char xpass[AST_MAX_AGENT] = "";
@@ -785,6 +879,7 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
        char info[512];
        char *opt_user = NULL;
        char *options = NULL;
+       char *context = NULL;
        int play_announcement;
        char *filename = "agent-loginok";
        
@@ -798,6 +893,13 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
                if (options) {
                        *options = '\0';
                        options++;
+                       if (callbackmode) {
+                               context = strchr(options, '@');
+                               if (context) {
+                                       *context = '\0';
+                                       context++;
+                               }
+                       }
                }
        }
 
@@ -844,7 +946,10 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
                                                        /* Retrieve login chan */
                                                        res = ast_app_getdata(chan, "agent-newlocation", tmpchan, sizeof(tmpchan) - 1, 0);
                                                        if (!res) {
-                                                               strncpy(p->loginchan, tmpchan, sizeof(p->loginchan) - 1);
+                                                               if (context && strlen(context) && strlen(tmpchan))
+                                                                       snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
+                                                               else
+                                                                       strncpy(p->loginchan, tmpchan, sizeof(p->loginchan) - 1);
                                                                if (!strlen(p->loginchan))
                                                                        filename = "agent-loggedoff";
                                                                p->acknowledged = 0;
@@ -857,7 +962,7 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
                                                if( options )
                                                        if( strchr( options, 's' ) )
                                                                play_announcement = 0;
-                                               if( play_announcement )
+                                               if( !res && play_announcement )
                                                        res = ast_streamfile(chan, filename, chan->language);
                                                if (!res)
                                                        ast_waitstream(chan, "");
@@ -914,6 +1019,17 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
                                                                if (res)
                                                                        break;
 
+                                                               ast_pthread_mutex_lock(&p->lock);
+                                                               if (p->lastdisc.tv_sec) {
+                                                                       gettimeofday(&tv, NULL);
+                                                                       if ((tv.tv_sec - p->lastdisc.tv_sec) * 1000 + 
+                                                                               (tv.tv_usec - p->lastdisc.tv_usec) / 1000 > p->wrapuptime) {
+                                                                                       ast_log(LOG_DEBUG, "Wrapup time expired!\n");
+                                                                               memset(&p->lastdisc, 0, sizeof(p->lastdisc));
+                                                                               check_availability(p, 1);
+                                                                       }
+                                                               }
+                                                               ast_pthread_mutex_unlock(&p->lock);
                                                                /*      Synchronize channel ownership between call to agent and itself. */
                                                                pthread_mutex_lock( &p->app_lock );
                                                                ast_pthread_mutex_lock(&p->lock);