Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:01:03 2007

Asterisk developer's documentation :: Codename Pineapple


chan_agent.c File Reference


Detailed Description

Implementation of Agents (proxy channel).

Author:
Mark Spencer <markster@digium.com>
This file is the implementation of Agents modules. It is a dynamic module that is loaded by Asterisk.
See also

Definition in file chan_agent.c.

#include "asterisk.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.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"
#include "asterisk/features.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/monitor.h"
#include "asterisk/stringfields.h"

Include dependency graph for chan_agent.c:

Go to the source code of this file.

Data Structures

struct  agent_pvt
 Structure representing an agent. More...

Defines

#define AST_MAX_AGENT   80
#define AST_MAX_BUF   256
#define AST_MAX_FILENAME_LEN   256
#define CHECK_FORMATS(ast, p)
#define CLEANUP(ast, p)
 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.
#define GETAGENTBYCALLERID   "AGENTBYCALLERID"
#define PA_MAX_LEN   2048

Functions

static int __agent_start_monitoring (struct ast_channel *ast, struct agent_pvt *p, int needlock)
static int __login_exec (struct ast_channel *chan, void *data, int callbackmode)
 Log in agent application.
static int action_agent_logoff (struct mansession *s, const struct message *m)
static int action_agents (struct mansession *s, const struct message *m)
static struct agent_pvtadd_agent (char *agent, int pending)
static int agent_ack_sleep (void *data)
static int agent_answer (struct ast_channel *ast)
static struct ast_channelagent_bridgedchannel (struct ast_channel *chan, struct ast_channel *bridge)
static int agent_call (struct ast_channel *ast, char *dest, int timeout)
static int agent_cleanup (struct agent_pvt *p)
static int agent_cont_sleep (void *data)
static int agent_devicestate (void *data)
 Part of PBX channel interface.
static int agent_digit_begin (struct ast_channel *ast, char digit)
static int agent_digit_end (struct ast_channel *ast, char digit, unsigned int duration)
static int agent_fixup (struct ast_channel *oldchan, struct ast_channel *newchan)
static int agent_hangup (struct ast_channel *ast)
static int agent_indicate (struct ast_channel *ast, int condition, const void *data, size_t datalen)
static int agent_logoff (const char *agent, int soft)
static int agent_logoff_cmd (int fd, int argc, char **argv)
static void agent_logoff_maintenance (struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
static struct ast_channelagent_new (struct agent_pvt *p, int state)
 Create new agent channel.
static struct ast_frameagent_read (struct ast_channel *ast)
static struct ast_channelagent_request (const char *type, int format, void *data, int *cause)
 Part of the Asterisk PBX interface.
static int agent_sendhtml (struct ast_channel *ast, int subclass, const char *data, int datalen)
static int agent_sendtext (struct ast_channel *ast, const char *text)
static int agent_start_monitoring (struct ast_channel *ast, int needlock)
static int agent_write (struct ast_channel *ast, struct ast_frame *f)
static int agentmonitoroutgoing_exec (struct ast_channel *chan, void *data)
 Called by the AgentMonitorOutgoing application (from the dial plan).
static int agents_show (int fd, int argc, char **argv)
static int agents_show_online (int fd, int argc, char **argv)
static int allow_multiple_login (char *chan, char *context)
static AST_LIST_HEAD_STATIC (agents, agent_pvt)
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,"Agent Proxy Channel",.load=load_module,.unload=unload_module,.reload=reload,)
static int check_availability (struct agent_pvt *newlyavailable, int needlock)
static int check_beep (struct agent_pvt *newlyavailable, int needlock)
static char * complete_agent_logoff_cmd (const char *line, const char *word, int pos, int state)
static void dump_agents (void)
 Dump AgentCallbackLogin agents to the ASTdb database for persistence.
static struct agent_pvtfind_agent (char *agentid)
static int function_agent (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int load_module (void)
 Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other things it registers applications, cli commands and reads the cofiguration file.
static int login_exec (struct ast_channel *chan, void *data)
static force_inline int powerof (unsigned int d)
static int read_agent_config (void)
static int reload (void)
static void reload_agents (void)
 Reload the persistent agents from astdb.
static void set_agentbycallerid (const char *callerid, const char *agent)
 store/clear the global variable that stores agentid based on the callerid
static int unload_module (void)

Variables

static int ackcall
ast_custom_function agent_function
static const char agent_logoff_usage []
static const struct ast_channel_tech agent_tech
 Channel interface description for PBX integration.
static char agentgoodbye [AST_MAX_FILENAME_LEN] = "vm-goodbye"
static const char app [] = "AgentLogin"
static const char app3 [] = "AgentMonitorOutgoing"
static int autologoff
static int autologoffunavail = 0
static char beep [AST_MAX_BUF] = "beep"
static struct ast_cli_entry cli_agents []
static const char config [] = "agents.conf"
static const char descrip []
static const char descrip3 []
static int endcall
static ast_group_t group
static const char mandescr_agent_callback_login []
static const char mandescr_agent_logoff []
static const char mandescr_agents []
static int maxlogintries = 3
static char moh [80] = "default"
static int multiplelogin = 1
static const char pa_family [] = "/Agents"
static int persistent_agents = 0
static int recordagentcalls = 0
static char recordformat [AST_MAX_BUF] = ""
static char recordformatext [AST_MAX_BUF] = ""
static char savecallsin [AST_MAX_BUF] = ""
static const char show_agents_online_usage []
static const char show_agents_usage []
static const char synopsis [] = "Call agent login"
static const char synopsis3 [] = "Record agent's outgoing call"
static const char tdesc [] = "Call Agent Proxy Channel"
static int updatecdr = 0
static char urlprefix [AST_MAX_BUF] = ""
static int wrapuptime


Define Documentation

#define AST_MAX_AGENT   80
 

Agent ID or Password max length

Definition at line 133 of file chan_agent.c.

Referenced by __login_exec(), agent_logoff_maintenance(), agentmonitoroutgoing_exec(), and complete_agent_logoff_cmd().

#define AST_MAX_BUF   256
 

Definition at line 134 of file chan_agent.c.

Referenced by __agent_start_monitoring(), __login_exec(), agentmonitoroutgoing_exec(), agents_show(), agents_show_online(), and set_agentbycallerid().

#define AST_MAX_FILENAME_LEN   256
 

Definition at line 135 of file chan_agent.c.

Referenced by __login_exec().

#define CHECK_FORMATS ast,
 ) 
 

Definition at line 194 of file chan_agent.c.

Referenced by agent_read(), and agent_write().

#define CLEANUP ast,
 ) 
 

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.

Definition at line 217 of file chan_agent.c.

Referenced by agent_call(), agent_read(), and agent_write().

#define GETAGENTBYCALLERID   "AGENTBYCALLERID"
 

Definition at line 162 of file chan_agent.c.

Referenced by agentmonitoroutgoing_exec(), and set_agentbycallerid().

#define PA_MAX_LEN   2048
 

The maximum length of each persistent member agent database entry

Definition at line 138 of file chan_agent.c.


Function Documentation

static int __agent_start_monitoring struct ast_channel ast,
struct agent_pvt p,
int  needlock
[static]
 

Definition at line 393 of file chan_agent.c.

References agent_pvt::agent, ast_cdr_alloc(), ast_cdr_setuserfield(), ast_log(), AST_MAX_BUF, ast_monitor_setjoinfiles(), ast_monitor_start(), ast_verbose(), ast_channel::cdr, LOG_ERROR, and ast_channel::monitor.

Referenced by agent_start_monitoring(), and agentmonitoroutgoing_exec().

00394 {
00395    char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
00396    char filename[AST_MAX_BUF];
00397    int res = -1;
00398    if (!p)
00399       return -1;
00400    if (!ast->monitor) {
00401       snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
00402       /* substitute . for - */
00403       if ((pointer = strchr(filename, '.')))
00404          *pointer = '-';
00405       snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename);
00406       ast_monitor_start(ast, recordformat, tmp, needlock);
00407       ast_monitor_setjoinfiles(ast, 1);
00408       snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext);
00409 #if 0
00410       ast_verbose("name is %s, link is %s\n",tmp, tmp2);
00411 #endif
00412       if (!ast->cdr)
00413          ast->cdr = ast_cdr_alloc();
00414       ast_cdr_setuserfield(ast, tmp2);
00415       res = 0;
00416    } else
00417       ast_log(LOG_ERROR, "Recording already started on that call.\n");
00418    return res;
00419 }

static int __login_exec struct ast_channel chan,
void *  data,
int  callbackmode
[static]
 

Log in agent application.

Parameters:
chan 
data 
callbackmode non-zero for AgentCallbackLogin

Definition at line 1749 of file chan_agent.c.

References ast_channel::_state, agent_pvt::ackcall, agent_pvt::acknowledged, agent_pvt::agent, agent_ack_sleep(), agent_cont_sleep(), agent_logoff_maintenance(), allow_multiple_login(), agent_pvt::app_lock, ast_answer(), AST_APP_ARG, ast_app_getdata(), ast_best_codec(), AST_CONTROL_HOLD, AST_DECLARE_APP_ARGS, ast_device_state_changed(), AST_DIGIT_ANY, ast_exists_extension(), ast_getformatname(), ast_indicate_data(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_AGENT, AST_MAX_BUF, AST_MAX_FILENAME_LEN, ast_module_user_add, ast_mutex_destroy(), ast_mutex_lock(), ast_mutex_unlock(), ast_queue_log(), ast_safe_sleep(), ast_safe_sleep_conditional(), ast_set_read_format(), ast_set_write_format(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_true(), ast_verbose(), ast_waitstream(), agent_pvt::autologoff, ast_channel::cdr, agent_pvt::chan, ast_module_user::chan, ast_cdr::channel, check_availability(), check_beep(), ast_channel::cid, ast_callerid::cid_num, context, agent_pvt::dead, dump_agents(), EVENT_FLAG_AGENT, free, agent_pvt::lastdisc, agent_pvt::lock, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, agent_pvt::logincallerid, agent_pvt::loginchan, agent_pvt::loginstart, manager_event, agent_pvt::moh, option_debug, option_verbose, agent_pvt::owner, agent_pvt::owning_app, parse(), agent_pvt::password, pbx_builtin_getvar_helper(), agent_pvt::pending, S_OR, set_agentbycallerid(), strsep(), VERBOSE_PREFIX_2, VERBOSE_PREFIX_3, and agent_pvt::wrapuptime.

Referenced by login_exec().

01750 {
01751    int res=0;
01752    int tries = 0;
01753    int max_login_tries = maxlogintries;
01754    struct agent_pvt *p;
01755    struct ast_module_user *u;
01756    int login_state = 0;
01757    char user[AST_MAX_AGENT] = "";
01758    char pass[AST_MAX_AGENT];
01759    char agent[AST_MAX_AGENT] = "";
01760    char xpass[AST_MAX_AGENT] = "";
01761    char *errmsg;
01762    char *parse;
01763    AST_DECLARE_APP_ARGS(args,
01764               AST_APP_ARG(agent_id);
01765               AST_APP_ARG(options);
01766               AST_APP_ARG(extension);
01767       );
01768    const char *tmpoptions = NULL;
01769    char *context = NULL;
01770    int play_announcement = 1;
01771    char agent_goodbye[AST_MAX_FILENAME_LEN];
01772    int update_cdr = updatecdr;
01773    char *filename = "agent-loginok";
01774    char tmpchan[AST_MAX_BUF] = "";
01775 
01776    u = ast_module_user_add(chan);
01777 
01778    parse = ast_strdupa(data);
01779 
01780    AST_STANDARD_APP_ARGS(args, parse);
01781 
01782    ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01783 
01784    /* Set Channel Specific Login Overrides */
01785    if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01786       max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01787       if (max_login_tries < 0)
01788          max_login_tries = 0;
01789       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01790       if (option_verbose > 2)
01791          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);
01792    }
01793    if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01794       if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01795          update_cdr = 1;
01796       else
01797          update_cdr = 0;
01798       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01799       if (option_verbose > 2)
01800          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
01801    }
01802    if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01803       strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01804       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01805       if (option_verbose > 2)
01806          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
01807    }
01808    /* End Channel Specific Login Overrides */
01809    
01810    if (callbackmode && args.extension) {
01811       parse = args.extension;
01812       args.extension = strsep(&parse, "@");
01813       context = parse;
01814    }
01815 
01816    if (!ast_strlen_zero(args.options)) {
01817       if (strchr(args.options, 's')) {
01818          play_announcement = 0;
01819       }
01820    }
01821 
01822    if (chan->_state != AST_STATE_UP)
01823       res = ast_answer(chan);
01824    if (!res) {
01825       if (!ast_strlen_zero(args.agent_id))
01826          ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
01827       else
01828          res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
01829    }
01830    while (!res && (max_login_tries==0 || tries < max_login_tries)) {
01831       tries++;
01832       /* Check for password */
01833       AST_LIST_LOCK(&agents);
01834       AST_LIST_TRAVERSE(&agents, p, list) {
01835          if (!strcmp(p->agent, user) && !p->pending)
01836             ast_copy_string(xpass, p->password, sizeof(xpass));
01837       }
01838       AST_LIST_UNLOCK(&agents);
01839       if (!res) {
01840          if (!ast_strlen_zero(xpass))
01841             res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
01842          else
01843             pass[0] = '\0';
01844       }
01845       errmsg = "agent-incorrect";
01846 
01847 #if 0
01848       ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
01849 #endif      
01850 
01851       /* Check again for accuracy */
01852       AST_LIST_LOCK(&agents);
01853       AST_LIST_TRAVERSE(&agents, p, list) {
01854          ast_mutex_lock(&p->lock);
01855          if (!strcmp(p->agent, user) &&
01856              !strcmp(p->password, pass) && !p->pending) {
01857             login_state = 1; /* Successful Login */
01858 
01859             /* Ensure we can't be gotten until we're done */
01860             gettimeofday(&p->lastdisc, NULL);
01861             p->lastdisc.tv_sec++;
01862 
01863             /* Set Channel Specific Agent Overrides */
01864             if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01865                if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
01866                   p->ackcall = 2;
01867                else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
01868                   p->ackcall = 1;
01869                else
01870                   p->ackcall = 0;
01871                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
01872                if (option_verbose > 2)
01873                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
01874             }
01875             if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
01876                p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
01877                if (p->autologoff < 0)
01878                   p->autologoff = 0;
01879                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
01880                if (option_verbose > 2)
01881                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
01882             }
01883             if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
01884                p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
01885                if (p->wrapuptime < 0)
01886                   p->wrapuptime = 0;
01887                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
01888                if (option_verbose > 2)
01889                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
01890             }
01891             /* End Channel Specific Agent Overrides */
01892             if (!p->chan) {
01893                char last_loginchan[80] = "";
01894                long logintime;
01895                snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01896 
01897                if (callbackmode) {
01898                   int pos = 0;
01899                   /* Retrieve login chan */
01900                   for (;;) {
01901                      if (!ast_strlen_zero(args.extension)) {
01902                         ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
01903                         res = 0;
01904                      } else
01905                         res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
01906                      if (ast_strlen_zero(tmpchan) )
01907                         break;
01908                      if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
01909                         if(!allow_multiple_login(tmpchan,context) ) {
01910                            args.extension = NULL;
01911                            pos = 0;
01912                         } else
01913                            break;
01914                      }
01915                      if (args.extension) {
01916                         ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
01917                         args.extension = NULL;
01918                         pos = 0;
01919                      } else {
01920                         ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
01921                         res = ast_streamfile(chan, "invalid", chan->language);
01922                         if (!res)
01923                            res = ast_waitstream(chan, AST_DIGIT_ANY);
01924                         if (res > 0) {
01925                            tmpchan[0] = res;
01926                            tmpchan[1] = '\0';
01927                            pos = 1;
01928                         } else {
01929                            tmpchan[0] = '\0';
01930                            pos = 0;
01931                         }
01932                      }
01933                   }
01934                   args.extension = tmpchan;
01935                   if (!res) {
01936                      set_agentbycallerid(p->logincallerid, NULL);
01937                      if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
01938                         snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
01939                      else {
01940                         ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
01941                         ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
01942                      }
01943                      p->acknowledged = 0;
01944                      if (ast_strlen_zero(p->loginchan)) {
01945                         login_state = 2;
01946                         filename = "agent-loggedoff";
01947                      } else {
01948                         if (chan->cid.cid_num) {
01949                            ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
01950                            set_agentbycallerid(p->logincallerid, p->agent);
01951                         } else
01952                            p->logincallerid[0] = '\0';
01953                      }
01954 
01955                      if(update_cdr && chan->cdr)
01956                         snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
01957 
01958                   }
01959                } else {
01960                   p->loginchan[0] = '\0';
01961                   p->logincallerid[0] = '\0';
01962                   p->acknowledged = 0;
01963                }
01964                ast_mutex_unlock(&p->lock);
01965                AST_LIST_UNLOCK(&agents);
01966                if( !res && play_announcement==1 )
01967                   res = ast_streamfile(chan, filename, chan->language);
01968                if (!res)
01969                   ast_waitstream(chan, "");
01970                AST_LIST_LOCK(&agents);
01971                ast_mutex_lock(&p->lock);
01972                if (!res) {
01973                   res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
01974                   if (res)
01975                      ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
01976                }
01977                if (!res) {
01978                   res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
01979                   if (res)
01980                      ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
01981                }
01982                /* Check once more just in case */
01983                if (p->chan)
01984                   res = -1;
01985                if (callbackmode && !res) {
01986                   /* Just say goodbye and be done with it */
01987                   if (!ast_strlen_zero(p->loginchan)) {
01988                      if (p->loginstart == 0)
01989                         time(&p->loginstart);
01990                      manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
01991                               "Agent: %s\r\n"
01992                               "Loginchan: %s\r\n"
01993                               "Uniqueid: %s\r\n",
01994                               p->agent, p->loginchan, chan->uniqueid);
01995                      ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
01996                      if (option_verbose > 1)
01997                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
01998                      ast_device_state_changed("Agent/%s", p->agent);
01999                      if (persistent_agents)
02000                         dump_agents();
02001                   } else {
02002                      logintime = time(NULL) - p->loginstart;
02003                      p->loginstart = 0;
02004 
02005                      agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
02006                      if (option_verbose > 1)
02007                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
02008                   }
02009                   AST_LIST_UNLOCK(&agents);
02010                   if (!res)
02011                      res = ast_safe_sleep(chan, 500);
02012                   ast_mutex_unlock(&p->lock);
02013                } else if (!res) {
02014                   ast_indicate_data(chan, AST_CONTROL_HOLD, 
02015                      S_OR(p->moh, NULL), 
02016                      !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02017                   if (p->loginstart == 0)
02018                      time(&p->loginstart);
02019                   manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02020                            "Agent: %s\r\n"
02021                            "Channel: %s\r\n"
02022                            "Uniqueid: %s\r\n",
02023                            p->agent, chan->name, chan->uniqueid);
02024                   if (update_cdr && chan->cdr)
02025                      snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02026                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
02027                   if (option_verbose > 1)
02028                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
02029                             ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
02030                   /* Login this channel and wait for it to go away */
02031                   p->chan = chan;
02032                   if (p->ackcall > 1)
02033                      check_beep(p, 0);
02034                   else
02035                      check_availability(p, 0);
02036                   ast_mutex_unlock(&p->lock);
02037                   AST_LIST_UNLOCK(&agents);
02038                   ast_device_state_changed("Agent/%s", p->agent);
02039                   while (res >= 0) {
02040                      ast_mutex_lock(&p->lock);
02041                      if (p->chan != chan)
02042                         res = -1;
02043                      ast_mutex_unlock(&p->lock);
02044                      /* Yield here so other interested threads can kick in. */
02045                      sched_yield();
02046                      if (res)
02047                         break;
02048 
02049                      AST_LIST_LOCK(&agents);
02050                      ast_mutex_lock(&p->lock);
02051                      if (p->lastdisc.tv_sec) {
02052                         if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime) {
02053                            if (option_debug)
02054                               ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
02055                            p->lastdisc = ast_tv(0, 0);
02056                            if (p->ackcall > 1)
02057                               check_beep(p, 0);
02058                            else
02059                               check_availability(p, 0);
02060                         }
02061                      }
02062                      ast_mutex_unlock(&p->lock);
02063                      AST_LIST_UNLOCK(&agents);
02064                      /* Synchronize channel ownership between call to agent and itself. */
02065                      ast_mutex_lock( &p->app_lock );
02066                      ast_mutex_lock(&p->lock);
02067                      p->owning_app = pthread_self();
02068                      ast_mutex_unlock(&p->lock);
02069                      if (p->ackcall > 1) 
02070                         res = agent_ack_sleep(p);
02071                      else
02072                         res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02073                      ast_mutex_unlock( &p->app_lock );
02074                      if ((p->ackcall > 1)  && (res == 1)) {
02075                         AST_LIST_LOCK(&agents);
02076                         ast_mutex_lock(&p->lock);
02077                         check_availability(p, 0);
02078                         ast_mutex_unlock(&p->lock);
02079                         AST_LIST_UNLOCK(&agents);
02080                         res = 0;
02081                      }
02082                      sched_yield();
02083                   }
02084                   ast_mutex_lock(&p->lock);
02085                   if (res && p->owner) 
02086                      ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
02087                   /* Log us off if appropriate */
02088                   if (p->chan == chan)
02089                      p->chan = NULL;
02090                   p->acknowledged = 0;
02091                   logintime = time(NULL) - p->loginstart;
02092                   p->loginstart = 0;
02093                   ast_mutex_unlock(&p->lock);
02094                   manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02095                            "Agent: %s\r\n"
02096                            "Logintime: %ld\r\n"
02097                            "Uniqueid: %s\r\n",
02098                            p->agent, logintime, chan->uniqueid);
02099                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02100                   if (option_verbose > 1)
02101                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
02102                   /* If there is no owner, go ahead and kill it now */
02103                   ast_device_state_changed("Agent/%s", p->agent);
02104                   if (p->dead && !p->owner) {
02105                      ast_mutex_destroy(&p->lock);
02106                      ast_mutex_destroy(&p->app_lock);
02107                      free(p);
02108                   }
02109                }
02110                else {
02111                   ast_mutex_unlock(&p->lock);
02112                   p = NULL;
02113                }
02114                res = -1;
02115             } else {
02116                ast_mutex_unlock(&p->lock);
02117                errmsg = "agent-alreadyon";
02118                p = NULL;
02119             }
02120             break;
02121          }
02122          ast_mutex_unlock(&p->lock);
02123       }
02124       if (!p)
02125          AST_LIST_UNLOCK(&agents);
02126 
02127       if (!res && (max_login_tries==0 || tries < max_login_tries))
02128          res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02129    }
02130       
02131    if (!res)
02132       res = ast_safe_sleep(chan, 500);
02133 
02134    /* AgentLogin() exit */
02135    if (!callbackmode) {
02136       ast_module_user_remove(u);
02137       return -1;
02138    } else { /* AgentCallbackLogin() exit*/
02139       /* Set variables */
02140       if (login_state > 0) {
02141          pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
02142          if (login_state==1) {
02143             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
02144             pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
02145          } else 
02146             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
02147       } else {
02148          pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
02149       }
02150       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
02151          ast_module_user_remove(u);
02152          return 0;
02153       }
02154       /* Do we need to play agent-goodbye now that we will be hanging up? */
02155       if (play_announcement) {
02156          if (!res)
02157             res = ast_safe_sleep(chan, 1000);
02158          res = ast_streamfile(chan, agent_goodbye, chan->language);
02159          if (!res)
02160             res = ast_waitstream(chan, "");
02161          if (!res)