Index: asterisk/channels/chan_agent.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_agent.c,v retrieving revision 1.77 diff -u -r1.77 chan_agent.c --- asterisk/channels/chan_agent.c 28 Jun 2004 18:40:41 -0000 1.77 +++ asterisk/channels/chan_agent.c 29 Jun 2004 05:51:50 -0000 @@ -63,13 +63,15 @@ "when a new call comes in. The agent can dump the call by pressing\n" "the star key.\n" "The option string may contain zero or more of the following characters:\n" -" 's' -- silent login - do not announce the login ok segment\n"; +" 's' -- silent login - do not announce the login ok segment after agent logged in/off\n"; static char *descrip2 = " AgentCallbackLogin([AgentNo][|[options][exten]@context]):\n" -"Asks the agent to login to the system with callback. Always returns -1.\n" +"Asks the agent to login to the system with callback.\n" "The agent's callback extension is called (optionally with the specified\n" -"context. \n"; +"context. \n" +"The option string may contain zero or more of the following characters:\n" +" 's' -- silent login - do not announce the login ok segment agent logged in/off\n"; static char *descrip3 = " AgentMonitorOutgoing([options]):\n" @@ -81,6 +83,7 @@ #define AST_MAX_AGENT 80 /* Agent ID or Password max length */ #define AST_MAX_BUF 256 +#define AST_MAX_FILENAME_LEN 256 static int capability = -1; @@ -89,6 +92,9 @@ static int wrapuptime; static int ackcall; +static int maxlogintries = 3; +static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye"; + static int usecnt =0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); @@ -256,7 +262,7 @@ ast_mutex_destroy(&p->lock); ast_mutex_destroy(&p->app_lock); free(p); - } + } return 0; } @@ -789,44 +795,53 @@ v = ast_variable_browse(cfg, "agents"); while(v) { /* Create the interface list */ - if (!strcasecmp(v->name, "agent")) { + if (!strcasecmp(v->name, "agent") && !ast_strlen_zero(v->value)) { add_agent(v->value, 0); - } else if (!strcasecmp(v->name, "group")) { + } else if (!strcasecmp(v->name, "group") && !ast_strlen_zero(v->value)) { group = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "autologoff")) { + } else if (!strcasecmp(v->name, "autologoff") && !ast_strlen_zero(v->value)) { autologoff = atoi(v->value); if (autologoff < 0) autologoff = 0; - } else if (!strcasecmp(v->name, "ackcall")) { + } else if (!strcasecmp(v->name, "ackcall") && !ast_strlen_zero(v->value)) { if (!strcasecmp(v->value, "always")) ackcall = 2; else if (ast_true(v->value)) - ackcall = 1; + ackcall = 1; else ackcall = 0; - } else if (!strcasecmp(v->name, "wrapuptime")) { + } else if (!strcasecmp(v->name, "wrapuptime") && !ast_strlen_zero(v->value)) { wrapuptime = atoi(v->value); if (wrapuptime < 0) wrapuptime = 0; - } else if (!strcasecmp(v->name, "musiconhold")) { + } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) { + maxlogintries = atoi(v->value); + if (maxlogintries < 0) + maxlogintries = 0; + } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) { + strcpy(agentgoodbye,v->value); + } else if (!strcasecmp(v->name, "musiconhold") && !ast_strlen_zero(v->value)) { strncpy(moh, v->value, sizeof(moh) - 1); - } else if (!strcasecmp(v->name, "updatecdr")) { - updatecdr = ast_true(v->value); - } else if (!strcasecmp(v->name, "recordagentcalls")) { + } else if (!strcasecmp(v->name, "updatecdr") && !ast_strlen_zero(v->value)) { + if (ast_true(v->value)) + updatecdr = 1; + else + updatecdr = 0; + } else if (!strcasecmp(v->name, "recordagentcalls") && !ast_strlen_zero(v->value)) { recordagentcalls = ast_true(v->value); - } else if (!strcasecmp(v->name, "createlink")) { + } else if (!strcasecmp(v->name, "createlink") && !ast_strlen_zero(v->value)) { createlink = ast_true(v->value); - } else if (!strcasecmp(v->name, "recordformat")) { + } else if (!strcasecmp(v->name, "recordformat") && !ast_strlen_zero(v->value)) { strncpy(recordformat, v->value, sizeof(recordformat) - 1); if (!strcasecmp(v->value, "wav49")) strcpy(recordformatext, "WAV"); else strncpy(recordformatext, v->value, sizeof(recordformat) - 1); - } else if (!strcasecmp(v->name, "urlprefix")) { + } else if (!strcasecmp(v->name, "urlprefix") && !ast_strlen_zero(v->value)) { strncpy(urlprefix, v->value, sizeof(urlprefix) - 2); if (urlprefix[strlen(urlprefix) - 1] != '/') strcat(urlprefix, "/"); - } else if (!strcasecmp(v->name, "savecallsin")) { + } else if (!strcasecmp(v->name, "savecallsin") && !ast_strlen_zero(v->value)) { if (v->value[0] == '/') strncpy(savecallsin, v->value, sizeof(savecallsin) - 2); else @@ -1139,9 +1154,11 @@ { int res=0; int tries = 0; + int max_login_tries = maxlogintries; struct agent_pvt *p; struct localuser *u; struct timeval tv; + int login_state = 0; char user[AST_MAX_AGENT]; char pass[AST_MAX_AGENT]; char agent[AST_MAX_AGENT] = ""; @@ -1150,9 +1167,15 @@ char info[512]; char *opt_user = NULL; char *options = NULL; + char option; + char badoption[2]; + char *tmpoptions = NULL; char *context = NULL; char *exten = NULL; - int play_announcement; + int play_announcement = 1; + char agent_goodbye[AST_MAX_FILENAME_LEN]; + strcpy(agent_goodbye, agentgoodbye); + int update_cdr = updatecdr; char *filename = "agent-loginok"; LOCAL_USER_ADD(u); @@ -1160,6 +1183,29 @@ /* Parse the arguments XXX Check for failure XXX */ strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1); opt_user = info; + /* Set Channel Specific Login Overrides */ + if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) { + max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES")); + if (max_login_tries < 0) + max_login_tries = 0; + tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"); + ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name); + } + if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && strlen(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) { + if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) + update_cdr = 1; + else + update_cdr = 0; + tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"); + ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name); + } + if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && strlen(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) { + strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE")); + tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"); + ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name); + } + /* End Channel Specific Login Overrides */ + /* Read command line options */ if( opt_user ) { options = strchr(opt_user, '|'); if (options) { @@ -1177,7 +1223,22 @@ exten = NULL; } } + if( options ) { + while(*options) { + option = (char)options[0]; + if (option=='s') + play_announcement = 0; + else { + badoption[0] = option; + badoption[1] = '\0'; + tmpoptions=badoption; + ast_verbose(VERBOSE_PREFIX_3 "Warning: option %s is unknown.\n",tmpoptions); + } + options++; + } + } } + /* End command line options */ if (chan->_state != AST_STATE_UP) res = ast_answer(chan); @@ -1187,7 +1248,8 @@ else res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0); } - while (!res && (tries < 3)) { + while (!res && (max_login_tries==0 || tries < max_login_tries)) { + tries++; /* Check for password */ ast_mutex_lock(&agentlock); p = agents; @@ -1216,6 +1278,33 @@ ast_mutex_lock(&p->lock); if (!strcmp(p->agent, user) && !strcmp(p->password, pass) && !p->pending) { + login_state = 1; /* Successful Login */ + /* Set Channel Specific Agent Overides */ + if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { + if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always")) + p->ackcall = 2; + else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) + p->ackcall = 1; + else + p->ackcall = 0; + tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL"); + ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent); + } + if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) { + p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF")); + if (p->autologoff < 0) + p->autologoff = 0; + tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"); + ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent); + } + if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) { + p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME")); + if (p->wrapuptime < 0) + p->wrapuptime = 0; + tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"); + ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent); + } + /* End Channel Specific Agent Overides */ if (!p->chan) { char last_loginchan[80] = ""; long logintime; @@ -1252,15 +1341,18 @@ } } } + exten = tmpchan; if (!res) { if (context && !ast_strlen_zero(context) && !ast_strlen_zero(tmpchan)) snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context); else { - strncpy(last_loginchan, p->loginchan, sizeof(last_loginchan) - 1); + strncpy(last_loginchan, p->loginchan, sizeof(last_loginchan) - 1); strncpy(p->loginchan, tmpchan, sizeof(p->loginchan) - 1); } - if (ast_strlen_zero(p->loginchan)) + if (ast_strlen_zero(p->loginchan)) { + login_state = 2; filename = "agent-loggedoff"; + } p->acknowledged = 0; /* store/clear the global variable that stores agentid based on the callerid */ if (chan->callerid) { @@ -1271,7 +1363,7 @@ else pbx_builtin_setvar_helper(NULL, agentvar, p->agent); } - if(updatecdr && chan->cdr) + if(update_cdr && chan->cdr) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); } @@ -1279,13 +1371,10 @@ strcpy(p->loginchan, ""); p->acknowledged = 0; } - play_announcement = 1; - if( options ) - if( strchr( options, 's' ) ) - play_announcement = 0; + ast_mutex_unlock(&p->lock); ast_mutex_unlock(&agentlock); - if( !res && play_announcement ) + if( !res && play_announcement==1 ) res = ast_streamfile(chan, filename, chan->language); if (!res) ast_waitstream(chan, ""); @@ -1315,7 +1404,7 @@ "Uniqueid: %s\r\n", p->agent, p->loginchan, chan->uniqueid); ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan); - if (option_verbose > 2) + if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan); } else { logintime = time(NULL) - p->loginstart; @@ -1333,11 +1422,6 @@ ast_mutex_unlock(&agentlock); if (!res) res = ast_safe_sleep(chan, 500); - res = ast_streamfile(chan, "vm-goodbye", chan->language); - if (!res) - res = ast_waitstream(chan, ""); - if (!res) - res = ast_safe_sleep(chan, 1000); ast_mutex_unlock(&p->lock); } else if (!res) { #ifdef HONOR_MUSIC_CLASS @@ -1353,7 +1437,7 @@ "Channel: %s\r\n" "Uniqueid: %s\r\n", p->agent, chan->name, chan->uniqueid); - if (updatecdr && chan->cdr) + if (update_cdr && chan->cdr) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name); if (option_verbose > 2) @@ -1458,12 +1542,49 @@ if (!p) ast_mutex_unlock(&agentlock); - if (!res) + if (!res && (max_login_tries==0 || tries < max_login_tries)) { res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0); + } } LOCAL_USER_REMOVE(u); - /* Always hangup */ + if (!res) + res = ast_safe_sleep(chan, 500); + + /* AgentLogin() exit */ + if (!callbackmode) { + return -1; + } + /* AgentCallbackLogin() exit*/ + else { + /* Set variables */ + if (login_state > 0) { + pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user); + if (login_state==1) { + pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on"); + pbx_builtin_setvar_helper(chan, "AGENTEXTEN", exten); + } + else { + pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off"); + } + } + else { + pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail"); + } + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->callerid)) + return 0; + /* Do we need to play agent-goodbye now that we will be hanging up? */ + if (play_announcement==1) { + if (!res) + res = ast_safe_sleep(chan, 1000); + res = ast_streamfile(chan, agent_goodbye, chan->language); + if (!res) + res = ast_waitstream(chan, ""); + if (!res) + res = ast_safe_sleep(chan, 1000); + } + } + /* We should never get here if next priority exists when in callbackmode */ return -1; } Index: asterisk/configs/agents.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/agents.conf.sample,v retrieving revision 1.11 diff -u -r1.11 agents.conf.sample --- asterisk/configs/agents.conf.sample 28 Jun 2004 18:40:41 -0000 1.11 +++ asterisk/configs/agents.conf.sample 29 Jun 2004 05:51:50 -0000 @@ -32,6 +32,42 @@ ; ;updatecdr=no ; +; Define maxlogintries to set the limit of failed login attempts. +; The value should be an integer. Default is "3". +; Setting to 0 will disable maxlogintries. +; +;maxlogintries=3 +; +; Define playannouncement to enable/disable playing of agent logged in/off +; and goodbye message. Default is "yes". Valid values are boolean. +; This option is the same as option 's' on AgentCallbackLogin() +; +;playannouncement=yes +; +; Define playgoodbye to enalbe/disable playing of goodbye message. +; playannouncemnet must be enabled for goodbye to be played. +; Default is "yes". Valid values are boolean. +; +;playgoodbye=yes +; +; Define hangupcall to adjust if the call is hungup when exiting function +; AgentCallbackLogin(). This option has no effect on AgentLogin() or +; AgentMonitorOutgoing(). Default is "always". +; Valid values are: never, always, loginfail, loginok +; Short versions are: no, yes, fail, ok +; Upon exiting due to login failure, the priority will be increased by 100. +; +;hangupcall=always +; +; The values autologoff, ackcall, wrapuptime, updatecdr, maxlogintries, +; playannouncement, playgoodbye, and hangupcall can be adjusted in +; extensions.conf for individual extensions when using AgentLogin() or +; AgentCallaackLogin(). Not all variables apply to both functions. +; Use the following convention with SetVar to make on-the-fly adjustments: +; variable name is "AGENT" + option in all caps. Values are the same. +; Ex: exten => s,1,SetVar(AGENTMAXLOGINTRIES=0) +; exten => s,2,AgentCallbackLogin() +; ; Group memberships for agents (may change in mid-file just) ; ;group=3 @@ -63,6 +99,9 @@ ; An optional custom beep sound file to play to always-connected agents. ;custom_beep=beep ; +; The file to play when saying goodbye. The default is vm-goodbye +;goodbye=vm-goodbye +; ; -------------------------------------------------- ; ; This section contains the agent definitions, in the form: Index: asterisk/doc/README.variables =================================================================== RCS file: /usr/cvsroot/asterisk/doc/README.variables,v retrieving revision 1.19 diff -u -r1.19 README.variables --- asterisk/doc/README.variables 23 May 2004 14:19:45 -0000 1.19 +++ asterisk/doc/README.variables 29 Jun 2004 05:51:50 -0000 @@ -70,6 +70,8 @@ ${SIPUSERAGENT} SIP user agent ${SIPCALLID} SIP Call-ID: header verbatim (for logging or CDR matching) ${MEETMESECS} Number of seconds a user participated in a MeetMe conference +${AGENTSTATUS} Status of agent upon exiting AgentCallbackLogin(). set to on/off/fail +${AGENTEXTEN} The callback extension of the agent logged in via AgentCallbackLogin(). There are two reference modes - reference by value and reference by name. To refer to a variable with its name (as an argument to a function that