2002-09-02 15:20:28 +00:00
/*
* Asterisk - - A telephony toolkit for Linux .
*
2004-08-11 19:02:46 +00:00
* Implementation of Agents
2002-09-02 15:20:28 +00:00
*
2004-08-11 19:02:46 +00:00
* Copyright ( C ) 1999 - 2004 , Digium Inc .
2002-09-02 15:20:28 +00:00
*
2004-08-11 19:02:46 +00:00
* Mark Spencer < markster @ digium . com >
2002-09-02 15:20:28 +00:00
*
* This program is free software , distributed under the terms of
* the GNU General Public License
*/
# include <stdio.h>
# include <string.h>
# include <asterisk/lock.h>
# include <asterisk/channel.h>
# include <asterisk/channel_pvt.h>
# include <asterisk/config.h>
# include <asterisk/logger.h>
# include <asterisk/module.h>
# include <asterisk/pbx.h>
# include <asterisk/options.h>
# include <asterisk/lock.h>
# include <asterisk/sched.h>
# include <asterisk/io.h>
# include <asterisk/rtp.h>
# include <asterisk/acl.h>
# include <asterisk/callerid.h>
# include <asterisk/file.h>
# include <asterisk/cli.h>
# include <asterisk/app.h>
# include <asterisk/musiconhold.h>
# include <asterisk/manager.h>
2004-07-17 20:58:01 +00:00
# include <asterisk/features.h>
2004-05-09 07:51:44 +00:00
# include <asterisk/utils.h>
2004-10-26 22:25:43 +00:00
# include <asterisk/causes.h>
2005-01-01 00:57:04 +00:00
# include <asterisk/astdb.h>
2002-09-02 15:20:28 +00:00
# include <sys/socket.h>
# include <errno.h>
# include <unistd.h>
# include <stdlib.h>
# include <fcntl.h>
# include <netdb.h>
# include <arpa/inet.h>
# include <sys/signal.h>
static char * desc = " Agent Proxy Channel " ;
2004-12-31 00:58:44 +00:00
static char * channeltype = " Agent " ;
2002-09-02 15:20:28 +00:00
static char * tdesc = " Call Agent Proxy Channel " ;
static char * config = " agents.conf " ;
static char * app = " AgentLogin " ;
2003-07-14 02:31:56 +00:00
static char * app2 = " AgentCallbackLogin " ;
2004-02-05 21:13:17 +00:00
static char * app3 = " AgentMonitorOutgoing " ;
2002-09-02 15:20:28 +00:00
static char * synopsis = " Call agent login " ;
2003-07-14 02:31:56 +00:00
static char * synopsis2 = " Call agent callback login " ;
2004-02-05 21:13:17 +00:00
static char * synopsis3 = " Record agent's outgoing call " ;
2002-09-02 15:20:28 +00:00
static char * descrip =
2002-12-06 16:22:19 +00:00
" AgentLogin([AgentNo][|options]): \n "
2002-09-02 15:20:28 +00:00
" Asks the agent to login to the system. Always returns -1. While \n "
" logged in, the agent can receive calls and will hear a 'beep' \n "
2004-12-31 00:58:44 +00:00
" when a new call comes in. The agent can dump the call by pressing \n "
2002-12-06 16:22:19 +00:00
" the star key. \n "
" The option string may contain zero or more of the following characters: \n "
2004-10-24 13:24:16 +00:00
" 's' -- silent login - do not announce the login ok segment after agent logged in/off \n " ;
2002-09-02 15:20:28 +00:00
2003-07-14 02:31:56 +00:00
static char * descrip2 =
2003-08-16 04:42:13 +00:00
" AgentCallbackLogin([AgentNo][|[options][exten]@context]): \n "
2004-10-24 13:24:16 +00:00
" Asks the agent to login to the system with callback. \n "
2003-07-14 02:31:56 +00:00
" The agent's callback extension is called (optionally with the specified \n "
2004-12-31 00:58:44 +00:00
" context). \n "
2004-10-24 13:24:16 +00:00
" 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 " ;
2003-07-14 02:31:56 +00:00
2004-02-05 21:13:17 +00:00
static char * descrip3 =
" AgentMonitorOutgoing([options]): \n "
2004-12-31 00:58:44 +00:00
" Tries to figure out the id of the agent who is placing outgoing call based on \n "
" comparision of the callerid of the current interface and the global variable \n "
" placed by the AgentCallbackLogin application. That's why it should be used only \n "
" with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n "
" instead of Monitor application. That have to be configured in the agents.conf file. \n "
" \n Return value: \n "
" Normally the app returns 0 unless the options are passed. Also if the callerid or \n "
" the agentid are not specified it'll look for n+101 priority. \n "
" \n Options: \n "
" 'd' - make the app return -1 if there is an error condition and there is \n "
" no extension n+101 \n "
2004-12-10 17:37:20 +00:00
" 'c' - change the CDR so that the source of the call is 'Agent/agent_id' \n "
2004-12-31 00:58:44 +00:00
" 'n' - don't generate the warnings when there is no callerid or the \n "
" agentid is not known. \n "
" It's handy if you want to have one context for agent and non-agent calls. \n " ;
2004-02-05 21:13:17 +00:00
2004-12-01 05:00:29 +00:00
static char mandescr_agents [ ] =
" Description: Will list info about all possible agents. \n "
" Variables: NONE \n " ;
2002-09-02 15:20:28 +00:00
static char moh [ 80 ] = " default " ;
# define AST_MAX_AGENT 80 /* Agent ID or Password max length */
2004-02-03 16:57:00 +00:00
# define AST_MAX_BUF 256
2004-10-24 13:24:16 +00:00
# define AST_MAX_FILENAME_LEN 256
2002-09-02 15:20:28 +00:00
2005-01-01 00:57:04 +00:00
/* Persistent Agents astdb family */
static const char * pa_family = " /Agents " ;
/* The maximum lengh of each persistent member agent database entry */
# define PA_MAX_LEN 2048
/* queues.conf [general] option */
static int persistent_agents = 0 ;
static void dump_agents ( void ) ;
2002-09-02 15:20:28 +00:00
static int capability = - 1 ;
2003-07-01 04:08:25 +00:00
static unsigned int group ;
2003-07-22 11:06:56 +00:00
static int autologoff ;
2003-07-28 14:24:10 +00:00
static int wrapuptime ;
2003-08-05 23:57:55 +00:00
static int ackcall ;
2003-07-01 04:08:25 +00:00
2004-10-24 13:24:16 +00:00
static int maxlogintries = 3 ;
static char agentgoodbye [ AST_MAX_FILENAME_LEN ] = " vm-goodbye " ;
2002-09-02 15:20:28 +00:00
static int usecnt = 0 ;
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( usecnt_lock ) ;
2002-09-02 15:20:28 +00:00
2004-12-31 00:58:44 +00:00
/* Protect the interface list (of pvt's) */
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( agentlock ) ;
2002-09-02 15:20:28 +00:00
2004-02-12 22:28:35 +00:00
static int recordagentcalls = 0 ;
2004-07-16 04:40:54 +00:00
static char recordformat [ AST_MAX_BUF ] = " " ;
static char recordformatext [ AST_MAX_BUF ] = " " ;
2004-02-12 22:28:35 +00:00
static int createlink = 0 ;
2004-07-16 04:40:54 +00:00
static char urlprefix [ AST_MAX_BUF ] = " " ;
static char savecallsin [ AST_MAX_BUF ] = " " ;
2004-04-03 00:41:47 +00:00
static int updatecdr = 0 ;
2004-06-28 18:40:41 +00:00
static char beep [ AST_MAX_BUF ] = " beep " ;
2004-02-03 16:57:00 +00:00
2004-02-05 21:13:17 +00:00
# define GETAGENTBYCALLERID "AGENTBYCALLERID"
2002-09-02 15:20:28 +00:00
static struct agent_pvt {
2004-12-31 00:58:44 +00:00
ast_mutex_t lock ; /* Channel private lock */
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 loginstart ; /* When agent first logged in (0 when logged off) */
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 */
char agent [ AST_MAX_AGENT ] ; /* Agent ID */
2002-09-02 15:20:28 +00:00
char password [ AST_MAX_AGENT ] ; /* Password for Agent login */
char name [ AST_MAX_AGENT ] ;
2003-08-13 15:25:16 +00:00
ast_mutex_t app_lock ; /* Synchronization between owning applications */
2002-12-06 16:22:19 +00:00
volatile pthread_t owning_app ; /* Owning application thread id */
volatile int app_sleep_cond ; /* Sleep condition for the login app */
2004-12-31 00:58:44 +00:00
struct ast_channel * owner ; /* Agent */
2003-07-14 02:31:56 +00:00
char loginchan [ 80 ] ;
2004-12-31 00:58:44 +00:00
struct ast_channel * chan ; /* Channel we use */
struct agent_pvt * next ; /* Agent */
2002-09-02 15:20:28 +00:00
} * agents = NULL ;
2003-08-13 17:32:44 +00:00
# define CHECK_FORMATS(ast, p) do { \
if ( p - > chan ) { \
if ( ast - > nativeformats ! = p - > chan - > nativeformats ) { \
ast_log ( LOG_DEBUG , " Native formats changing from %d to %d \n " , ast - > nativeformats , p - > chan - > nativeformats ) ; \
/* Native formats changed, reset things */ \
ast - > nativeformats = p - > chan - > nativeformats ; \
ast_log ( LOG_DEBUG , " Resetting read to %d and write to %d \n " , ast - > readformat , ast - > writeformat ) ; \
2004-04-06 22:17:32 +00:00
ast_set_read_format ( ast , ast - > readformat ) ; \
ast_set_write_format ( ast , ast - > writeformat ) ; \
2003-08-13 17:32:44 +00:00
} \
if ( p - > chan - > readformat ! = ast - > pvt - > rawreadformat ) \
2004-04-06 22:17:32 +00:00
ast_set_read_format ( p - > chan , ast - > pvt - > rawreadformat ) ; \
2003-08-13 17:32:44 +00:00
if ( p - > chan - > writeformat ! = ast - > pvt - > rawwriteformat ) \
2004-04-06 22:17:32 +00:00
ast_set_write_format ( p - > chan , ast - > pvt - > rawwriteformat ) ; \
2003-08-13 17:32:44 +00:00
} \
} while ( 0 )
2003-08-23 02:11:44 +00:00
/* Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
properly for a timingfd XXX This might need more work if agents were logged in as agents or other
totally impractical combinations XXX */
2002-09-02 15:20:28 +00:00
# define CLEANUP(ast, p) do { \
int x ; \
if ( p - > chan ) { \
2003-08-23 02:11:44 +00:00
for ( x = 0 ; x < AST_MAX_FDS ; x + + ) { \
if ( x ! = AST_MAX_FDS - 2 ) \
ast - > fds [ x ] = p - > chan - > fds [ x ] ; \
} \
ast - > fds [ AST_MAX_FDS - 3 ] = p - > chan - > fds [ AST_MAX_FDS - 2 ] ; \
2002-09-02 15:20:28 +00:00
} \
} while ( 0 )
2005-01-01 00:57:04 +00:00
2003-07-01 16:16:28 +00:00
static void agent_unlink ( struct agent_pvt * agent )
{
struct agent_pvt * p , * prev ;
prev = NULL ;
p = agents ;
while ( p ) {
if ( p = = agent ) {
if ( prev )
prev - > next = agent - > next ;
else
agents = agent - > next ;
break ;
}
prev = p ;
p = p - > next ;
}
}
static struct agent_pvt * add_agent ( char * agent , int pending )
2002-09-02 15:20:28 +00:00
{
2004-07-16 04:40:54 +00:00
char tmp [ AST_MAX_BUF ] = " " ;
2002-09-02 15:20:28 +00:00
char * password = NULL , * name = NULL ;
2003-07-01 16:16:28 +00:00
struct agent_pvt * p , * prev ;
2002-09-02 15:20:28 +00:00
2004-07-16 04:40:54 +00:00
strncpy ( tmp , agent , sizeof ( tmp ) - 1 ) ;
2002-09-02 15:20:28 +00:00
if ( ( password = strchr ( tmp , ' , ' ) ) ) {
* password = ' \0 ' ;
password + + ;
while ( * password < 33 ) password + + ;
}
if ( password & & ( name = strchr ( password , ' , ' ) ) ) {
* name = ' \0 ' ;
name + + ;
while ( * name < 33 ) name + + ;
}
2003-07-01 16:16:28 +00:00
prev = NULL ;
2002-09-02 15:20:28 +00:00
p = agents ;
while ( p ) {
2003-07-01 16:16:28 +00:00
if ( ! pending & & ! strcmp ( p - > agent , tmp ) )
2002-09-02 15:20:28 +00:00
break ;
2003-07-01 16:16:28 +00:00
prev = p ;
2002-09-02 15:20:28 +00:00
p = p - > next ;
}
if ( ! p ) {
p = malloc ( sizeof ( struct agent_pvt ) ) ;
if ( p ) {
memset ( p , 0 , sizeof ( struct agent_pvt ) ) ;
strncpy ( p - > agent , tmp , sizeof ( p - > agent ) - 1 ) ;
2004-06-22 17:42:14 +00:00
ast_mutex_init ( & p - > lock ) ;
ast_mutex_init ( & p - > app_lock ) ;
2003-09-08 16:48:07 +00:00
p - > owning_app = ( pthread_t ) - 1 ;
2002-12-06 16:22:19 +00:00
p - > app_sleep_cond = 1 ;
2003-07-01 04:08:25 +00:00
p - > group = group ;
2003-07-01 16:16:28 +00:00
p - > pending = pending ;
p - > next = NULL ;
if ( prev )
prev - > next = p ;
else
agents = p ;
2002-09-02 15:20:28 +00:00
}
}
if ( ! p )
2003-07-01 16:16:28 +00:00
return NULL ;
2002-09-02 15:20:28 +00:00
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 ) ;
2003-08-05 23:57:55 +00:00
p - > ackcall = ackcall ;
2003-07-22 11:06:56 +00:00
p - > autologoff = autologoff ;
2003-07-28 14:24:10 +00:00
p - > wrapuptime = wrapuptime ;
2003-07-01 16:16:28 +00:00
if ( pending )
p - > dead = 1 ;
else
p - > dead = 0 ;
return p ;
2002-09-02 15:20:28 +00:00
}
2003-07-09 22:41:51 +00:00
static int agent_cleanup ( struct agent_pvt * p )
{
struct ast_channel * chan = p - > owner ;
p - > owner = NULL ;
chan - > pvt - > pvt = NULL ;
p - > app_sleep_cond = 1 ;
/* Release ownership of the agent to other threads (presumably running the login app). */
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > app_lock ) ;
2003-07-09 22:41:51 +00:00
if ( chan )
ast_channel_free ( chan ) ;
2004-06-22 17:42:14 +00:00
if ( p - > dead ) {
ast_mutex_destroy ( & p - > lock ) ;
ast_mutex_destroy ( & p - > app_lock ) ;
2003-07-09 22:41:51 +00:00
free ( p ) ;
2004-06-22 17:42:14 +00:00
}
2003-07-09 22:41:51 +00:00
return 0 ;
}
2003-07-01 16:16:28 +00:00
static int check_availability ( struct agent_pvt * newlyavailable , int needlock ) ;
2002-09-02 15:20:28 +00:00
static int agent_answer ( struct ast_channel * ast )
{
ast_log ( LOG_WARNING , " Huh? Agent is being asked to answer? \n " ) ;
return - 1 ;
}
2004-02-05 21:13:17 +00:00
static int __agent_start_monitoring ( struct ast_channel * ast , struct agent_pvt * p , int needlock )
2004-02-03 16:57:00 +00:00
{
char tmp [ AST_MAX_BUF ] , tmp2 [ AST_MAX_BUF ] , * pointer ;
char filename [ AST_MAX_BUF ] ;
int res = - 1 ;
if ( ! p )
return - 1 ;
if ( ! ast - > monitor ) {
snprintf ( filename , sizeof ( filename ) , " agent-%s-%s " , p - > agent , ast - > uniqueid ) ;
2004-02-10 21:14:48 +00:00
/* substitute . for - */
if ( ( pointer = strchr ( filename , ' . ' ) ) )
2004-02-03 16:57:00 +00:00
* pointer = ' - ' ;
2004-02-10 21:14:48 +00:00
snprintf ( tmp , sizeof ( tmp ) , " %s%s " , savecallsin ? savecallsin : " " , filename ) ;
2004-02-03 16:57:00 +00:00
ast_monitor_start ( ast , recordformat , tmp , needlock ) ;
ast_monitor_setjoinfiles ( ast , 1 ) ;
snprintf ( tmp2 , sizeof ( tmp2 ) , " %s%s.%s " , urlprefix ? urlprefix : " " , filename , recordformatext ) ;
#if 0
ast_verbose ( " name is %s, link is %s \n " , tmp , tmp2 ) ;
# endif
if ( ! ast - > cdr )
ast - > cdr = ast_cdr_alloc ( ) ;
ast_cdr_setuserfield ( ast , tmp2 ) ;
res = 0 ;
} else
ast_log ( LOG_ERROR , " Recording already started on that call. \n " ) ;
return res ;
}
2004-02-05 21:13:17 +00:00
static int agent_start_monitoring ( struct ast_channel * ast , int needlock )
{
return __agent_start_monitoring ( ast , ast - > pvt - > pvt , needlock ) ;
}
2004-12-31 00:58:44 +00:00
static struct ast_frame * agent_read ( struct ast_channel * ast )
2002-09-02 15:20:28 +00:00
{
struct agent_pvt * p = ast - > pvt - > pvt ;
struct ast_frame * f = NULL ;
2003-07-01 16:16:28 +00:00
static struct ast_frame null_frame = { AST_FRAME_NULL , } ;
2003-07-14 02:31:56 +00:00
static struct ast_frame answer_frame = { AST_FRAME_CONTROL , AST_CONTROL_ANSWER } ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-08-13 17:32:44 +00:00
CHECK_FORMATS ( ast , p ) ;
2003-08-13 01:12:19 +00:00
if ( p - > chan ) {
2004-12-07 20:38:43 +00:00
ast_copy_flags ( p - > chan , ast , AST_FLAG_EXCEPTION ) ;
2003-08-23 02:11:44 +00:00
if ( ast - > fdno = = AST_MAX_FDS - 3 )
p - > chan - > fdno = AST_MAX_FDS - 2 ;
else
p - > chan - > fdno = ast - > fdno ;
2002-09-02 15:20:28 +00:00
f = ast_read ( p - > chan ) ;
2003-08-13 01:12:19 +00:00
} else
2003-07-01 16:16:28 +00:00
f = & null_frame ;
2002-09-02 15:20:28 +00:00
if ( ! f ) {
2003-07-28 14:24:10 +00:00
/* If there's a channel, hang it up (if it's on a callback) make it NULL */
2003-07-27 04:27:18 +00:00
if ( p - > chan ) {
2003-08-27 15:59:43 +00:00
/* Note that we don't hangup if it's not a callback because Asterisk will do it
for us when the PBX instance that called login finishes */
2004-08-11 19:02:46 +00:00
if ( ! ast_strlen_zero ( p - > loginchan ) ) {
2004-10-23 12:19:47 +00:00
p - > chan - > _bridge = NULL ;
2003-07-28 14:24:10 +00:00
ast_hangup ( p - > chan ) ;
2004-08-11 19:02:46 +00:00
if ( p - > wrapuptime ) {
gettimeofday ( & p - > lastdisc , NULL ) ;
p - > lastdisc . tv_usec + = ( p - > wrapuptime % 1000 ) * 1000 ;
if ( p - > lastdisc . tv_usec > 1000000 ) {
p - > lastdisc . tv_usec - = 1000000 ;
p - > lastdisc . tv_sec + + ;
}
p - > lastdisc . tv_sec + = ( p - > wrapuptime / 1000 ) ;
}
}
2002-09-02 15:20:28 +00:00
p - > chan = NULL ;
2003-08-06 01:39:42 +00:00
p - > acknowledged = 0 ;
2003-07-27 04:27:18 +00:00
}
2002-09-02 15:20:28 +00:00
}
2004-12-22 20:50:23 +00:00
if ( ( p - > chan & & ( p - > chan - > _state ! = AST_STATE_UP ) ) & & f & & ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = AST_CONTROL_ANSWER ) ) {
2003-08-05 23:57:55 +00:00
/* TC */
2003-08-13 17:32:44 +00:00
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 ;
}
2003-08-05 23:57:55 +00:00
else {
2003-08-13 17:32:44 +00:00
p - > acknowledged = 1 ;
f = & answer_frame ;
2004-10-23 12:19:47 +00:00
if ( p - > chan )
p - > chan - > _bridge = ast ;
2003-08-05 23:57:55 +00:00
}
2003-07-14 02:31:56 +00:00
}
if ( f & & ( f - > frametype = = AST_FRAME_DTMF ) & & ( f - > subclass = = ' # ' ) ) {
if ( ! p - > acknowledged ) {
2003-08-02 21:25:43 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s acknowledged \n " , p - > chan - > name ) ;
2003-07-14 02:31:56 +00:00
p - > acknowledged = 1 ;
ast_frfree ( f ) ;
f = & answer_frame ;
2004-10-23 12:19:47 +00:00
if ( p - > chan )
p - > chan - > _bridge = ast ;
2003-07-14 02:31:56 +00:00
}
}
2002-09-02 15:20:28 +00:00
if ( f & & ( f - > frametype = = AST_FRAME_DTMF ) & & ( f - > subclass = = ' * ' ) ) {
/* * terminates call */
ast_frfree ( f ) ;
f = NULL ;
}
CLEANUP ( ast , p ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2004-02-12 22:28:35 +00:00
if ( recordagentcalls & & f = = & answer_frame )
2004-02-03 16:57:00 +00:00
agent_start_monitoring ( ast , 0 ) ;
2002-09-02 15:20:28 +00:00
return f ;
}
static int agent_write ( struct ast_channel * ast , struct ast_frame * f )
{
struct agent_pvt * p = ast - > pvt - > pvt ;
int res = - 1 ;
2003-08-13 17:32:44 +00:00
CHECK_FORMATS ( ast , p ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-08-13 01:12:19 +00:00
if ( p - > chan ) {
2003-08-13 17:32:44 +00:00
if ( ( f - > frametype ! = AST_FRAME_VOICE ) | |
( f - > subclass = = p - > chan - > writeformat ) ) {
res = ast_write ( p - > chan , f ) ;
} else {
ast_log ( LOG_DEBUG , " Dropping one incompatible voice frame on '%s' to '%s' \n " , ast - > name , p - > chan - > name ) ;
res = 0 ;
}
2003-08-13 01:12:19 +00:00
} else
2003-07-01 16:16:28 +00:00
res = 0 ;
2002-09-02 15:20:28 +00:00
CLEANUP ( ast , p ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
return res ;
}
2004-04-08 19:19:24 +00:00
static int agent_fixup ( struct ast_channel * oldchan , struct ast_channel * newchan )
2002-09-02 15:20:28 +00:00
{
struct agent_pvt * p = newchan - > pvt - > pvt ;
2004-04-08 19:28:05 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
if ( p - > owner ! = oldchan ) {
ast_log ( LOG_WARNING , " old channel wasn't %p but was %p \n " , oldchan , p - > owner ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
return - 1 ;
}
p - > owner = newchan ;
2004-04-08 19:28:05 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
return 0 ;
}
static int agent_indicate ( struct ast_channel * ast , int condition )
{
struct agent_pvt * p = ast - > pvt - > pvt ;
int res = - 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
if ( p - > chan )
res = ast_indicate ( p - > chan , condition ) ;
2003-07-01 16:16:28 +00:00
else
res = 0 ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
return res ;
}
static int agent_digit ( struct ast_channel * ast , char digit )
{
struct agent_pvt * p = ast - > pvt - > pvt ;
int res = - 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
if ( p - > chan )
res = p - > chan - > pvt - > send_digit ( p - > chan , digit ) ;
2003-07-01 16:16:28 +00:00
else
res = 0 ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
return res ;
}
static int agent_call ( struct ast_channel * ast , char * dest , int timeout )
{
struct agent_pvt * p = ast - > pvt - > pvt ;
int res = - 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-08-21 16:42:13 +00:00
p - > acknowledged = 0 ;
2003-07-01 16:16:28 +00:00
if ( ! p - > chan ) {
if ( p - > pending ) {
ast_log ( LOG_DEBUG , " Pretending to dial on pending agent \n " ) ;
ast_setstate ( ast , AST_STATE_DIALING ) ;
res = 0 ;
} else {
ast_log ( LOG_NOTICE , " Whoa, they hung up between alloc and call... what are the odds of that? \n " ) ;
res = - 1 ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-07-01 16:16:28 +00:00
return res ;
2004-05-09 07:51:44 +00:00
} else if ( ! ast_strlen_zero ( p - > loginchan ) ) {
2003-07-22 11:06:56 +00:00
time ( & p - > start ) ;
2003-07-14 02:31:56 +00:00
/* 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 ) ;
2004-10-02 00:58:31 +00:00
if ( p - > chan - > cid . cid_num )
free ( p - > chan - > cid . cid_num ) ;
if ( ast - > cid . cid_num )
p - > chan - > cid . cid_num = strdup ( ast - > cid . cid_num ) ;
2003-08-14 20:42:25 +00:00
else
2004-10-02 00:58:31 +00:00
p - > chan - > cid . cid_num = NULL ;
if ( p - > chan - > cid . cid_name )
free ( p - > chan - > cid . cid_name ) ;
if ( ast - > cid . cid_name )
p - > chan - > cid . cid_name = strdup ( ast - > cid . cid_name ) ;
else
p - > chan - > cid . cid_name = NULL ;
2003-07-14 02:31:56 +00:00
res = ast_call ( p - > chan , p - > loginchan , 0 ) ;
CLEANUP ( ast , p ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-07-14 02:31:56 +00:00
return res ;
2003-07-01 16:16:28 +00:00
}
2003-03-20 17:21:54 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " agent_call, call to agent '%s' call on '%s' \n " , p - > agent , p - > chan - > name ) ;
ast_log ( LOG_DEBUG , " Playing beep, lang '%s' \n " , p - > chan - > language ) ;
2004-06-28 18:40:41 +00:00
res = ast_streamfile ( p - > chan , beep , p - > chan - > language ) ;
2003-03-20 17:21:54 +00:00
ast_log ( LOG_DEBUG , " Played beep, result '%d' \n " , res ) ;
if ( ! res ) {
2002-09-02 15:20:28 +00:00
res = ast_waitstream ( p - > chan , " " ) ;
2003-03-20 17:21:54 +00:00
ast_log ( LOG_DEBUG , " Waited for stream, result '%d' \n " , res ) ;
}
2002-09-02 15:20:28 +00:00
if ( ! res ) {
2004-04-06 22:17:32 +00:00
res = ast_set_read_format ( p - > chan , ast_best_codec ( p - > chan - > nativeformats ) ) ;
2003-03-20 17:21:54 +00:00
ast_log ( LOG_DEBUG , " Set read format, result '%d' \n " , res ) ;
2002-09-02 15:20:28 +00:00
if ( res )
2003-08-16 05:10:35 +00:00
ast_log ( LOG_WARNING , " Unable to set read format to %s \n " , ast_getformatname ( ast_best_codec ( p - > chan - > nativeformats ) ) ) ;
2003-07-01 16:16:28 +00:00
} else {
2004-12-19 21:13:41 +00:00
/* Agent hung-up */
2003-03-20 17:21:54 +00:00
p - > chan = NULL ;
}
2002-09-02 15:20:28 +00:00
if ( ! res ) {
2004-04-06 22:17:32 +00:00
ast_set_write_format ( p - > chan , ast_best_codec ( p - > chan - > nativeformats ) ) ;
2003-03-20 17:21:54 +00:00
ast_log ( LOG_DEBUG , " Set write format, result '%d' \n " , res ) ;
2002-09-02 15:20:28 +00:00
if ( res )
2003-08-16 05:10:35 +00:00
ast_log ( LOG_WARNING , " Unable to set write format to %s \n " , ast_getformatname ( ast_best_codec ( p - > chan - > nativeformats ) ) ) ;
2002-09-02 15:20:28 +00:00
}
2003-03-20 17:21:54 +00:00
if ( ! res )
{
2003-08-21 16:42:13 +00:00
/* Call is immediately up, or might need ack */
if ( p - > ackcall > 1 )
ast_setstate ( ast , AST_STATE_RINGING ) ;
else {
ast_setstate ( ast , AST_STATE_UP ) ;
2004-02-12 22:28:35 +00:00
if ( recordagentcalls )
agent_start_monitoring ( ast , 0 ) ;
2003-08-21 16:42:13 +00:00
p - > acknowledged = 1 ;
}
res = 0 ;
2003-03-20 17:21:54 +00:00
}
2002-12-06 16:22:19 +00:00
CLEANUP ( ast , p ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
return res ;
}
static int agent_hangup ( struct ast_channel * ast )
{
struct agent_pvt * p = ast - > pvt - > pvt ;
2003-07-22 11:06:56 +00:00
int howlong = 0 ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
p - > owner = NULL ;
ast - > pvt - > pvt = NULL ;
2002-12-06 16:22:19 +00:00
p - > app_sleep_cond = 1 ;
2003-08-21 16:42:13 +00:00
p - > acknowledged = 0 ;
2004-10-16 15:01:59 +00:00
/* if they really are hung up then set start to 0 so the test
* later if we ' re called on an already downed channel
* doesn ' t cause an agent to be logged out like when
* agent_request ( ) is followed immediately by agent_hangup ( )
* as in apps / app_chanisavail . c : chanavail_exec ( )
*/
2004-12-11 04:46:49 +00:00
ast_mutex_lock ( & usecnt_lock ) ;
usecnt - - ;
ast_mutex_unlock ( & usecnt_lock ) ;
2004-10-16 15:01:59 +00:00
ast_log ( LOG_DEBUG , " Hangup called for state %s \n " , ast_state2str ( ast - > _state ) ) ;
if ( p - > start & & ( ast - > _state ! = AST_STATE_UP ) ) {
2003-07-22 11:06:56 +00:00
howlong = time ( NULL ) - p - > start ;
2004-10-16 15:01:59 +00:00
p - > start = 0 ;
} else if ( ast - > _state = = AST_STATE_RESERVED ) {
howlong = 0 ;
} else
p - > start = 0 ;
2002-09-02 15:20:28 +00:00
if ( p - > chan ) {
/* If they're dead, go ahead and hang up on the agent now */
2004-05-09 07:51:44 +00:00
if ( ! ast_strlen_zero ( p - > loginchan ) ) {
2004-08-01 02:39:32 +00:00
/* Store last disconnect time */
if ( p - > wrapuptime ) {
gettimeofday ( & p - > lastdisc , NULL ) ;
p - > lastdisc . tv_usec + = ( p - > wrapuptime % 1000 ) * 1000 ;
if ( p - > lastdisc . tv_usec > = 1000000 ) {
p - > lastdisc . tv_usec - = 1000000 ;
p - > lastdisc . tv_sec + + ;
}
p - > lastdisc . tv_sec + = ( p - > wrapuptime / 1000 ) ;
} else
memset ( & p - > lastdisc , 0 , sizeof ( p - > lastdisc ) ) ;
2003-07-14 02:31:56 +00:00
if ( p - > chan ) {
/* Recognize the hangup and pass it along immediately */
ast_hangup ( p - > chan ) ;
p - > chan = NULL ;
}
2003-07-27 04:27:18 +00:00
ast_log ( LOG_DEBUG , " Hungup, howlong is %d, autologoff is %d \n " , howlong , p - > autologoff ) ;
2003-07-22 11:06:56 +00:00
if ( howlong & & p - > autologoff & & ( howlong > p - > autologoff ) ) {
2004-06-03 17:33:35 +00:00
char agent [ AST_MAX_AGENT ] = " " ;
long logintime = time ( NULL ) - p - > loginstart ;
p - > loginstart = 0 ;
2003-07-22 11:06:56 +00:00
ast_log ( LOG_NOTICE , " Agent '%s' didn't answer/confirm within %d seconds (waited %d) \n " , p - > name , p - > autologoff , howlong ) ;
2004-06-03 17:33:35 +00:00
manager_event ( EVENT_FLAG_AGENT , " Agentcallbacklogoff " ,
" Agent: %s \r \n "
" Loginchan: %s \r \n "
" Logintime: %ld \r \n "
" Reason: Autologoff \r \n "
" Uniqueid: %s \r \n " ,
p - > agent , p - > loginchan , logintime , ast - > uniqueid ) ;
snprintf ( agent , sizeof ( agent ) , " Agent/%s " , p - > agent ) ;
ast_queue_log ( " NONE " , ast - > uniqueid , agent , " AGENTCALLBACKLOGOFF " , " %s|%ld|%s " , p - > loginchan , logintime , " Autologoff " ) ;
2004-07-16 04:40:54 +00:00
p - > loginchan [ 0 ] = ' \0 ' ;
2004-11-13 22:44:33 +00:00
ast_device_state_changed ( " Agent/%s " , p - > agent ) ;
2003-07-22 11:06:56 +00:00
}
2003-07-14 02:31:56 +00:00
} else if ( p - > dead ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > chan - > lock ) ;
2002-09-02 15:20:28 +00:00
ast_softhangup ( p - > chan , AST_SOFTHANGUP_EXPLICIT ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > chan - > lock ) ;
2003-07-14 02:31:56 +00:00
} else {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > chan - > lock ) ;
2003-07-14 02:31:56 +00:00
ast_moh_start ( p - > chan , p - > moh ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > chan - > lock ) ;
2003-07-14 02:31:56 +00:00
}
2003-07-01 16:16:28 +00:00
}
#if 0
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-03-20 17:21:54 +00:00
/* Release ownership of the agent to other threads (presumably running the login app). */
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > app_lock ) ;
2003-03-20 17:21:54 +00:00
} else if ( p - > dead ) {
2002-09-02 15:20:28 +00:00
/* Go ahead and lose it */
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-03-20 17:21:54 +00:00
/* Release ownership of the agent to other threads (presumably running the login app). */
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > app_lock ) ;
2003-03-20 17:21:54 +00:00
} else {
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-03-20 17:21:54 +00:00
/* Release ownership of the agent to other threads (presumably running the login app). */
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > app_lock ) ;
2003-03-20 17:21:54 +00:00
}
2003-07-01 16:16:28 +00:00
# endif
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-12-06 16:22:19 +00:00
2003-07-01 16:16:28 +00:00
if ( p - > pending ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & agentlock ) ;
2003-07-01 16:16:28 +00:00
agent_unlink ( p ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2003-07-01 16:16:28 +00:00
}
2003-07-09 22:41:51 +00:00
if ( p - > abouttograb ) {
/* Let the "about to grab" thread know this isn't valid anymore, and let it
kill it later */
p - > abouttograb = 0 ;
} else if ( p - > dead ) {
2004-06-22 17:42:14 +00:00
ast_mutex_destroy ( & p - > lock ) ;
ast_mutex_destroy ( & p - > app_lock ) ;
2003-07-01 16:16:28 +00:00
free ( p ) ;
2003-08-27 15:59:43 +00:00
} else {
if ( p - > chan ) {
/* Not dead -- check availability now */
ast_mutex_lock ( & p - > lock ) ;
/* Store last disconnect time */
gettimeofday ( & p - > lastdisc , NULL ) ;
ast_mutex_unlock ( & p - > lock ) ;
}
2003-08-26 03:50:01 +00:00
/* Release ownership of the agent to other threads (presumably running the login app). */
ast_mutex_unlock ( & p - > app_lock ) ;
2003-07-01 16:16:28 +00:00
}
2002-09-02 15:20:28 +00:00
return 0 ;
}
2002-12-06 16:22:19 +00:00
static int agent_cont_sleep ( void * data )
{
struct agent_pvt * p ;
2003-07-28 14:24:10 +00:00
struct timeval tv ;
2002-12-06 16:22:19 +00:00
int res ;
p = ( struct agent_pvt * ) data ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-12-06 16:22:19 +00:00
res = p - > app_sleep_cond ;
2003-07-28 14:24:10 +00:00
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 ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-07-01 16:16:28 +00:00
#if 0
2002-12-06 16:22:19 +00:00
if ( ! res )
ast_log ( LOG_DEBUG , " agent_cont_sleep() returning %d \n " , res ) ;
2003-07-01 16:16:28 +00:00
# endif
2002-12-06 16:22:19 +00:00
return res ;
}
2003-08-26 00:36:03 +00:00
static int agent_ack_sleep ( void * data )
{
struct agent_pvt * p ;
2003-08-26 03:50:01 +00:00
int res = 0 ;
2003-08-26 00:36:03 +00:00
int to = 1000 ;
2003-08-26 18:50:46 +00:00
struct ast_frame * f ;
2003-08-26 00:36:03 +00:00
/* Wait a second and look for something */
p = ( struct agent_pvt * ) data ;
if ( p - > chan ) {
for ( ; ; ) {
to = ast_waitfor ( p - > chan , to ) ;
if ( to < 0 ) {
res = - 1 ;
break ;
}
if ( ! to ) {
res = 0 ;
break ;
}
2003-08-26 18:50:46 +00:00
f = ast_read ( p - > chan ) ;
if ( ! f ) {
res = - 1 ;
break ;
}
if ( f - > frametype = = AST_FRAME_DTMF )
res = f - > subclass ;
else
res = 0 ;
ast_frfree ( f ) ;
2003-08-26 00:36:03 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-08-26 04:53:49 +00:00
if ( ! p - > app_sleep_cond ) {
2003-08-26 00:36:03 +00:00
ast_mutex_unlock ( & p - > lock ) ;
res = 0 ;
break ;
} else if ( res = = ' # ' ) {
ast_mutex_unlock ( & p - > lock ) ;
2003-08-26 04:53:49 +00:00
res = 1 ;
2003-08-26 00:36:03 +00:00
break ;
}
ast_mutex_unlock ( & p - > lock ) ;
res = 0 ;
}
} else
res = - 1 ;
return res ;
}
2004-10-23 12:19:47 +00:00
static struct ast_channel * agent_bridgedchannel ( struct ast_channel * chan , struct ast_channel * bridge )
{
struct agent_pvt * p ;
struct ast_channel * ret = NULL ;
p = bridge - > pvt - > pvt ;
if ( chan = = p - > chan )
ret = bridge - > _bridge ;
else if ( chan = = bridge - > _bridge )
ret = p - > chan ;
return NULL ;
}
2004-12-31 00:58:44 +00:00
/*--- agent_new: Create new agent channel ---*/
2002-09-02 15:20:28 +00:00
static struct ast_channel * agent_new ( struct agent_pvt * p , int state )
{
struct ast_channel * tmp ;
2002-12-06 16:22:19 +00:00
struct ast_frame null_frame = { AST_FRAME_NULL } ;
2003-07-01 16:16:28 +00:00
#if 0
2002-09-02 15:20:28 +00:00
if ( ! p - > chan ) {
ast_log ( LOG_WARNING , " No channel? :( \n " ) ;
return NULL ;
}
2003-07-01 16:16:28 +00:00
# endif
2002-09-02 15:20:28 +00:00
tmp = ast_channel_alloc ( 0 ) ;
if ( tmp ) {
2003-07-01 16:16:28 +00:00
if ( p - > chan ) {
tmp - > nativeformats = p - > chan - > nativeformats ;
tmp - > writeformat = p - > chan - > writeformat ;
tmp - > pvt - > rawwriteformat = p - > chan - > writeformat ;
tmp - > readformat = p - > chan - > readformat ;
tmp - > pvt - > rawreadformat = p - > chan - > readformat ;
strncpy ( tmp - > language , p - > chan - > language , sizeof ( tmp - > language ) - 1 ) ;
strncpy ( tmp - > context , p - > chan - > context , sizeof ( tmp - > context ) - 1 ) ;
strncpy ( tmp - > exten , p - > chan - > exten , sizeof ( tmp - > exten ) - 1 ) ;
} else {
tmp - > nativeformats = AST_FORMAT_SLINEAR ;
tmp - > writeformat = AST_FORMAT_SLINEAR ;
tmp - > pvt - > rawwriteformat = AST_FORMAT_SLINEAR ;
tmp - > readformat = AST_FORMAT_SLINEAR ;
tmp - > pvt - > rawreadformat = AST_FORMAT_SLINEAR ;
}
if ( p - > pending )
snprintf ( tmp - > name , sizeof ( tmp - > name ) , " Agent/P%s-%d " , p - > agent , rand ( ) & 0xffff ) ;
else
snprintf ( tmp - > name , sizeof ( tmp - > name ) , " Agent/%s " , p - > agent ) ;
2004-12-31 00:58:44 +00:00
tmp - > type = channeltype ;
2002-09-02 15:20:28 +00:00
ast_setstate ( tmp , state ) ;
tmp - > pvt - > pvt = p ;
tmp - > pvt - > send_digit = agent_digit ;
tmp - > pvt - > call = agent_call ;
tmp - > pvt - > hangup = agent_hangup ;
tmp - > pvt - > answer = agent_answer ;
tmp - > pvt - > read = agent_read ;
tmp - > pvt - > write = agent_write ;
tmp - > pvt - > exception = agent_read ;
tmp - > pvt - > indicate = agent_indicate ;
tmp - > pvt - > fixup = agent_fixup ;
2004-10-23 12:19:47 +00:00
tmp - > pvt - > bridged_channel = agent_bridgedchannel ;
2002-09-02 15:20:28 +00:00
p - > owner = tmp ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & usecnt_lock ) ;
2002-09-02 15:20:28 +00:00
usecnt + + ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & usecnt_lock ) ;
2002-09-02 15:20:28 +00:00
ast_update_use_count ( ) ;
tmp - > priority = 1 ;
2002-12-06 16:22:19 +00:00
/* Wake up and wait for other applications (by definition the login app)
* to release this channel ) . Takes ownership of the agent channel
* to this thread only .
* For signalling the other thread , ast_queue_frame is used until we
* can safely use signals for this purpose . The pselect ( ) needs to be
* implemented in the kernel for this .
*/
p - > app_sleep_cond = 0 ;
2003-08-13 15:25:16 +00:00
if ( ast_mutex_trylock ( & p - > app_lock ) )
2002-12-06 16:22:19 +00:00
{
2003-07-08 18:50:02 +00:00
if ( p - > chan ) {
2004-04-06 22:17:32 +00:00
ast_queue_frame ( p - > chan , & null_frame ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ; /* For other thread to read the condition. */
ast_mutex_lock ( & p - > app_lock ) ;
ast_mutex_lock ( & p - > lock ) ;
2003-07-08 18:50:02 +00:00
}
2003-03-20 17:21:54 +00:00
if ( ! p - > chan )
{
ast_log ( LOG_WARNING , " Agent disconnected while we were connecting the call \n " ) ;
p - > owner = NULL ;
tmp - > pvt - > pvt = NULL ;
p - > app_sleep_cond = 1 ;
ast_channel_free ( tmp ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ; /* For other thread to read the condition. */
ast_mutex_unlock ( & p - > app_lock ) ;
2003-03-20 17:21:54 +00:00
return NULL ;
}
2002-12-06 16:22:19 +00:00
}
p - > owning_app = pthread_self ( ) ;
/* After the above step, there should not be any blockers. */
2003-07-01 16:16:28 +00:00
if ( p - > chan ) {
2004-12-07 20:38:43 +00:00
if ( ast_test_flag ( p - > chan , AST_FLAG_BLOCKING ) ) {
2003-07-01 16:16:28 +00:00
ast_log ( LOG_ERROR , " A blocker exists after agent channel ownership acquired \n " ) ;
CRASH ;
}
ast_moh_stop ( p - > chan ) ;
2002-09-02 15:20:28 +00:00
}
} else
2004-12-31 00:58:44 +00:00
ast_log ( LOG_WARNING , " Unable to allocate agent channel structure \n " ) ;
2002-09-02 15:20:28 +00:00
return tmp ;
}
2004-12-31 00:58:44 +00:00
/*--- read_agent_config: Read configuration data (agents.conf) ---*/
2002-09-02 15:20:28 +00:00
static int read_agent_config ( void )
{
struct ast_config * cfg ;
struct ast_variable * v ;
struct agent_pvt * p , * pl , * pn ;
2005-01-01 00:57:04 +00:00
char * general_val ;
2004-12-31 00:58:44 +00:00
2003-07-01 04:08:25 +00:00
group = 0 ;
2003-07-22 11:06:56 +00:00
autologoff = 0 ;
2003-07-28 14:24:10 +00:00
wrapuptime = 0 ;
2003-08-05 23:59:56 +00:00
ackcall = 1 ;
2002-09-02 15:20:28 +00:00
cfg = ast_load ( config ) ;
if ( ! cfg ) {
ast_log ( LOG_NOTICE , " No agent configuration found -- agent support disabled \n " ) ;
return 0 ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
p = agents ;
while ( p ) {
p - > dead = 1 ;
p = p - > next ;
}
2004-07-16 04:40:54 +00:00
strncpy ( moh , " default " , sizeof ( moh ) - 1 ) ;
2004-02-03 16:57:00 +00:00
/* set the default recording values */
recordagentcalls = 0 ;
createlink = 0 ;
2004-07-16 04:40:54 +00:00
strncpy ( recordformat , " wav " , sizeof ( recordformat ) - 1 ) ;
strncpy ( recordformatext , " wav " , sizeof ( recordformatext ) - 1 ) ;
urlprefix [ 0 ] = ' \0 ' ;
savecallsin [ 0 ] = ' \0 ' ;
2004-02-03 16:57:00 +00:00
2005-01-01 00:57:04 +00:00
/* Read in [general] section for persistance */
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " persistentagents " ) ) )
persistent_agents = ast_true ( general_val ) ;
/* Read in the [agents] section */
2002-09-02 15:20:28 +00:00
v = ast_variable_browse ( cfg , " agents " ) ;
while ( v ) {
/* Create the interface list */
if ( ! strcasecmp ( v - > name , " agent " ) ) {
2003-07-01 16:16:28 +00:00
add_agent ( v - > value , 0 ) ;
2003-07-01 04:08:25 +00:00
} else if ( ! strcasecmp ( v - > name , " group " ) ) {
group = ast_get_group ( v - > value ) ;
2003-07-22 11:06:56 +00:00
} else if ( ! strcasecmp ( v - > name , " autologoff " ) ) {
autologoff = atoi ( v - > value ) ;
if ( autologoff < 0 )
autologoff = 0 ;
2003-08-05 23:57:55 +00:00
} else if ( ! strcasecmp ( v - > name , " ackcall " ) ) {
2003-08-21 16:42:13 +00:00
if ( ! strcasecmp ( v - > value , " always " ) )
ackcall = 2 ;
else if ( ast_true ( v - > value ) )
2004-12-31 00:58:44 +00:00
ackcall = 1 ;
2003-08-21 16:42:13 +00:00
else
ackcall = 0 ;
2003-07-28 14:24:10 +00:00
} else if ( ! strcasecmp ( v - > name , " wrapuptime " ) ) {
wrapuptime = atoi ( v - > value ) ;
if ( wrapuptime < 0 )
wrapuptime = 0 ;
2004-10-24 13:24:16 +00:00
} 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 ) ;
2002-09-02 15:20:28 +00:00
} else if ( ! strcasecmp ( v - > name , " musiconhold " ) ) {
strncpy ( moh , v - > value , sizeof ( moh ) - 1 ) ;
2004-04-03 00:41:47 +00:00
} else if ( ! strcasecmp ( v - > name , " updatecdr " ) ) {
2004-10-24 13:24:16 +00:00
if ( ast_true ( v - > value ) )
updatecdr = 1 ;
else
updatecdr = 0 ;
2004-02-03 16:57:00 +00:00
} else if ( ! strcasecmp ( v - > name , " recordagentcalls " ) ) {
recordagentcalls = ast_true ( v - > value ) ;
} else if ( ! strcasecmp ( v - > name , " createlink " ) ) {
createlink = ast_true ( v - > value ) ;
} else if ( ! strcasecmp ( v - > name , " recordformat " ) ) {
strncpy ( recordformat , v - > value , sizeof ( recordformat ) - 1 ) ;
if ( ! strcasecmp ( v - > value , " wav49 " ) )
2004-07-16 04:40:54 +00:00
strncpy ( recordformatext , " WAV " , sizeof ( recordformatext ) - 1 ) ;
2004-02-03 16:57:00 +00:00
else
2004-07-16 04:40:54 +00:00
strncpy ( recordformatext , v - > value , sizeof ( recordformatext ) - 1 ) ;
2004-02-03 16:57:00 +00:00
} else if ( ! strcasecmp ( v - > name , " urlprefix " ) ) {
strncpy ( urlprefix , v - > value , sizeof ( urlprefix ) - 2 ) ;
if ( urlprefix [ strlen ( urlprefix ) - 1 ] ! = ' / ' )
2004-07-16 04:40:54 +00:00
strncat ( urlprefix , " / " , sizeof ( urlprefix ) - strlen ( urlprefix ) - 1 ) ;
2004-02-03 16:57:00 +00:00
} else if ( ! strcasecmp ( v - > name , " savecallsin " ) ) {
if ( v - > value [ 0 ] = = ' / ' )
strncpy ( savecallsin , v - > value , sizeof ( savecallsin ) - 2 ) ;
else
snprintf ( savecallsin , sizeof ( savecallsin ) - 2 , " /%s " , v - > value ) ;
if ( savecallsin [ strlen ( savecallsin ) - 1 ] ! = ' / ' )
2004-07-16 04:40:54 +00:00
strncat ( savecallsin , " / " , sizeof ( savecallsin ) - strlen ( savecallsin ) - 1 ) ;
2004-06-28 18:40:41 +00:00
} else if ( ! strcasecmp ( v - > name , " custom_beep " ) ) {
strncpy ( beep , v - > value , sizeof ( beep ) - 1 ) ;
2002-09-02 15:20:28 +00:00
}
v = v - > next ;
}
p = agents ;
pl = NULL ;
while ( p ) {
pn = p - > next ;
if ( p - > dead ) {
/* Unlink */
if ( pl )
pl - > next = p - > next ;
else
agents = p - > next ;
/* Destroy if appropriate */
if ( ! p - > owner ) {
if ( ! p - > chan ) {
2004-06-22 17:42:14 +00:00
ast_mutex_destroy ( & p - > lock ) ;
ast_mutex_destroy ( & p - > app_lock ) ;
2002-09-02 15:20:28 +00:00
free ( p ) ;
} else {
/* Cause them to hang up */
ast_softhangup ( p - > chan , AST_SOFTHANGUP_EXPLICIT ) ;
}
}
} else
pl = p ;
p = pn ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
ast_destroy ( cfg ) ;
return 0 ;
}
2003-07-01 16:16:28 +00:00
static int check_availability ( struct agent_pvt * newlyavailable , int needlock )
{
struct ast_channel * chan = NULL , * parent = NULL ;
struct agent_pvt * p ;
int res ;
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Checking availability of '%s' \n " , newlyavailable - > agent ) ;
2003-07-01 16:16:28 +00:00
if ( needlock )
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & agentlock ) ;
2003-07-01 16:16:28 +00:00
p = agents ;
while ( p ) {
if ( p = = newlyavailable ) {
p = p - > next ;
continue ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-07-16 02:25:53 +00:00
if ( ! p - > abouttograb & & p - > pending & & ( ( p - > group & & ( newlyavailable - > group & p - > group ) ) | | ! strcmp ( p - > agent , newlyavailable - > agent ) ) ) {
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Call '%s' looks like a winner for agent '%s' \n " , p - > owner - > name , newlyavailable - > agent ) ;
2003-07-01 16:16:28 +00:00
/* We found a pending call, time to merge */
2003-08-19 21:35:33 +00:00
chan = agent_new ( newlyavailable , AST_STATE_DOWN ) ;
2003-07-01 16:16:28 +00:00
parent = p - > owner ;
2003-07-09 22:41:51 +00:00
p - > abouttograb = 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-07-01 16:16:28 +00:00
break ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-07-01 16:16:28 +00:00
p = p - > next ;
}
if ( needlock )
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2003-07-01 16:16:28 +00:00
if ( parent & & chan ) {
2003-09-04 04:03:47 +00:00
if ( newlyavailable - > ackcall > 1 ) {
2003-08-26 00:36:03 +00:00
/* Don't do beep here */
res = 0 ;
} else {
2004-12-31 00:58:44 +00:00
if ( option_debug > 2 )
ast_log ( LOG_DEBUG , " Playing beep, lang '%s' \n " , newlyavailable - > chan - > language ) ;
2004-06-28 18:40:41 +00:00
res = ast_streamfile ( newlyavailable - > chan , beep , newlyavailable - > chan - > language ) ;
2004-12-31 00:58:44 +00:00
if ( option_debug > 2 )
ast_log ( LOG_DEBUG , " Played beep, result '%d' \n " , res ) ;
2003-08-26 00:36:03 +00:00
if ( ! res ) {
res = ast_waitstream ( newlyavailable - > chan , " " ) ;
ast_log ( LOG_DEBUG , " Waited for stream, result '%d' \n " , res ) ;
}
2003-07-01 16:16:28 +00:00
}
if ( ! res ) {
2003-07-09 22:41:51 +00:00
/* Note -- parent may have disappeared */
if ( p - > abouttograb ) {
2003-09-06 20:13:09 +00:00
newlyavailable - > acknowledged = 1 ;
2003-07-09 22:41:51 +00:00
ast_setstate ( parent , AST_STATE_UP ) ;
ast_setstate ( chan , AST_STATE_UP ) ;
2003-09-07 00:12:29 +00:00
strncpy ( parent - > context , chan - > context , sizeof ( parent - > context ) - 1 ) ;
2003-07-14 03:45:53 +00:00
/* Go ahead and mark the channel as a zombie so that masquerade will
destroy it for us , and we need not call ast_hangup */
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & parent - > lock ) ;
2004-12-07 20:38:43 +00:00
ast_set_flag ( chan , AST_FLAG_ZOMBIE ) ;
2003-07-09 22:41:51 +00:00
ast_channel_masquerade ( parent , chan ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & parent - > lock ) ;
2003-07-09 22:41:51 +00:00
p - > abouttograb = 0 ;
} else {
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Sneaky, parent disappeared in the mean time... \n " ) ;
2003-07-09 22:41:51 +00:00
agent_cleanup ( newlyavailable ) ;
}
2003-07-01 16:16:28 +00:00
} else {
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Ugh... Agent hung up at exactly the wrong time \n " ) ;
2003-07-09 22:41:51 +00:00
agent_cleanup ( newlyavailable ) ;
2003-07-01 16:16:28 +00:00
}
}
return 0 ;
}
2003-08-26 00:36:03 +00:00
static int check_beep ( struct agent_pvt * newlyavailable , int needlock )
{
struct agent_pvt * p ;
2003-08-26 03:17:00 +00:00
int res = 0 ;
2004-12-31 00:58:44 +00:00
2003-08-26 00:36:03 +00:00
ast_log ( LOG_DEBUG , " Checking beep availability of '%s' \n " , newlyavailable - > agent ) ;
if ( needlock )
ast_mutex_lock ( & agentlock ) ;
p = agents ;
while ( p ) {
if ( p = = newlyavailable ) {
p = p - > next ;
continue ;
}
ast_mutex_lock ( & p - > lock ) ;
if ( ! p - > abouttograb & & p - > pending & & ( ( p - > group & & ( newlyavailable - > group & p - > group ) ) | | ! strcmp ( p - > agent , newlyavailable - > agent ) ) ) {
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Call '%s' looks like a would-be winner for agent '%s' \n " , p - > owner - > name , newlyavailable - > agent ) ;
2003-08-26 00:36:03 +00:00
ast_mutex_unlock ( & p - > lock ) ;
break ;
}
ast_mutex_unlock ( & p - > lock ) ;
p = p - > next ;
}
if ( needlock )
ast_mutex_unlock ( & agentlock ) ;
if ( p ) {
2003-08-26 04:08:12 +00:00
ast_mutex_unlock ( & newlyavailable - > lock ) ;
2004-12-31 00:58:44 +00:00
if ( option_debug > 2 )
ast_log ( LOG_DEBUG , " Playing beep, lang '%s' \n " , newlyavailable - > chan - > language ) ;
2004-06-28 18:40:41 +00:00
res = ast_streamfile ( newlyavailable - > chan , beep , newlyavailable - > chan - > language ) ;
2004-12-31 00:58:44 +00:00
if ( option_debug > 2 )
ast_log ( LOG_DEBUG , " Played beep, result '%d' \n " , res ) ;
2003-08-26 00:36:03 +00:00
if ( ! res ) {
res = ast_waitstream ( newlyavailable - > chan , " " ) ;
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Waited for stream, result '%d' \n " , res ) ;
2003-08-26 00:36:03 +00:00
}
2003-08-26 04:08:12 +00:00
ast_mutex_lock ( & newlyavailable - > lock ) ;
2003-08-26 00:36:03 +00:00
}
return res ;
}
2004-12-31 00:58:44 +00:00
/*--- agent_request: Part of the Asterisk PBX interface ---*/
2004-10-26 22:25:43 +00:00
static struct ast_channel * agent_request ( const char * type , int format , void * data , int * cause )
2002-09-02 15:20:28 +00:00
{
struct agent_pvt * p ;
struct ast_channel * chan = NULL ;
2003-07-01 04:08:25 +00:00
char * s ;
unsigned int groupmatch ;
2003-07-01 16:16:28 +00:00
int waitforagent = 0 ;
2003-08-19 01:01:00 +00:00
int hasagent = 0 ;
2004-08-11 19:02:46 +00:00
struct timeval tv ;
2004-12-31 00:58:44 +00:00
2003-07-01 04:08:25 +00:00
s = data ;
if ( ( s [ 0 ] = = ' @ ' ) & & ( sscanf ( s + 1 , " %d " , & groupmatch ) = = 1 ) ) {
groupmatch = ( 1 < < groupmatch ) ;
2003-07-01 16:16:28 +00:00
} else if ( ( s [ 0 ] = = ' : ' ) & & ( sscanf ( s + 1 , " %d " , & groupmatch ) = = 1 ) ) {
groupmatch = ( 1 < < groupmatch ) ;
waitforagent = 1 ;
2003-07-01 04:08:25 +00:00
} else {
groupmatch = 0 ;
}
2003-08-06 04:00:37 +00:00
/* Check actual logged in agents first */
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
p = agents ;
while ( p ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-07-28 14:24:10 +00:00
if ( ! p - > pending & & ( ( groupmatch & & ( p - > group & groupmatch ) ) | | ! strcmp ( data , p - > agent ) ) & &
2004-05-09 07:51:44 +00:00
ast_strlen_zero ( p - > loginchan ) ) {
2003-08-19 01:01:00 +00:00
if ( p - > chan )
hasagent + + ;
if ( ! 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 */
2003-07-14 02:31:56 +00:00
chan = agent_new ( p , AST_STATE_DOWN ) ;
2003-08-19 01:01:00 +00:00
}
if ( chan ) {
ast_mutex_unlock ( & p - > lock ) ;
break ;
}
2002-09-02 15:20:28 +00:00
}
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
p = p - > next ;
}
2003-08-06 04:00:37 +00:00
if ( ! p ) {
p = agents ;
while ( p ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-08-19 01:01:00 +00:00
if ( ! p - > pending & & ( ( groupmatch & & ( p - > group & groupmatch ) ) | | ! strcmp ( data , p - > agent ) ) ) {
2004-05-09 07:51:44 +00:00
if ( p - > chan | | ! ast_strlen_zero ( p - > loginchan ) )
2003-08-19 01:01:00 +00:00
hasagent + + ;
2004-08-11 19:02:46 +00:00
gettimeofday ( & tv , NULL ) ;
#if 0
ast_log ( LOG_NOTICE , " Time now: %ld, Time of lastdisc: %ld \n " , tv . tv_sec , p - > lastdisc . tv_sec ) ;
# endif
if ( ! p - > lastdisc . tv_sec | | ( tv . tv_sec > p - > lastdisc . tv_sec ) ) {
2004-08-01 02:39:32 +00:00
memset ( & p - > lastdisc , 0 , sizeof ( p - > lastdisc ) ) ;
2003-08-19 01:01:00 +00:00
/* Agent must be registered, but not have any active call, and not be in a waiting state */
if ( ! p - > owner & & p - > chan ) {
/* Could still get a fixed agent */
2003-08-06 04:00:37 +00:00
chan = agent_new ( p , AST_STATE_DOWN ) ;
2004-05-09 07:51:44 +00:00
} else if ( ! p - > owner & & ! ast_strlen_zero ( p - > loginchan ) ) {
2003-08-19 01:01:00 +00:00
/* Adjustable agent */
2004-10-26 22:25:43 +00:00
p - > chan = ast_request ( " Local " , format , p - > loginchan , cause ) ;
2003-08-19 01:01:00 +00:00
if ( p - > chan )
chan = agent_new ( p , AST_STATE_DOWN ) ;
}
if ( chan ) {
ast_mutex_unlock ( & p - > lock ) ;
break ;
}
2003-08-06 04:00:37 +00:00
}
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-08-06 04:00:37 +00:00
p = p - > next ;
}
}
2003-07-01 16:16:28 +00:00
if ( ! chan & & waitforagent ) {
/* No agent available -- but we're requesting to wait for one.
Allocate a place holder */
2003-08-19 01:01:00 +00:00
if ( hasagent ) {
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Creating place holder for '%s' \n " , s ) ;
2003-08-19 01:01:00 +00:00
p = add_agent ( data , 1 ) ;
p - > group = groupmatch ;
chan = agent_new ( p , AST_STATE_DOWN ) ;
if ( ! chan ) {
ast_log ( LOG_WARNING , " Weird... Fix this to drop the unused pending agent \n " ) ;
}
} else
2003-08-19 01:20:29 +00:00
ast_log ( LOG_DEBUG , " Not creating place holder for '%s' since nobody logged in \n " , s ) ;
2003-07-01 16:16:28 +00:00
}
2004-10-26 22:25:43 +00:00
if ( hasagent )
* cause = AST_CAUSE_BUSY ;
else
* cause = AST_CAUSE_UNREGISTERED ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
return chan ;
}
2003-07-01 16:16:28 +00:00
static int powerof ( unsigned int v )
{
int x ;
for ( x = 0 ; x < 32 ; x + + ) {
if ( v & ( 1 < < x ) ) return x ;
}
return 0 ;
}
2004-12-01 05:00:29 +00:00
static int action_agents ( struct mansession * s , struct message * m )
{
2004-12-31 00:58:44 +00:00
struct agent_pvt * p ;
char * username = NULL ;
char * loginChan = NULL ;
char * talkingtoChan = NULL ;
char * status = NULL ;
ast_mutex_lock ( & agentlock ) ;
p = agents ;
while ( p ) {
ast_mutex_lock ( & p - > lock ) ;
/* Status Values:
AGENT_LOGGEDOFF - Agent isn ' t logged in
AGENT_IDLE - Agent is logged in , and waiting for call
AGENT_ONCALL - Agent is logged in , and on a call
AGENT_UNKNOWN - Don ' t know anything about agent . Shouldn ' t ever get this . */
if ( ! ast_strlen_zero ( p - > name ) ) {
username = p - > name ;
} else {
username = " None " ;
}
/* Set a default status. It 'should' get changed. */
status = " AGENT_UNKNOWN " ;
if ( p - > chan ) {
loginChan = p - > loginchan ;
if ( p - > owner & & p - > owner - > _bridge ) {
talkingtoChan = p - > chan - > cid . cid_num ;
status = " AGENT_ONCALL " ;
} else {
talkingtoChan = " n/a " ;
status = " AGENT_IDLE " ;
}
} else if ( ! ast_strlen_zero ( p - > loginchan ) ) {
loginChan = p - > loginchan ;
talkingtoChan = " n/a " ;
status = " AGENT_IDLE " ;
if ( p - > acknowledged ) {
sprintf ( loginChan , " %s (Confirmed) " , loginChan ) ;
}
} else {
loginChan = " n/a " ;
talkingtoChan = " n/a " ;
status = " AGENT_LOGGEDOFF " ;
}
ast_cli ( s - > fd , " Event: Agents \r \n "
" Agent: %s \r \n "
" Name: %s \r \n "
" Status: %s \r \n "
" LoggedInChan: %s \r \n "
" LoggedInTime: %ld \r \n "
" TalkingTo: %s \r \n "
" \r \n " ,
p - > agent , p - > name , status , loginChan , p - > loginstart , talkingtoChan ) ;
ast_mutex_unlock ( & p - > lock ) ;
p = p - > next ;
}
ast_mutex_unlock ( & agentlock ) ;
return 0 ;
2004-12-01 05:00:29 +00:00
}
2004-12-31 00:58:44 +00:00
/*--- agents_show: Show agents in cli ---*/
2002-09-02 15:20:28 +00:00
static int agents_show ( int fd , int argc , char * * argv )
{
struct agent_pvt * p ;
2004-02-03 16:57:00 +00:00
char username [ AST_MAX_BUF ] ;
2004-07-16 04:40:54 +00:00
char location [ AST_MAX_BUF ] = " " ;
char talkingto [ AST_MAX_BUF ] = " " ;
2004-02-03 16:57:00 +00:00
char moh [ AST_MAX_BUF ] ;
2002-09-02 15:20:28 +00:00
if ( argc ! = 2 )
return RESULT_SHOWUSAGE ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
p = agents ;
while ( p ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-07-01 16:16:28 +00:00
if ( p - > pending ) {
if ( p - > group )
ast_cli ( fd , " -- Pending call to group %d \n " , powerof ( p - > group ) ) ;
else
ast_cli ( fd , " -- Pending call to agent %s \n " , p - > agent ) ;
} else {
2004-05-09 07:51:44 +00:00
if ( ! ast_strlen_zero ( p - > name ) )
2003-07-01 16:16:28 +00:00
snprintf ( username , sizeof ( username ) , " (%s) " , p - > name ) ;
else
2004-07-16 04:40:54 +00:00
username [ 0 ] = ' \0 ' ;
2003-07-01 16:16:28 +00:00
if ( p - > chan ) {
snprintf ( location , sizeof ( location ) , " logged in on %s " , p - > chan - > name ) ;
2004-10-23 12:19:47 +00:00
if ( p - > owner & & ast_bridged_channel ( p - > owner ) ) {
snprintf ( talkingto , sizeof ( talkingto ) , " talking to %s " , ast_bridged_channel ( p - > owner ) - > name ) ;
2003-07-01 16:16:28 +00:00
} else {
2004-07-16 04:40:54 +00:00
strncpy ( talkingto , " is idle " , sizeof ( talkingto ) - 1 ) ;
2003-07-01 16:16:28 +00:00
}
2004-05-09 07:51:44 +00:00
} else if ( ! ast_strlen_zero ( p - > loginchan ) ) {
2003-07-14 02:31:56 +00:00
snprintf ( location , sizeof ( location ) - 20 , " available at '%s' " , p - > loginchan ) ;
2004-07-16 04:40:54 +00:00
talkingto [ 0 ] = ' \0 ' ;
2003-07-14 02:31:56 +00:00
if ( p - > acknowledged )
2004-07-16 04:40:54 +00:00
strncat ( location , " (Confirmed) " , sizeof ( location ) - strlen ( location ) - 1 ) ;
2002-09-02 15:20:28 +00:00
} else {
2004-07-16 04:40:54 +00:00
strncpy ( location , " not logged in " , sizeof ( location ) - 1 ) ;
talkingto [ 0 ] = ' \0 ' ;
2002-09-02 15:20:28 +00:00
}
2004-05-09 07:51:44 +00:00
if ( ! ast_strlen_zero ( p - > moh ) )
2003-07-28 14:24:10 +00:00
snprintf ( moh , sizeof ( moh ) , " (musiconhold is '%s') " , p - > moh ) ;
2003-07-14 04:21:00 +00:00
ast_cli ( fd , " %-12.12s %s%s%s%s \n " , p - > agent ,
username , location , talkingto , moh ) ;
2002-09-02 15:20:28 +00:00
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
p = p - > next ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
return RESULT_SUCCESS ;
}
static char show_agents_usage [ ] =
" Usage: show agents \n "
" Provides summary information on agents. \n " ;
static struct ast_cli_entry cli_show_agents = {
{ " show " , " agents " , NULL } , agents_show ,
" Show status of agents " , show_agents_usage , NULL } ;
STANDARD_LOCAL_USER ;
LOCAL_USER_DECL ;
2004-12-31 00:58:44 +00:00
/*--- __login_exec: Log in agent application ---*/
2003-07-14 02:31:56 +00:00
static int __login_exec ( struct ast_channel * chan , void * data , int callbackmode )
2002-09-02 15:20:28 +00:00
{
int res = 0 ;
int tries = 0 ;
2004-10-24 13:24:16 +00:00
int max_login_tries = maxlogintries ;
2002-09-02 15:20:28 +00:00
struct agent_pvt * p ;
struct localuser * u ;
2003-07-28 14:24:10 +00:00
struct timeval tv ;
2004-10-24 13:24:16 +00:00
int login_state = 0 ;
2004-07-16 04:40:54 +00:00
char user [ AST_MAX_AGENT ] = " " ;
2002-09-02 15:20:28 +00:00
char pass [ AST_MAX_AGENT ] ;
2004-02-14 04:54:39 +00:00
char agent [ AST_MAX_AGENT ] = " " ;
2002-09-02 15:20:28 +00:00
char xpass [ AST_MAX_AGENT ] = " " ;
char * errmsg ;
2002-12-06 16:22:19 +00:00
char info [ 512 ] ;
char * opt_user = NULL ;
char * options = NULL ;
2004-10-24 13:24:16 +00:00
char option ;
char badoption [ 2 ] ;
char * tmpoptions = NULL ;
2003-08-03 18:22:12 +00:00
char * context = NULL ;
2003-08-14 21:27:24 +00:00
char * exten = NULL ;
2004-10-24 13:24:16 +00:00
int play_announcement = 1 ;
char agent_goodbye [ AST_MAX_FILENAME_LEN ] ;
int update_cdr = updatecdr ;
2003-07-14 02:31:56 +00:00
char * filename = " agent-loginok " ;
2002-12-06 16:22:19 +00:00
2004-10-24 18:02:32 +00:00
strcpy ( agent_goodbye , agentgoodbye ) ;
2002-09-02 15:20:28 +00:00
LOCAL_USER_ADD ( u ) ;
2002-12-06 16:22:19 +00:00
/* Parse the arguments XXX Check for failure XXX */
strncpy ( info , ( char * ) data , strlen ( ( char * ) data ) + AST_MAX_EXTENSION - 1 ) ;
opt_user = info ;
2004-10-24 13:24:16 +00:00
/* 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 " ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
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 ) ;
2004-10-24 13:24:16 +00:00
}
2004-12-31 00:58:44 +00:00
if ( pbx_builtin_getvar_helper ( chan , " AGENTUPDATECDR " ) & & ! ast_strlen_zero ( pbx_builtin_getvar_helper ( chan , " AGENTUPDATECDR " ) ) ) {
2004-10-24 13:24:16 +00:00
if ( ast_true ( pbx_builtin_getvar_helper ( chan , " AGENTUPDATECDR " ) ) )
update_cdr = 1 ;
else
update_cdr = 0 ;
tmpoptions = pbx_builtin_getvar_helper ( chan , " AGENTUPDATECDR " ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'. \n " , tmpoptions , update_cdr , chan - > name ) ;
2004-10-24 13:24:16 +00:00
}
2004-12-31 00:58:44 +00:00
if ( pbx_builtin_getvar_helper ( chan , " AGENTGOODBYE " ) & & ! ast_strlen_zero ( pbx_builtin_getvar_helper ( chan , " AGENTGOODBYE " ) ) ) {
2004-10-24 13:24:16 +00:00
strcpy ( agent_goodbye , pbx_builtin_getvar_helper ( chan , " AGENTGOODBYE " ) ) ;
tmpoptions = pbx_builtin_getvar_helper ( chan , " AGENTGOODBYE " ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'. \n " , tmpoptions , agent_goodbye , chan - > name ) ;
2004-10-24 13:24:16 +00:00
}
/* End Channel Specific Login Overrides */
/* Read command line options */
if ( opt_user ) {
2002-12-06 16:22:19 +00:00
options = strchr ( opt_user , ' | ' ) ;
if ( options ) {
* options = ' \0 ' ;
options + + ;
2003-08-03 18:22:12 +00:00
if ( callbackmode ) {
context = strchr ( options , ' @ ' ) ;
if ( context ) {
* context = ' \0 ' ;
context + + ;
}
2003-08-14 21:27:24 +00:00
exten = options ;
while ( * exten & & ( ( * exten < ' 0 ' ) | | ( * exten > ' 9 ' ) ) ) exten + + ;
if ( ! * exten )
exten = NULL ;
2003-08-03 18:22:12 +00:00
}
2002-12-06 16:22:19 +00:00
}
2004-12-09 22:39:14 +00:00
if ( options ) {
2004-10-24 13:24:16 +00:00
while ( * options ) {
option = ( char ) options [ 0 ] ;
2004-12-09 22:39:14 +00:00
if ( ( option > = 0 ) & & ( option < = ' 9 ' ) )
{
options + + ;
continue ;
}
2004-10-24 13:24:16 +00:00
if ( option = = ' s ' )
play_announcement = 0 ;
else {
badoption [ 0 ] = option ;
badoption [ 1 ] = ' \0 ' ;
tmpoptions = badoption ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Warning: option %s is unknown. \n " , tmpoptions ) ;
2004-10-24 13:24:16 +00:00
}
options + + ;
}
}
2002-12-06 16:22:19 +00:00
}
2004-10-24 13:24:16 +00:00
/* End command line options */
2002-12-06 16:22:19 +00:00
2002-09-02 15:20:28 +00:00
if ( chan - > _state ! = AST_STATE_UP )
res = ast_answer ( chan ) ;
2002-12-06 16:22:19 +00:00
if ( ! res ) {
2004-05-09 07:51:44 +00:00
if ( opt_user & & ! ast_strlen_zero ( opt_user ) )
2004-07-16 04:40:54 +00:00
strncpy ( user , opt_user , AST_MAX_AGENT - 1 ) ;
2002-12-06 16:22:19 +00:00
else
res = ast_app_getdata ( chan , " agent-user " , user , sizeof ( user ) - 1 , 0 ) ;
}
2004-10-24 13:24:16 +00:00
while ( ! res & & ( max_login_tries = = 0 | | tries < max_login_tries ) ) {
tries + + ;
2002-09-02 15:20:28 +00:00
/* Check for password */
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
p = agents ;
while ( p ) {
2003-07-01 16:16:28 +00:00
if ( ! strcmp ( p - > agent , user ) & & ! p - > pending )
2002-09-02 15:20:28 +00:00
strncpy ( xpass , p - > password , sizeof ( xpass ) - 1 ) ;
p = p - > next ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
if ( ! res ) {
2004-05-09 07:51:44 +00:00
if ( ! ast_strlen_zero ( xpass ) )
2002-09-02 15:20:28 +00:00
res = ast_app_getdata ( chan , " agent-pass " , pass , sizeof ( pass ) - 1 , 0 ) ;
else
2004-07-16 04:40:54 +00:00
pass [ 0 ] = ' \0 ' ;
2002-09-02 15:20:28 +00:00
}
errmsg = " agent-incorrect " ;
#if 0
ast_log ( LOG_NOTICE , " user: %s, pass: %s \n " , user , pass ) ;
# endif
/* Check again for accuracy */
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
p = agents ;
while ( p ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
if ( ! strcmp ( p - > agent , user ) & &
2003-07-01 16:16:28 +00:00
! strcmp ( p - > password , pass ) & & ! p - > pending ) {
2004-10-24 13:24:16 +00:00
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 " ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'. \n " , tmpoptions , p - > ackcall , p - > agent ) ;
2004-10-24 13:24:16 +00:00
}
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 " ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'. \n " , tmpoptions , p - > autologoff , p - > agent ) ;
2004-10-24 13:24:16 +00:00
}
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 " ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'. \n " , tmpoptions , p - > wrapuptime , p - > agent ) ;
2004-10-24 13:24:16 +00:00
}
/* End Channel Specific Agent Overides */
2002-09-02 15:20:28 +00:00
if ( ! p - > chan ) {
2004-06-03 17:33:35 +00:00
char last_loginchan [ 80 ] = " " ;
long logintime ;
snprintf ( agent , sizeof ( agent ) , " Agent/%s " , p - > agent ) ;
2003-07-14 02:31:56 +00:00
if ( callbackmode ) {
2004-02-03 16:57:00 +00:00
char tmpchan [ AST_MAX_BUF ] = " " ;
2003-08-16 15:31:18 +00:00
int pos = 0 ;
2003-07-14 02:31:56 +00:00
/* Retrieve login chan */
2003-08-16 15:31:18 +00:00
for ( ; ; ) {
if ( exten ) {
strncpy ( tmpchan , exten , sizeof ( tmpchan ) - 1 ) ;
res = 0 ;
} else
res = ast_app_getdata ( chan , " agent-newlocation " , tmpchan + pos , sizeof ( tmpchan ) - 2 , 0 ) ;
2004-05-09 07:51:44 +00:00
if ( ast_strlen_zero ( tmpchan ) | | ast_exists_extension ( chan , context & & ! ast_strlen_zero ( context ) ? context : " default " , tmpchan ,
2003-08-16 15:31:18 +00:00
1 , NULL ) )
break ;
if ( exten ) {
ast_log ( LOG_WARNING , " Extension '%s' is not valid for automatic login of agent '%s' \n " , exten , p - > agent ) ;
exten = NULL ;
pos = 0 ;
} else {
2004-11-01 01:44:11 +00:00
ast_log ( LOG_WARNING , " Extension '%s@%s' is not valid for automatic login of agent '%s' \n " , tmpchan , context & & ! ast_strlen_zero ( context ) ? context : " default " , p - > agent ) ;
2003-08-16 15:31:18 +00:00
res = ast_streamfile ( chan , " invalid " , chan - > language ) ;
if ( ! res )
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
if ( res > 0 ) {
tmpchan [ 0 ] = res ;
tmpchan [ 1 ] = ' \0 ' ;
pos = 1 ;
} else {
tmpchan [ 0 ] = ' \0 ' ;
pos = 0 ;
}
}
}
2004-10-24 13:24:16 +00:00
exten = tmpchan ;
2003-07-14 02:31:56 +00:00
if ( ! res ) {
2004-05-09 07:51:44 +00:00
if ( context & & ! ast_strlen_zero ( context ) & & ! ast_strlen_zero ( tmpchan ) )
2003-08-03 18:22:12 +00:00
snprintf ( p - > loginchan , sizeof ( p - > loginchan ) , " %s@%s " , tmpchan , context ) ;
2004-06-03 17:33:35 +00:00
else {
strncpy ( last_loginchan , p - > loginchan , sizeof ( last_loginchan ) - 1 ) ;
2003-08-03 18:22:12 +00:00
strncpy ( p - > loginchan , tmpchan , sizeof ( p - > loginchan ) - 1 ) ;
2004-06-03 17:33:35 +00:00
}
2004-10-24 13:24:16 +00:00
if ( ast_strlen_zero ( p - > loginchan ) ) {
login_state = 2 ;
2003-07-14 02:31:56 +00:00
filename = " agent-loggedoff " ;
2004-10-24 13:24:16 +00:00
}
2003-07-14 02:31:56 +00:00
p - > acknowledged = 0 ;
2004-02-10 21:14:48 +00:00
/* store/clear the global variable that stores agentid based on the callerid */
2004-10-02 00:58:31 +00:00
if ( chan - > cid . cid_num ) {
2004-02-05 21:13:17 +00:00
char agentvar [ AST_MAX_BUF ] ;
2004-10-02 00:58:31 +00:00
snprintf ( agentvar , sizeof ( agentvar ) , " %s_%s " , GETAGENTBYCALLERID , chan - > cid . cid_num ) ;
2004-05-09 07:51:44 +00:00
if ( ast_strlen_zero ( p - > loginchan ) )
2004-02-10 21:14:48 +00:00
pbx_builtin_setvar_helper ( NULL , agentvar , NULL ) ;
else
pbx_builtin_setvar_helper ( NULL , agentvar , p - > agent ) ;
2004-02-05 21:13:17 +00:00
}
2004-10-24 13:24:16 +00:00
if ( update_cdr & & chan - > cdr )
2004-04-03 00:41:47 +00:00
snprintf ( chan - > cdr - > channel , sizeof ( chan - > cdr - > channel ) , " Agent/%s " , p - > agent ) ;
2003-07-14 02:31:56 +00:00
}
} else {
2004-07-16 04:40:54 +00:00
p - > loginchan [ 0 ] = ' \0 ' ;
2003-07-14 02:31:56 +00:00
p - > acknowledged = 0 ;
}
2003-11-08 04:35:57 +00:00
ast_mutex_unlock ( & p - > lock ) ;
ast_mutex_unlock ( & agentlock ) ;
2004-10-24 13:24:16 +00:00
if ( ! res & & play_announcement = = 1 )
2003-07-14 02:31:56 +00:00
res = ast_streamfile ( chan , filename , chan - > language ) ;
2002-09-02 15:20:28 +00:00
if ( ! res )
ast_waitstream ( chan , " " ) ;
2003-11-08 04:35:57 +00:00
ast_mutex_lock ( & agentlock ) ;
ast_mutex_lock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
if ( ! res ) {
2004-04-06 22:17:32 +00:00
res = ast_set_read_format ( chan , ast_best_codec ( chan - > nativeformats ) ) ;
2002-09-02 15:20:28 +00:00
if ( res )
ast_log ( LOG_WARNING , " Unable to set read format to %d \n " , ast_best_codec ( chan - > nativeformats ) ) ;
}
if ( ! res ) {
2004-04-06 22:17:32 +00:00
ast_set_write_format ( chan , ast_best_codec ( chan - > nativeformats ) ) ;
2002-09-02 15:20:28 +00:00
if ( res )
ast_log ( LOG_WARNING , " Unable to set write format to %d \n " , ast_best_codec ( chan - > nativeformats ) ) ;
}
/* Check once more just in case */
if ( p - > chan )
res = - 1 ;
2003-07-14 02:31:56 +00:00
if ( callbackmode & & ! res ) {
/* Just say goodbye and be done with it */
2004-06-03 17:33:35 +00:00
if ( ! ast_strlen_zero ( p - > loginchan ) ) {
if ( p - > loginstart = = 0 )
time ( & p - > loginstart ) ;
manager_event ( EVENT_FLAG_AGENT , " Agentcallbacklogin " ,
" Agent: %s \r \n "
" Loginchan: %s \r \n "
" Uniqueid: %s \r \n " ,
p - > agent , p - > loginchan , chan - > uniqueid ) ;
ast_queue_log ( " NONE " , chan - > uniqueid , agent , " AGENTCALLBACKLOGIN " , " %s " , p - > loginchan ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Callback Agent '%s' logged in on %s \n " , p - > agent , p - > loginchan ) ;
2005-01-01 00:57:04 +00:00
ast_device_state_changed ( " Agent/%s " , p - > agent ) ;
2004-06-03 17:33:35 +00:00
} else {
logintime = time ( NULL ) - p - > loginstart ;
p - > loginstart = 0 ;
manager_event ( EVENT_FLAG_AGENT , " Agentcallbacklogoff " ,
" Agent: %s \r \n "
" Loginchan: %s \r \n "
" Logintime: %ld \r \n "
" Uniqueid: %s \r \n " ,
p - > agent , last_loginchan , logintime , chan - > uniqueid ) ;
ast_queue_log ( " NONE " , chan - > uniqueid , agent , " AGENTCALLBACKLOGOFF " , " %s|%ld| " , last_loginchan , logintime ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Callback Agent '%s' logged out \n " , p - > agent ) ;
2005-01-01 00:57:04 +00:00
ast_device_state_changed ( " Agent/%s " , p - > agent ) ;
2004-06-03 17:33:35 +00:00
}
2003-11-08 04:35:57 +00:00
ast_mutex_unlock ( & agentlock ) ;
2003-07-14 02:31:56 +00:00
if ( ! res )
res = ast_safe_sleep ( chan , 500 ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2005-01-01 00:57:04 +00:00
if ( persistent_agents )
dump_agents ( ) ;
2003-07-14 02:31:56 +00:00
} else if ( ! res ) {
2003-08-14 21:04:54 +00:00
# ifdef HONOR_MUSIC_CLASS
2003-06-16 16:58:47 +00:00
/* check if the moh class was changed with setmusiconhold */
if ( * ( chan - > musicclass ) )
strncpy ( p - > moh , chan - > musicclass , sizeof ( p - > moh ) - 1 ) ;
2003-08-14 21:04:54 +00:00
# endif
2002-09-02 15:20:28 +00:00
ast_moh_start ( chan , p - > moh ) ;
2004-06-03 17:33:35 +00:00
if ( p - > loginstart = = 0 )
time ( & p - > loginstart ) ;
2002-09-02 15:20:28 +00:00
manager_event ( EVENT_FLAG_AGENT , " Agentlogin " ,
" Agent: %s \r \n "
2004-06-03 17:33:35 +00:00
" Channel: %s \r \n "
" Uniqueid: %s \r \n " ,
p - > agent , chan - > name , chan - > uniqueid ) ;
2004-10-24 13:24:16 +00:00
if ( update_cdr & & chan - > cdr )
2004-04-03 00:41:47 +00:00
snprintf ( chan - > cdr - > channel , sizeof ( chan - > cdr - > channel ) , " Agent/%s " , p - > agent ) ;
2004-02-14 04:54:39 +00:00
ast_queue_log ( " NONE " , chan - > uniqueid , agent , " AGENTLOGIN " , " %s " , chan - > name ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Agent '%s' logged in (format %s/%s) \n " , p - > agent ,
2003-08-16 05:10:35 +00:00
ast_getformatname ( chan - > readformat ) , ast_getformatname ( chan - > writeformat ) ) ;
2002-09-02 15:20:28 +00:00
/* Login this channel and wait for it to
go away */
p - > chan = chan ;
2003-08-26 00:36:03 +00:00
if ( p - > ackcall > 1 )
check_beep ( p , 0 ) ;
else
check_availability ( p , 0 ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
ast_mutex_unlock ( & agentlock ) ;
2004-12-31 00:58:44 +00:00
ast_device_state_changed ( " Agent/%s " , p - > agent ) ;
2002-12-06 16:22:19 +00:00
while ( res > = 0 ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-12-06 16:22:19 +00:00
if ( p - > chan ! = chan )
res = - 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-03-20 17:21:54 +00:00
/* Yield here so other interested threads can kick in. */
sched_yield ( ) ;
2002-12-06 16:22:19 +00:00
if ( res )
break ;
2003-09-06 19:10:08 +00:00
ast_mutex_lock ( & agentlock ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2003-07-28 14:24:10 +00:00
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 ) {
2004-12-31 00:58:44 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Wrapup time for %s expired! \n " , p - > agent ) ;
2003-07-28 14:24:10 +00:00
memset ( & p - > lastdisc , 0 , sizeof ( p - > lastdisc ) ) ;
2003-08-26 00:36:03 +00:00
if ( p - > ackcall > 1 )
check_beep ( p , 0 ) ;
else
check_availability ( p , 0 ) ;
2003-07-28 14:24:10 +00:00
}
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-09-06 19:10:08 +00:00
ast_mutex_unlock ( & agentlock ) ;
2002-12-06 16:22:19 +00:00
/* Synchronize channel ownership between call to agent and itself. */
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > app_lock ) ;
ast_mutex_lock ( & p - > lock ) ;
2002-12-06 16:22:19 +00:00
p - > owning_app = pthread_self ( ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2003-08-26 04:53:49 +00:00
if ( p - > ackcall > 1 )
2003-08-26 00:36:03 +00:00
res = agent_ack_sleep ( p ) ;
else
res = ast_safe_sleep_conditional ( chan , 1000 ,
2003-08-26 04:53:49 +00:00
agent_cont_sleep , p ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > app_lock ) ;
2003-08-26 04:53:49 +00:00
if ( ( p - > ackcall > 1 ) & & ( res = = 1 ) ) {
2003-09-06 19:10:08 +00:00
ast_mutex_lock ( & agentlock ) ;
2003-08-26 04:53:49 +00:00
ast_mutex_lock ( & p - > lock ) ;
check_availability ( p , 0 ) ;
ast_mutex_unlock ( & p - > lock ) ;
2003-09-06 19:10:08 +00:00
ast_mutex_unlock ( & agentlock ) ;
2003-08-26 05:21:22 +00:00
res = 0 ;
2003-08-26 04:53:49 +00:00
}
2003-03-20 17:21:54 +00:00
sched_yield ( ) ;
2002-09-02 15:20:28 +00:00
}
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
if ( res & & p - > owner )
ast_log ( LOG_WARNING , " Huh? We broke out when there was still an owner? \n " ) ;
/* Log us off if appropriate */
if ( p - > chan = = chan )
p - > chan = NULL ;
2003-07-14 02:31:56 +00:00
p - > acknowledged = 0 ;
2004-06-03 17:33:35 +00:00
logintime = time ( NULL ) - p - > loginstart ;
p - > loginstart = 0 ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2004-06-03 17:33:35 +00:00
manager_event ( EVENT_FLAG_AGENT , " Agentlogoff " ,
" Agent: %s \r \n "
" Logintime: %ld \r \n "
" Uniqueid: %s \r \n " ,
p - > agent , logintime , chan - > uniqueid ) ;
ast_queue_log ( " NONE " , chan - > uniqueid , agent , " AGENTLOGOFF " , " %s|%ld " , chan - > name , logintime ) ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Agent '%s' logged out \n " , p - > agent ) ;
2002-09-02 15:20:28 +00:00
/* If there is no owner, go ahead and kill it now */
2004-12-31 00:58:44 +00:00
ast_device_state_changed ( " Agent/%s " , p - > agent ) ;
2004-06-22 17:42:14 +00:00
if ( p - > dead & & ! p - > owner ) {
ast_mutex_destroy ( & p - > lock ) ;
ast_mutex_destroy ( & p - > app_lock ) ;
2002-09-02 15:20:28 +00:00
free ( p ) ;
2004-06-22 17:42:14 +00:00
}
2002-09-02 15:20:28 +00:00
}
2002-12-06 16:22:19 +00:00
else {
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-12-06 16:22:19 +00:00
p = NULL ;
}
2002-09-02 15:20:28 +00:00
res = - 1 ;
} else {
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
errmsg = " agent-alreadyon " ;
p = NULL ;
}
break ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & p - > lock ) ;
2002-09-02 15:20:28 +00:00
p = p - > next ;
}
if ( ! p )
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
2004-10-24 13:24:16 +00:00
if ( ! res & & ( max_login_tries = = 0 | | tries < max_login_tries ) )
2002-09-02 15:20:28 +00:00
res = ast_app_getdata ( chan , errmsg , user , sizeof ( user ) - 1 , 0 ) ;
}
LOCAL_USER_REMOVE ( u ) ;
2004-10-24 13:24:16 +00:00
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 - > cid . cid_num ) )
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 ;
2002-09-02 15:20:28 +00:00
}
2003-07-14 02:31:56 +00:00
static int login_exec ( struct ast_channel * chan , void * data )
{
return __login_exec ( chan , data , 0 ) ;
}
static int callback_exec ( struct ast_channel * chan , void * data )
{
return __login_exec ( chan , data , 1 ) ;
}
2002-09-02 15:20:28 +00:00
2004-02-05 21:13:17 +00:00
static int agentmonitoroutgoing_exec ( struct ast_channel * chan , void * data )
{
int exitifnoagentid = 0 ;
int nowarnings = 0 ;
2004-12-10 17:37:20 +00:00
int changeoutgoing = 0 ;
2004-02-05 21:13:17 +00:00
int res = 0 ;
char agent [ AST_MAX_AGENT ] , * tmp ;
2004-12-31 00:58:44 +00:00
2004-02-05 21:13:17 +00:00
if ( data ) {
if ( strchr ( data , ' d ' ) )
exitifnoagentid = 1 ;
if ( strchr ( data , ' n ' ) )
nowarnings = 1 ;
2004-12-10 17:37:20 +00:00
if ( strchr ( data , ' c ' ) )
changeoutgoing = 1 ;
2004-02-05 21:13:17 +00:00
}
2004-10-02 00:58:31 +00:00
if ( chan - > cid . cid_num ) {
2004-02-05 21:13:17 +00:00
char agentvar [ AST_MAX_BUF ] ;
2004-10-02 00:58:31 +00:00
snprintf ( agentvar , sizeof ( agentvar ) , " %s_%s " , GETAGENTBYCALLERID , chan - > cid . cid_num ) ;
2004-02-05 21:13:17 +00:00
if ( ( tmp = pbx_builtin_getvar_helper ( NULL , agentvar ) ) ) {
struct agent_pvt * p = agents ;
strncpy ( agent , tmp , sizeof ( agent ) - 1 ) ;
ast_mutex_lock ( & agentlock ) ;
while ( p ) {
if ( ! strcasecmp ( p - > agent , tmp ) ) {
2004-12-10 17:37:20 +00:00
if ( changeoutgoing ) snprintf ( chan - > cdr - > channel , sizeof ( chan - > cdr - > channel ) , " Agent/%s " , p - > agent ) ;
2004-02-05 21:13:17 +00:00
__agent_start_monitoring ( chan , p , 1 ) ;
break ;
}
p = p - > next ;
}
ast_mutex_unlock ( & agentlock ) ;
} else {
res = - 1 ;
if ( ! nowarnings )
ast_log ( LOG_WARNING , " Couldn't find the global variable %s, so I can't figure out which agent (if it's an agent) is placing outgoing call. \n " , agentvar ) ;
}
} else {
res = - 1 ;
if ( ! nowarnings )
ast_log ( LOG_WARNING , " There is no callerid on that call, so I can't figure out which agent (if it's an agent) is placing outgoing call. \n " ) ;
}
/* check if there is n + 101 priority */
if ( res ) {
2004-10-02 00:58:31 +00:00
if ( ast_exists_extension ( chan , chan - > context , chan - > exten , chan - > priority + 101 , chan - > cid . cid_num ) ) {
2004-02-05 21:13:17 +00:00
chan - > priority + = 100 ;
2004-12-31 00:58:44 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Going to %d priority because there is no callerid or the agentid cannot be found. \n " , chan - > priority ) ;
2004-02-05 21:13:17 +00:00
}
else if ( exitifnoagentid )
return res ;
}
return 0 ;
}
2005-01-01 00:57:04 +00:00
/* Dump AgentCallbackLogin agents to the database for persistence
* ( basically copied from dump_queue_members ( ) in apps / app_queue . c )
*/
static void dump_agents ( void )
{
struct agent_pvt * cur_agent = NULL ;
cur_agent = agents ;
while ( cur_agent ) {
if ( cur_agent - > chan ! = NULL ) {
cur_agent = cur_agent - > next ;
continue ;
}
if ( ! ast_strlen_zero ( cur_agent - > loginchan ) ) {
if ( ast_db_put ( pa_family , cur_agent - > agent , cur_agent - > loginchan ) ) {
ast_log ( LOG_WARNING , " failed to create persistent entry! \n " ) ;
} else {
if ( option_debug ) {
ast_log ( LOG_DEBUG , " Saved Agent: %s on %s \n " ,
cur_agent - > agent , cur_agent - > loginchan ) ;
}
}
} else {
/* Delete - no agent or there is an error */
ast_db_del ( pa_family , cur_agent - > agent ) ;
}
cur_agent = cur_agent - > next ;
}
}
/* Reload the persistent agents from astdb */
static void reload_agents ( void )
{
char * pa_agent_num ;
struct ast_db_entry * pa_db_tree = NULL ;
int pa_family_len = 0 ;
struct agent_pvt * cur_agent = NULL ;
char agent_data [ 80 ] ;
pa_db_tree = ast_db_gettree ( pa_family , NULL ) ;
pa_family_len = strlen ( pa_family ) ;
ast_mutex_lock ( & agentlock ) ;
while ( pa_db_tree ) {
pa_agent_num = pa_db_tree - > key + pa_family_len + 2 ;
cur_agent = agents ;
while ( cur_agent ) {
ast_mutex_lock ( & cur_agent - > lock ) ;
if ( strcmp ( pa_agent_num , cur_agent - > agent ) = = 0 )
break ;
ast_mutex_unlock ( & cur_agent - > lock ) ;
cur_agent = cur_agent - > next ;
}
if ( ! cur_agent ) {
ast_db_del ( pa_family , pa_agent_num ) ;
pa_db_tree = pa_db_tree - > next ;
continue ;
} else
ast_mutex_unlock ( & cur_agent - > lock ) ;
if ( ! ast_db_get ( pa_family , pa_agent_num , agent_data , 80 ) ) {
if ( option_debug ) {
ast_log ( LOG_DEBUG , " Reload Agent: %s on %s \n " ,
cur_agent - > agent , agent_data ) ;
}
strncpy ( cur_agent - > loginchan , agent_data , 80 ) ;
if ( cur_agent - > loginstart = = 0 )
time ( & cur_agent - > loginstart ) ;
ast_device_state_changed ( " Agent/%s " , cur_agent - > agent ) ;
}
pa_db_tree = pa_db_tree - > next ;
}
ast_log ( LOG_NOTICE , " Agents sucessfully reloaded from database. \n " ) ;
ast_mutex_unlock ( & agentlock ) ;
if ( pa_db_tree ) {
ast_db_freetree ( pa_db_tree ) ;
pa_db_tree = NULL ;
}
}
2004-12-31 00:58:44 +00:00
/*--- agent_devicestate: Part of PBX channel interface ---*/
2004-11-12 03:50:19 +00:00
static int agent_devicestate ( void * data )
{
struct agent_pvt * p ;
char * s ;
unsigned int groupmatch ;
int waitforagent = 0 ;
int res = AST_DEVICE_INVALID ;
s = data ;
if ( ( s [ 0 ] = = ' @ ' ) & & ( sscanf ( s + 1 , " %d " , & groupmatch ) = = 1 ) ) {
groupmatch = ( 1 < < groupmatch ) ;
} else if ( ( s [ 0 ] = = ' : ' ) & & ( sscanf ( s + 1 , " %d " , & groupmatch ) = = 1 ) ) {
groupmatch = ( 1 < < groupmatch ) ;
waitforagent = 1 ;
} else {
groupmatch = 0 ;
}
/* Check actual logged in agents first */
ast_mutex_lock ( & agentlock ) ;
p = agents ;
while ( p ) {
ast_mutex_lock ( & p - > lock ) ;
if ( ! p - > pending & & ( ( groupmatch & & ( p - > group & groupmatch ) ) | | ! strcmp ( data , p - > agent ) ) ) {
if ( p - > owner ) {
if ( res ! = AST_DEVICE_INUSE )
res = AST_DEVICE_BUSY ;
} else {
if ( res = = AST_DEVICE_BUSY )
res = AST_DEVICE_INUSE ;
if ( p - > chan | | ! ast_strlen_zero ( p - > loginchan ) ) {
if ( res = = AST_DEVICE_INVALID )
res = AST_DEVICE_UNKNOWN ;
} else if ( res = = AST_DEVICE_INVALID )
res = AST_DEVICE_UNAVAILABLE ;
}
if ( ! strcmp ( data , p - > agent ) ) {
ast_mutex_unlock ( & p - > lock ) ;
break ;
}
}
ast_mutex_unlock ( & p - > lock ) ;
p = p - > next ;
}
ast_mutex_unlock ( & agentlock ) ;
return res ;
}
2004-12-31 00:58:44 +00:00
/*--- load_module: Initialize channel module ---*/
2002-09-02 15:20:28 +00:00
int load_module ( )
{
2004-12-31 00:58:44 +00:00
/* Make sure we can register our agent channel type */
if ( ast_channel_register_ex ( channeltype , tdesc , capability , agent_request , agent_devicestate ) ) {
ast_log ( LOG_ERROR , " Unable to register channel class %s \n " , channeltype ) ;
2002-09-02 15:20:28 +00:00
return - 1 ;
}
2004-12-31 00:58:44 +00:00
/* Dialplan applications */
2002-09-02 15:20:28 +00:00
ast_register_application ( app , login_exec , synopsis , descrip ) ;
2003-07-14 02:31:56 +00:00
ast_register_application ( app2 , callback_exec , synopsis2 , descrip2 ) ;
2004-02-05 21:13:17 +00:00
ast_register_application ( app3 , agentmonitoroutgoing_exec , synopsis3 , descrip3 ) ;
2004-12-31 00:58:44 +00:00
/* Manager command */
2004-12-01 05:00:29 +00:00
ast_manager_register2 ( " Agents " , 0 , action_agents , " Agents " , mandescr_agents ) ;
2004-12-31 00:58:44 +00:00
/* CLI Application */
2002-09-02 15:20:28 +00:00
ast_cli_register ( & cli_show_agents ) ;
/* Read in the config */
read_agent_config ( ) ;
2005-01-01 00:57:04 +00:00
if ( persistent_agents )
reload_agents ( ) ;
2002-09-02 15:20:28 +00:00
return 0 ;
}
int reload ( )
{
read_agent_config ( ) ;
2005-01-01 00:57:04 +00:00
if ( persistent_agents )
reload_agents ( ) ;
2002-09-02 15:20:28 +00:00
return 0 ;
}
int unload_module ( )
{
struct agent_pvt * p ;
/* First, take us out of the channel loop */
2004-12-31 00:58:44 +00:00
/* Unregister CLI application */
2002-09-02 15:20:28 +00:00
ast_cli_unregister ( & cli_show_agents ) ;
2004-12-31 00:58:44 +00:00
/* Unregister dialplan applications */
2002-09-02 15:20:28 +00:00
ast_unregister_application ( app ) ;
2003-07-14 02:31:56 +00:00
ast_unregister_application ( app2 ) ;
2004-02-05 21:13:17 +00:00
ast_unregister_application ( app3 ) ;
2004-12-31 00:58:44 +00:00
/* Unregister manager command */
2004-12-01 05:00:29 +00:00
ast_manager_unregister ( " Agents " ) ;
2004-12-31 00:58:44 +00:00
/* Unregister channel */
ast_channel_unregister ( channeltype ) ;
2003-08-13 15:25:16 +00:00
if ( ! ast_mutex_lock ( & agentlock ) ) {
2002-09-02 15:20:28 +00:00
/* Hangup all interfaces if they have an owner */
p = agents ;
while ( p ) {
if ( p - > owner )
ast_softhangup ( p - > owner , AST_SOFTHANGUP_APPUNLOAD ) ;
p = p - > next ;
}
agents = NULL ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & agentlock ) ;
2002-09-02 15:20:28 +00:00
} else {
ast_log ( LOG_WARNING , " Unable to lock the monitor \n " ) ;
return - 1 ;
}
return 0 ;
}
int usecount ( )
{
int res ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & usecnt_lock ) ;
2002-09-02 15:20:28 +00:00
res = usecnt ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & usecnt_lock ) ;
2002-09-02 15:20:28 +00:00
return res ;
}
char * key ( )
{
return ASTERISK_GPL_KEY ;
}
char * description ( )
{
return desc ;
}