Fri Jan 31 13:16:18 2014

Asterisk developer's documentation


chan_agent.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 
00020 /*! \file
00021  * 
00022  * \brief Implementation of Agents (proxy channel)
00023  *
00024  * \author Mark Spencer <markster@digium.com>
00025  *
00026  * This file is the implementation of Agents modules.
00027  * It is a dynamic module that is loaded by Asterisk. 
00028  * \par See also
00029  * \arg \ref Config_agent
00030  *
00031  * \ingroup channel_drivers
00032  */
00033 /*** MODULEINFO
00034         <depend>chan_local</depend>
00035  ***/
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 314908 $")
00040 
00041 #include <stdio.h>
00042 #include <string.h>
00043 #include <errno.h>
00044 #include <unistd.h>
00045 #include <sys/socket.h>
00046 #include <stdlib.h>
00047 #include <fcntl.h>
00048 #include <netdb.h>
00049 #include <netinet/in.h>
00050 #include <arpa/inet.h>
00051 #include <sys/signal.h>
00052 
00053 #include "asterisk/lock.h"
00054 #include "asterisk/channel.h"
00055 #include "asterisk/config.h"
00056 #include "asterisk/logger.h"
00057 #include "asterisk/module.h"
00058 #include "asterisk/pbx.h"
00059 #include "asterisk/options.h"
00060 #include "asterisk/lock.h"
00061 #include "asterisk/sched.h"
00062 #include "asterisk/io.h"
00063 #include "asterisk/rtp.h"
00064 #include "asterisk/acl.h"
00065 #include "asterisk/callerid.h"
00066 #include "asterisk/file.h"
00067 #include "asterisk/cli.h"
00068 #include "asterisk/app.h"
00069 #include "asterisk/musiconhold.h"
00070 #include "asterisk/manager.h"
00071 #include "asterisk/features.h"
00072 #include "asterisk/utils.h"
00073 #include "asterisk/causes.h"
00074 #include "asterisk/astdb.h"
00075 #include "asterisk/devicestate.h"
00076 #include "asterisk/monitor.h"
00077 #include "asterisk/stringfields.h"
00078 
00079 static const char tdesc[] = "Call Agent Proxy Channel";
00080 static const char config[] = "agents.conf";
00081 
00082 static const char app[] = "AgentLogin";
00083 static const char app2[] = "AgentCallbackLogin";
00084 static const char app3[] = "AgentMonitorOutgoing";
00085 
00086 static const char synopsis[] = "Call agent login";
00087 static const char synopsis2[] = "Call agent callback login";
00088 static const char synopsis3[] = "Record agent's outgoing call";
00089 
00090 static const char descrip[] =
00091 "  AgentLogin([AgentNo][|options]):\n"
00092 "Asks the agent to login to the system.  Always returns -1.  While\n"
00093 "logged in, the agent can receive calls and will hear a 'beep'\n"
00094 "when a new call comes in. The agent can dump the call by pressing\n"
00095 "the star key.\n"
00096 "The option string may contain zero or more of the following characters:\n"
00097 "      's' -- silent login - do not announce the login ok segment after agent logged in/off\n";
00098 
00099 static const char descrip2[] =
00100 "  AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n"
00101 "Asks the agent to login to the system with callback.\n"
00102 "The agent's callback extension is called (optionally with the specified\n"
00103 "context).\n"
00104 "The option string may contain zero or more of the following characters:\n"
00105 "      's' -- silent login - do not announce the login ok segment agent logged in/off\n";
00106 
00107 static const char descrip3[] =
00108 "  AgentMonitorOutgoing([options]):\n"
00109 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
00110 "comparison of the callerid of the current interface and the global variable \n"
00111 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
00112 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
00113 "instead of Monitor application. That have to be configured in the agents.conf file.\n"
00114 "\nReturn value:\n"
00115 "Normally the app returns 0 unless the options are passed. Also if the callerid or\n"
00116 "the agentid are not specified it'll look for n+101 priority.\n"
00117 "\nOptions:\n"
00118 "  'd' - make the app return -1 if there is an error condition and there is\n"
00119 "        no extension n+101\n"
00120 "  'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
00121 "  'n' - don't generate the warnings when there is no callerid or the\n"
00122 "        agentid is not known.\n"
00123 "             It's handy if you want to have one context for agent and non-agent calls.\n";
00124 
00125 static const char mandescr_agents[] =
00126 "Description: Will list info about all possible agents.\n"
00127 "Variables: NONE\n";
00128 
00129 static const char mandescr_agent_logoff[] =
00130 "Description: Sets an agent as no longer logged in.\n"
00131 "Variables: (Names marked with * are required)\n"
00132 "  *Agent: Agent ID of the agent to log off\n"
00133 "  Soft: Set to 'true' to not hangup existing calls\n";
00134 
00135 static const char mandescr_agent_callback_login[] =
00136 "Description: Sets an agent as logged in with callback.\n"
00137 "Variables: (Names marked with * are required)\n"
00138 "  *Agent: Agent ID of the agent to login\n"
00139 "  *Exten: Extension to use for callback\n"
00140 "  Context: Context to use for callback\n"
00141 "  AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n"
00142 "  WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n";
00143 
00144 static char moh[80] = "default";
00145 
00146 #define AST_MAX_AGENT   80                          /*!< Agent ID or Password max length */
00147 #define AST_MAX_BUF  256
00148 #define AST_MAX_FILENAME_LEN  256
00149 
00150 static const char pa_family[] = "Agents";          /*!< Persistent Agents astdb family */
00151 #define PA_MAX_LEN 2048                             /*!< The maximum length of each persistent member agent database entry */
00152 
00153 static int persistent_agents = 0;                   /*!< queues.conf [general] option */
00154 static void dump_agents(void);
00155 
00156 static ast_group_t group;
00157 static int autologoff;
00158 static int wrapuptime;
00159 static int ackcall;
00160 static int endcall;
00161 static int multiplelogin = 1;
00162 static int autologoffunavail = 0;
00163 
00164 static int maxlogintries = 3;
00165 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
00166 
00167 static int recordagentcalls = 0;
00168 static char recordformat[AST_MAX_BUF] = "";
00169 static char recordformatext[AST_MAX_BUF] = "";
00170 static char urlprefix[AST_MAX_BUF] = "";
00171 static char savecallsin[AST_MAX_BUF] = "";
00172 static int updatecdr = 0;
00173 static char beep[AST_MAX_BUF] = "beep";
00174 
00175 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
00176 
00177 /*! \brief Structure representing an agent.  */
00178 struct agent_pvt {
00179    ast_mutex_t lock;              /*!< Channel private lock */
00180    int dead;                      /*!< Poised for destruction? */
00181    int pending;                   /*!< Not a real agent -- just pending a match */
00182    int abouttograb;               /*!< About to grab */
00183    int autologoff;                /*!< Auto timeout time */
00184    int ackcall;                   /*!< ackcall */
00185    int deferlogoff;               /*!< Defer logoff to hangup */
00186    time_t loginstart;             /*!< When agent first logged in (0 when logged off) */
00187    time_t start;                  /*!< When call started */
00188    struct timeval lastdisc;       /*!< When last disconnected */
00189    int wrapuptime;                /*!< Wrapup time in ms */
00190    ast_group_t group;             /*!< Group memberships */
00191    int acknowledged;              /*!< Acknowledged */
00192    char moh[80];                  /*!< Which music on hold */
00193    char agent[AST_MAX_AGENT];     /*!< Agent ID */
00194    char password[AST_MAX_AGENT];  /*!< Password for Agent login */
00195    char name[AST_MAX_AGENT];
00196    int inherited_devicestate;     /*!< Does the underlying channel have a devicestate to pass? */
00197    int app_lock_flag;
00198    ast_cond_t app_complete_cond;
00199    ast_cond_t login_wait_cond;
00200    volatile int app_sleep_cond;   /**< Sleep condition for the login app */
00201    struct ast_channel *owner;     /**< Agent */
00202    /**! channel they logged in from. This may also be used to tell if an agent
00203     * is a callback agent or not. If this field is not zero-length, then this is
00204     * a callback agent */
00205    char loginchan[80];
00206    char logincallerid[80];        /**< Caller ID they had when they logged in */
00207    struct ast_channel *chan;      /**< Channel we use */
00208    AST_LIST_ENTRY(agent_pvt) list;  /**< Next Agent in the linked list. */
00209 };
00210 
00211 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
00212 
00213 #define CHECK_FORMATS(ast, p) do { \
00214    if (p->chan) {\
00215       if (ast->nativeformats != p->chan->nativeformats) { \
00216          ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
00217          /* Native formats changed, reset things */ \
00218          ast->nativeformats = p->chan->nativeformats; \
00219          ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
00220          ast_set_read_format(ast, ast->readformat); \
00221          ast_set_write_format(ast, ast->writeformat); \
00222       } \
00223       if (p->chan->readformat != ast->rawreadformat && !p->chan->generator)  \
00224          ast_set_read_format(p->chan, ast->rawreadformat); \
00225       if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
00226          ast_set_write_format(p->chan, ast->rawwriteformat); \
00227    } \
00228 } while(0)
00229 
00230 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
00231    properly for a timingfd XXX This might need more work if agents were logged in as agents or other
00232    totally impractical combinations XXX */
00233 
00234 #define CLEANUP(ast, p) do { \
00235    int x; \
00236    if (p->chan) { \
00237       for (x=0;x<AST_MAX_FDS;x++) {\
00238          if (x != AST_TIMING_FD) \
00239             ast->fds[x] = p->chan->fds[x]; \
00240       } \
00241       ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
00242    } \
00243 } while(0)
00244 
00245 /*--- Forward declarations */
00246 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
00247 static int agent_devicestate(void *data);
00248 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
00249 static int agent_digit_begin(struct ast_channel *ast, char digit);
00250 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00251 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
00252 static int agent_hangup(struct ast_channel *ast);
00253 static int agent_answer(struct ast_channel *ast);
00254 static struct ast_frame *agent_read(struct ast_channel *ast);
00255 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
00256 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00257 static int agent_sendtext(struct ast_channel *ast, const char *text);
00258 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00259 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00260 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00261 static void set_agentbycallerid(const char *callerid, const char *agent);
00262 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
00263 static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
00264 static int agent_logoff(const char *agent, int soft);
00265 
00266 /*! \brief Channel interface description for PBX integration */
00267 static const struct ast_channel_tech agent_tech = {
00268    .type = "Agent",
00269    .description = tdesc,
00270    .capabilities = -1,
00271    .requester = agent_request,
00272    .devicestate = agent_devicestate,
00273    .send_digit_begin = agent_digit_begin,
00274    .send_digit_end = agent_digit_end,
00275    .call = agent_call,
00276    .hangup = agent_hangup,
00277    .answer = agent_answer,
00278    .read = agent_read,
00279    .write = agent_write,
00280    .write_video = agent_write,
00281    .send_html = agent_sendhtml,
00282    .send_text = agent_sendtext,
00283    .exception = agent_read,
00284    .indicate = agent_indicate,
00285    .fixup = agent_fixup,
00286    .bridged_channel = agent_bridgedchannel,
00287    .get_base_channel = agent_get_base_channel,
00288    .set_base_channel = agent_set_base_channel,
00289 };
00290 
00291 static int agent_devicestate_cb(const char *dev, int state, void *data)
00292 {
00293    int res, i;
00294    struct agent_pvt *p;
00295    char basename[AST_CHANNEL_NAME], *tmp;
00296 
00297    /* Skip Agent status */
00298    if (!strncasecmp(dev, "Agent/", 6)) {
00299       return 0;
00300    }
00301 
00302    /* Try to be safe, but don't deadlock */
00303    for (i = 0; i < 10; i++) {
00304       if ((res = AST_LIST_TRYLOCK(&agents)) == 0) {
00305          break;
00306       }
00307    }
00308    if (res) {
00309       return -1;
00310    }
00311 
00312    AST_LIST_TRAVERSE(&agents, p, list) {
00313       ast_mutex_lock(&p->lock);
00314       if (p->chan && !ast_strlen_zero(p->loginchan)) {
00315          ast_copy_string(basename, p->chan->name, sizeof(basename));
00316          if ((tmp = strrchr(basename, '-'))) {
00317             *tmp = '\0';
00318          }
00319          if (strcasecmp(p->chan->name, dev) == 0 || strcasecmp(basename, dev) == 0) {
00320             p->inherited_devicestate = state;
00321             ast_device_state_changed("Agent/%s", p->agent);
00322          }
00323       }
00324       ast_mutex_unlock(&p->lock);
00325    }
00326    AST_LIST_UNLOCK(&agents);
00327    return 0;
00328 }
00329 
00330 /*!
00331  * Adds an agent to the global list of agents.
00332  *
00333  * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
00334  * \param pending If it is pending or not.
00335  * @return The just created agent.
00336  * \sa agent_pvt, agents.
00337  */
00338 static struct agent_pvt *add_agent(char *agent, int pending)
00339 {
00340    char *parse;
00341    AST_DECLARE_APP_ARGS(args,
00342       AST_APP_ARG(agt);
00343       AST_APP_ARG(password);
00344       AST_APP_ARG(name);
00345    );
00346    char *password = NULL;
00347    char *name = NULL;
00348    char *agt = NULL;
00349    struct agent_pvt *p;
00350 
00351    parse = ast_strdupa(agent);
00352 
00353    /* Extract username (agt), password and name from agent (args). */
00354    AST_NONSTANDARD_APP_ARGS(args, parse, ',');
00355 
00356    if(args.argc == 0) {
00357       ast_log(LOG_WARNING, "A blank agent line!\n");
00358       return NULL;
00359    }
00360 
00361    if(ast_strlen_zero(args.agt) ) {
00362       ast_log(LOG_WARNING, "An agent line with no agentid!\n");
00363       return NULL;
00364    } else
00365       agt = args.agt;
00366 
00367    if(!ast_strlen_zero(args.password)) {
00368       password = args.password;
00369       while (*password && *password < 33) password++;
00370    }
00371    if(!ast_strlen_zero(args.name)) {
00372       name = args.name;
00373       while (*name && *name < 33) name++;
00374    }
00375    
00376    /* Are we searching for the agent here ? To see if it exists already ? */
00377    AST_LIST_TRAVERSE(&agents, p, list) {
00378       if (!pending && !strcmp(p->agent, agt))
00379          break;
00380    }
00381    if (!p) {
00382       // Build the agent.
00383       if (!(p = ast_calloc(1, sizeof(*p))))
00384          return NULL;
00385       ast_copy_string(p->agent, agt, sizeof(p->agent));
00386       ast_mutex_init(&p->lock);
00387       ast_cond_init(&p->app_complete_cond, NULL);
00388       ast_cond_init(&p->login_wait_cond, NULL);
00389       p->app_lock_flag = 0;
00390       p->app_sleep_cond = 1;
00391       memcpy(&p->group, group, sizeof(p->group));
00392       p->pending = pending;
00393       p->inherited_devicestate = -1;
00394       AST_LIST_INSERT_TAIL(&agents, p, list);
00395    }
00396 
00397    ast_copy_string(p->password, password ? password : "", sizeof(p->password));
00398    ast_copy_string(p->name, name ? name : "", sizeof(p->name));
00399    ast_copy_string(p->moh, moh, sizeof(p->moh));
00400    p->ackcall = ackcall;
00401    p->autologoff = autologoff;
00402 
00403    /* If someone reduces the wrapuptime and reloads, we want it
00404     * to change the wrapuptime immediately on all calls */
00405    if (p->wrapuptime > wrapuptime) {
00406       struct timeval now = ast_tvnow();
00407       /* XXX check what is this exactly */
00408 
00409       /* We won't be pedantic and check the tv_usec val */
00410       if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
00411          p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
00412          p->lastdisc.tv_usec = now.tv_usec;
00413       }
00414    }
00415    p->wrapuptime = wrapuptime;
00416 
00417    if (pending)
00418       p->dead = 1;
00419    else
00420       p->dead = 0;
00421    return p;
00422 }
00423 
00424 /*!
00425  * Deletes an agent after doing some clean up.
00426  * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
00427  * \param p Agent to be deleted.
00428  * \returns Always 0.
00429  */
00430 static int agent_cleanup(struct agent_pvt *p)
00431 {
00432    struct ast_channel *chan = NULL;
00433    ast_mutex_lock(&p->lock);
00434    chan = p->owner;
00435    p->owner = NULL;
00436    chan->tech_pvt = NULL;
00437    /* Release ownership of the agent to other threads (presumably running the login app). */
00438    p->app_sleep_cond = 1;
00439    p->app_lock_flag = 0;
00440    ast_cond_signal(&p->app_complete_cond);
00441    if (chan)
00442       ast_channel_free(chan);
00443    if (p->dead) {
00444       ast_mutex_unlock(&p->lock);
00445       ast_mutex_destroy(&p->lock);
00446       ast_cond_destroy(&p->app_complete_cond);
00447       ast_cond_destroy(&p->login_wait_cond);
00448       free(p);
00449         } else {
00450       ast_mutex_unlock(&p->lock);
00451    }
00452    return 0;
00453 }
00454 
00455 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
00456 
00457 static int agent_answer(struct ast_channel *ast)
00458 {
00459    ast_log(LOG_WARNING, "Huh?  Agent is being asked to answer?\n");
00460    return -1;
00461 }
00462 
00463 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
00464 {
00465    char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
00466    char filename[AST_MAX_BUF];
00467    int res = -1;
00468    if (!p)
00469       return -1;
00470    if (!ast->monitor) {
00471       snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
00472       /* substitute . for - */
00473       if ((pointer = strchr(filename, '.')))
00474          *pointer = '-';
00475       snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
00476       ast_monitor_start(ast, recordformat, tmp, needlock);
00477       ast_monitor_setjoinfiles(ast, 1);
00478       snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
00479 #if 0
00480       ast_verbose("name is %s, link is %s\n",tmp, tmp2);
00481 #endif
00482       if (!ast->cdr)
00483          ast->cdr = ast_cdr_alloc();
00484       ast_cdr_setuserfield(ast, tmp2);
00485       res = 0;
00486    } else
00487       ast_log(LOG_ERROR, "Recording already started on that call.\n");
00488    return res;
00489 }
00490 
00491 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
00492 {
00493    return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
00494 }
00495 
00496 static struct ast_frame *agent_read(struct ast_channel *ast)
00497 {
00498    struct agent_pvt *p = ast->tech_pvt;
00499    struct ast_frame *f = NULL;
00500    static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00501    const char *status;
00502    int cur_time = time(NULL);
00503    ast_mutex_lock(&p->lock);
00504    CHECK_FORMATS(ast, p);
00505    if (!p->start) {
00506       p->start = cur_time;
00507    }
00508    if (p->chan) {
00509       ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
00510       p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
00511       f = ast_read(p->chan);
00512    } else
00513       f = &ast_null_frame;
00514    if (!f) {
00515       /* If there's a channel, hang it up (if it's on a callback) make it NULL */
00516       if (p->chan) {
00517          p->chan->_bridge = NULL;
00518          /* Note that we don't hangup if it's not a callback because Asterisk will do it
00519             for us when the PBX instance that called login finishes */
00520          if (!ast_strlen_zero(p->loginchan)) {
00521             if (p->chan)
00522                ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name);
00523             if (p->owner->_state != AST_STATE_UP) {
00524                int howlong = cur_time - p->start;
00525                if (p->autologoff && howlong >= p->autologoff) {
00526                   p->loginstart = 0;
00527                      ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00528                   agent_logoff_maintenance(p, p->loginchan, (cur_time = p->loginstart), ast->uniqueid, "Autologoff");
00529                }
00530             }
00531             status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00532             if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00533                long logintime = cur_time - p->loginstart;
00534                p->loginstart = 0;
00535                ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
00536                agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00537             }
00538             ast_hangup(p->chan);
00539             if (p->wrapuptime && p->acknowledged)
00540                p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00541          }
00542          p->chan = NULL;
00543          p->inherited_devicestate = -1;
00544          ast_device_state_changed("Agent/%s", p->agent);
00545          p->acknowledged = 0;
00546       }
00547    } else {
00548       /* if acknowledgement is not required, and the channel is up, we may have missed
00549          an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
00550       if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) {
00551          p->acknowledged = 1;
00552       }
00553       if (!p->acknowledged) {
00554          int howlong = cur_time - p->start;
00555          if (p->autologoff && (howlong >= p->autologoff)) {
00556             ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00557             agent_logoff_maintenance(p, p->loginchan, (cur_time - p->loginstart), ast->uniqueid, "Autologoff");
00558             if (p->owner || p->chan) {
00559                while (p->owner && ast_channel_trylock(p->owner)) {
00560                   DEADLOCK_AVOIDANCE(&p->lock);
00561                }
00562                if (p->owner) {
00563                   ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
00564                   ast_channel_unlock(p->owner);
00565                }
00566 
00567                while (p->chan && ast_channel_trylock(p->chan)) {
00568                   DEADLOCK_AVOIDANCE(&p->lock);
00569                }
00570                if (p->chan) {
00571                   ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00572                   ast_channel_unlock(p->chan);
00573                }
00574             } else {
00575                long logintime;
00576                logintime = time(NULL) - p->loginstart;
00577                p->loginstart = 0;
00578                agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
00579             }
00580          }
00581       }
00582       switch (f->frametype) {
00583       case AST_FRAME_CONTROL:
00584          if (f->subclass == AST_CONTROL_ANSWER) {
00585             if (p->ackcall) {
00586                if (option_verbose > 2)
00587                   ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
00588                /* Don't pass answer along */
00589                ast_frfree(f);
00590                f = &ast_null_frame;
00591             } else {
00592                p->acknowledged = 1;
00593                /* Use the builtin answer frame for the 
00594                   recording start check below. */
00595                ast_frfree(f);
00596                f = &answer_frame;
00597             }
00598          }
00599          break;
00600       case AST_FRAME_DTMF_BEGIN:
00601          /*ignore DTMF begin's as it can cause issues with queue announce files*/
00602          if((!p->acknowledged && f->subclass == '#') || (f->subclass == '*' && endcall)){
00603             ast_frfree(f);
00604             f = &ast_null_frame;
00605          }
00606          break;
00607       case AST_FRAME_DTMF_END:
00608          if (!p->acknowledged && (f->subclass == '#')) {
00609             if (option_verbose > 2)
00610                ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
00611             p->acknowledged = 1;
00612             ast_frfree(f);
00613             f = &answer_frame;
00614          } else if (f->subclass == '*' && endcall) {
00615             /* terminates call */
00616             ast_frfree(f);
00617             f = NULL;
00618          }
00619          break;
00620       case AST_FRAME_VOICE:
00621       case AST_FRAME_VIDEO:
00622          /* don't pass voice or video until the call is acknowledged */
00623          if (!p->acknowledged) {
00624             ast_frfree(f);
00625             f = &ast_null_frame;
00626          }
00627       default:
00628          /* pass everything else on through */
00629          break;
00630       }
00631    }
00632 
00633    CLEANUP(ast,p);
00634    if (p->chan && !p->chan->_bridge) {
00635       if (strcasecmp(p->chan->tech->type, "Local")) {
00636          p->chan->_bridge = ast;
00637          if (p->chan)
00638             ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
00639       }
00640    }
00641    ast_mutex_unlock(&p->lock);
00642    if (recordagentcalls && f == &answer_frame)
00643       agent_start_monitoring(ast,0);
00644    return f;
00645 }
00646 
00647 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00648 {
00649    struct agent_pvt *p = ast->tech_pvt;
00650    int res = -1;
00651    ast_mutex_lock(&p->lock);
00652    if (p->chan) 
00653       res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
00654    ast_mutex_unlock(&p->lock);
00655    return res;
00656 }
00657 
00658 static int agent_sendtext(struct ast_channel *ast, const char *text)
00659 {
00660    struct agent_pvt *p = ast->tech_pvt;
00661    int res = -1;
00662    ast_mutex_lock(&p->lock);
00663    if (p->chan) 
00664       res = ast_sendtext(p->chan, text);
00665    ast_mutex_unlock(&p->lock);
00666    return res;
00667 }
00668 
00669 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
00670 {
00671    struct agent_pvt *p = ast->tech_pvt;
00672    int res = -1;
00673    CHECK_FORMATS(ast, p);
00674    ast_mutex_lock(&p->lock);
00675    if (!p->chan) 
00676       res = 0;
00677    else {
00678       if ((f->frametype != AST_FRAME_VOICE) ||
00679           (f->frametype != AST_FRAME_VIDEO) ||
00680           (f->subclass == p->chan->writeformat)) {
00681          res = ast_write(p->chan, f);
00682       } else {
00683          ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n", 
00684             f->frametype == AST_FRAME_VOICE ? "audio" : "video",
00685             ast->name, p->chan->name);
00686          res = 0;
00687       }
00688    }
00689    CLEANUP(ast, p);
00690    ast_mutex_unlock(&p->lock);
00691    return res;
00692 }
00693 
00694 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00695 {
00696    struct agent_pvt *p = newchan->tech_pvt;
00697    ast_mutex_lock(&p->lock);
00698    if (p->owner != oldchan) {
00699       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
00700       ast_mutex_unlock(&p->lock);
00701       return -1;
00702    }
00703    p->owner = newchan;
00704    ast_mutex_unlock(&p->lock);
00705    return 0;
00706 }
00707 
00708 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00709 {
00710    struct agent_pvt *p = ast->tech_pvt;
00711    int res = -1;
00712    ast_mutex_lock(&p->lock);
00713    if (p->chan && !ast_check_hangup(p->chan)) {
00714       while (ast_channel_trylock(p->chan)) {
00715          int res;
00716          if ((res = ast_channel_unlock(ast))) {
00717             ast_log(LOG_ERROR, "chan_agent bug! Channel was not locked upon entry to agent_indicate: %s\n", strerror(res));
00718             ast_mutex_unlock(&p->lock);
00719             return -1;
00720          }
00721          usleep(1);
00722          ast_channel_lock(ast);
00723       }
00724       res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
00725       ast_channel_unlock(p->chan);
00726    } else
00727       res = 0;
00728    ast_mutex_unlock(&p->lock);
00729    return res;
00730 }
00731 
00732 static int agent_digit_begin(struct ast_channel *ast, char digit)
00733 {
00734    struct agent_pvt *p = ast->tech_pvt;
00735    ast_mutex_lock(&p->lock);
00736    if (p->chan) {
00737       ast_senddigit_begin(p->chan, digit);
00738    }
00739    ast_mutex_unlock(&p->lock);
00740    return 0;
00741 }
00742 
00743 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00744 {
00745    struct agent_pvt *p = ast->tech_pvt;
00746    ast_mutex_lock(&p->lock);
00747    if (p->chan) {
00748       ast_senddigit_end(p->chan, digit, duration);
00749    }
00750    ast_mutex_unlock(&p->lock);
00751    return 0;
00752 }
00753 
00754 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
00755 {
00756    struct agent_pvt *p = ast->tech_pvt;
00757    int res = -1;
00758    int newstate=0;
00759    struct ast_channel *chan;
00760 
00761    ast_mutex_lock(&p->lock);
00762    p->acknowledged = 0;
00763 
00764    if (p->pending) {
00765       ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
00766       ast_mutex_unlock(&p->lock);
00767       ast_setstate(ast, AST_STATE_DIALING);
00768       return 0;
00769    }
00770 
00771    if (!p->chan) {
00772       ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
00773       ast_mutex_unlock(&p->lock);
00774       return res;
00775    }
00776 
00777    if (!ast_strlen_zero(p->loginchan)) {
00778       time(&p->start);
00779       /* Call on this agent */
00780       if (option_verbose > 2)
00781          ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
00782       ast_set_callerid(p->chan,
00783          ast->cid.cid_num, ast->cid.cid_name, NULL);
00784       ast_channel_inherit_variables(ast, p->chan);
00785       res = ast_call(p->chan, p->loginchan, 0);
00786       CLEANUP(ast,p);
00787       ast_mutex_unlock(&p->lock);
00788       return res;
00789    }
00790    if (option_verbose > 2)
00791       ast_verbose(VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
00792    if (option_debug > 2)
00793       ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language);
00794 
00795    chan = p->chan;
00796    ast_mutex_unlock(&p->lock);
00797 
00798    res = ast_streamfile(chan, beep, chan->language);
00799    if (option_debug > 2)
00800       ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
00801    if (!res) {
00802       res = ast_waitstream(chan, "");
00803       if (option_debug > 2)
00804          ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
00805    }
00806 
00807    ast_mutex_lock(&p->lock);
00808    if (!p->chan) {
00809       /* chan went away while we were streaming, this shouldn't be possible */
00810       res = -1;
00811    }
00812 
00813    if (!res) {
00814       res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
00815       if (option_debug > 2)
00816          ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res);
00817       if (res)
00818          ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00819    } else {
00820       /* Agent hung-up */
00821       p->chan = NULL;
00822       p->inherited_devicestate = -1;
00823       ast_device_state_changed("Agent/%s", p->agent);
00824    }
00825 
00826    if (!res) {
00827       res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
00828       if (option_debug > 2)
00829          ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res);
00830       if (res)
00831          ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00832    }
00833    if(!res) {
00834       /* Call is immediately up, or might need ack */
00835       if (p->ackcall > 1)
00836          newstate = AST_STATE_RINGING;
00837       else {
00838          newstate = AST_STATE_UP;
00839          if (recordagentcalls)
00840             agent_start_monitoring(ast, 0);
00841          p->acknowledged = 1;
00842       }
00843       res = 0;
00844    }
00845    CLEANUP(ast, p);
00846    ast_mutex_unlock(&p->lock);
00847    if (newstate)
00848       ast_setstate(ast, newstate);
00849    return res;
00850 }
00851 
00852 /*! \brief store/clear the global variable that stores agentid based on the callerid */
00853 static void set_agentbycallerid(const char *callerid, const char *agent)
00854 {
00855    char buf[AST_MAX_BUF];
00856 
00857    /* if there is no Caller ID, nothing to do */
00858    if (ast_strlen_zero(callerid))
00859       return;
00860 
00861    snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
00862    pbx_builtin_setvar_helper(NULL, buf, agent);
00863 }
00864 
00865 /*! \brief return the channel or base channel if one exists.  This function assumes the channel it is called on is already locked */
00866 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
00867 {
00868    struct agent_pvt *p = NULL;
00869    struct ast_channel *base = chan;
00870 
00871    /* chan is locked by the calling function */
00872    if (!chan || !chan->tech_pvt) {
00873       ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL);
00874       return NULL;
00875    }
00876    p = chan->tech_pvt;
00877    if (p->chan) 
00878       base = p->chan;
00879    return base;
00880 }
00881 
00882 int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
00883 {
00884    struct agent_pvt *p = NULL;
00885    
00886    if (!chan || !base) {
00887       ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
00888       return -1;
00889    }
00890    p = chan->tech_pvt;
00891    if (!p) {
00892       ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
00893       return -1;
00894    }
00895    p->chan = base;
00896    return 0;
00897 }
00898 
00899 static int agent_hangup(struct ast_channel *ast)
00900 {
00901    struct agent_pvt *p = ast->tech_pvt;
00902    int howlong = 0;
00903    const char *status;
00904    ast_mutex_lock(&p->lock);
00905    p->owner = NULL;
00906    ast->tech_pvt = NULL;
00907    p->app_sleep_cond = 1;
00908    p->acknowledged = 0;
00909 
00910    /* Release ownership of the agent to other threads (presumably running the login app). */
00911    if (ast_strlen_zero(p->loginchan)) {
00912       p->app_lock_flag = 0;
00913       ast_cond_signal(&p->app_complete_cond);
00914    }
00915 
00916    /* if they really are hung up then set start to 0 so the test
00917     * later if we're called on an already downed channel
00918     * doesn't cause an agent to be logged out like when
00919     * agent_request() is followed immediately by agent_hangup()
00920     * as in apps/app_chanisavail.c:chanavail_exec()
00921     */
00922 
00923    if (option_debug)
00924       ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state));
00925    if (p->start && (ast->_state != AST_STATE_UP)) {
00926       howlong = time(NULL) - p->start;
00927       p->start = 0;
00928    } else if (ast->_state == AST_STATE_RESERVED) 
00929       howlong = 0;
00930    else
00931       p->start = 0; 
00932    if (p->chan) {
00933       p->chan->_bridge = NULL;
00934       /* If they're dead, go ahead and hang up on the agent now */
00935       if (!ast_strlen_zero(p->loginchan)) {
00936          /* Store last disconnect time */
00937          if (p->wrapuptime)
00938             p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00939          else
00940             p->lastdisc = ast_tv(0,0);
00941          if (p->chan) {
00942             status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00943             if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00944                long logintime = time(NULL) - p->loginstart;
00945                p->loginstart = 0;
00946                ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
00947                agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00948             }
00949             /* Recognize the hangup and pass it along immediately */
00950             ast_hangup(p->chan);
00951             p->chan = NULL;
00952             p->inherited_devicestate = -1;
00953             ast_device_state_changed("Agent/%s", p->agent);
00954          }
00955          ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
00956          if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
00957             long logintime = time(NULL) - p->loginstart;
00958             p->loginstart = 0;
00959             if (!p->deferlogoff)
00960                ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00961             p->deferlogoff = 0;
00962             agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
00963             if (persistent_agents)
00964                dump_agents();
00965          }
00966       } else if (p->dead) {
00967          ast_channel_lock(p->chan);
00968          ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00969          ast_channel_unlock(p->chan);
00970       } else if (p->loginstart) {
00971          ast_channel_lock(p->chan);
00972          ast_indicate_data(p->chan, AST_CONTROL_HOLD, 
00973             S_OR(p->moh, NULL),
00974             !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
00975          ast_channel_unlock(p->chan);
00976       }
00977    }
00978    ast_mutex_unlock(&p->lock);
00979 
00980    /* Only register a device state change if the agent is still logged in */
00981    if (!p->loginstart) {
00982       p->loginchan[0] = '\0';
00983       p->logincallerid[0] = '\0';
00984       if (persistent_agents)
00985          dump_agents();
00986    } else {
00987       ast_device_state_changed("Agent/%s", p->agent);
00988    }
00989 
00990    if (p->pending) {
00991       AST_LIST_LOCK(&agents);
00992       AST_LIST_REMOVE(&agents, p, list);
00993       AST_LIST_UNLOCK(&agents);
00994    }
00995    if (p->abouttograb) {
00996       /* Let the "about to grab" thread know this isn't valid anymore, and let it
00997          kill it later */
00998       p->abouttograb = 0;
00999    } else if (p->dead) {
01000       ast_mutex_destroy(&p->lock);
01001       ast_cond_destroy(&p->app_complete_cond);
01002       ast_cond_destroy(&p->login_wait_cond);
01003       free(p);
01004    } else {
01005       if (p->chan) {
01006          /* Not dead -- check availability now */
01007          ast_mutex_lock(&p->lock);
01008          /* Store last disconnect time */
01009          p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
01010          ast_mutex_unlock(&p->lock);
01011       }
01012    }
01013    return 0;
01014 }
01015 
01016 static int agent_cont_sleep( void *data )
01017 {
01018    struct agent_pvt *p;
01019    int res;
01020 
01021    p = (struct agent_pvt *)data;
01022 
01023    ast_mutex_lock(&p->lock);
01024    res = p->app_sleep_cond;
01025    if (p->lastdisc.tv_sec) {
01026       if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) 
01027          res = 1;
01028    }
01029    ast_mutex_unlock(&p->lock);
01030 
01031    if(option_debug > 4 && !res )
01032       ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
01033 
01034    return res;
01035 }
01036 
01037 static int agent_ack_sleep(void *data)
01038 {
01039    struct agent_pvt *p;
01040    int res=0;
01041    int to = 1000;
01042    struct ast_frame *f;
01043 
01044    /* Wait a second and look for something */
01045 
01046    p = (struct agent_pvt *) data;
01047    if (!p->chan) 
01048       return -1;
01049 
01050    for(;;) {
01051       to = ast_waitfor(p->chan, to);
01052       if (to < 0) 
01053          return -1;
01054       if (!to) 
01055          return 0;
01056       f = ast_read(p->chan);
01057       if (!f) 
01058          return -1;
01059       if (f->frametype == AST_FRAME_DTMF)
01060          res = f->subclass;
01061       else
01062          res = 0;
01063       ast_frfree(f);
01064       ast_mutex_lock(&p->lock);
01065       if (!p->app_sleep_cond) {
01066          ast_mutex_unlock(&p->lock);
01067          return 0;
01068       } else if (res == '#') {
01069          ast_mutex_unlock(&p->lock);
01070          return 1;
01071       }
01072       ast_mutex_unlock(&p->lock);
01073       res = 0;
01074    }
01075    return res;
01076 }
01077 
01078 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
01079 {
01080    struct agent_pvt *p = bridge->tech_pvt;
01081    struct ast_channel *ret = NULL;
01082 
01083    if (p) {
01084       if (chan == p->chan)
01085          ret = bridge->_bridge;
01086       else if (chan == bridge->_bridge)
01087          ret = p->chan;
01088    }
01089 
01090    if (option_debug)
01091       ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
01092    return ret;
01093 }
01094 
01095 /*! \brief Create new agent channel */
01096 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
01097 {
01098    struct ast_channel *tmp;
01099 #if 0
01100    if (!p->chan) {
01101       ast_log(LOG_WARNING, "No channel? :(\n");
01102       return NULL;
01103    }
01104 #endif   
01105    if (p->pending)
01106       tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff);
01107    else
01108       tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
01109    if (!tmp) {
01110       ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
01111       return NULL;
01112    }
01113 
01114    tmp->tech = &agent_tech;
01115    if (p->chan) {
01116       tmp->nativeformats = p->chan->nativeformats;
01117       tmp->writeformat = p->chan->writeformat;
01118       tmp->rawwriteformat = p->chan->writeformat;
01119       tmp->readformat = p->chan->readformat;
01120       tmp->rawreadformat = p->chan->readformat;
01121       ast_string_field_set(tmp, language, p->chan->language);
01122       ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
01123       ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
01124       /* XXX Is this really all we copy form the originating channel?? */
01125    } else {
01126       tmp->nativeformats = AST_FORMAT_SLINEAR;
01127       tmp->writeformat = AST_FORMAT_SLINEAR;
01128       tmp->rawwriteformat = AST_FORMAT_SLINEAR;
01129       tmp->readformat = AST_FORMAT_SLINEAR;
01130       tmp->rawreadformat = AST_FORMAT_SLINEAR;
01131    }
01132    /* Safe, agentlock already held */
01133    tmp->tech_pvt = p;
01134    p->owner = tmp;
01135    /* XXX: this needs fixing */
01136 #if 0
01137    ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1);
01138 #endif
01139    ast_update_use_count();
01140    tmp->priority = 1;
01141    return tmp;
01142 }
01143 
01144 
01145 /*!
01146  * Read configuration data. The file named agents.conf.
01147  *
01148  * \returns Always 0, or so it seems.
01149  */
01150 static int read_agent_config(void)
01151 {
01152    struct ast_config *cfg;
01153    struct ast_config *ucfg;
01154    struct ast_variable *v;
01155    struct agent_pvt *p;
01156    const char *general_val;
01157    const char *catname;
01158    const char *hasagent;
01159    int genhasagent;
01160 
01161    memset(group, 0, sizeof(group));
01162    autologoff = 0;
01163    wrapuptime = 0;
01164    ackcall = 0;
01165    endcall = 1;
01166    cfg = ast_config_load(config);
01167    if (!cfg) {
01168       ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
01169       return 0;
01170    }
01171    AST_LIST_LOCK(&agents);
01172    AST_LIST_TRAVERSE(&agents, p, list) {
01173       p->dead = 1;
01174    }
01175    strcpy(moh, "default");
01176    /* set the default recording values */
01177    recordagentcalls = 0;
01178    strcpy(recordformat, "wav");
01179    strcpy(recordformatext, "wav");
01180    urlprefix[0] = '\0';
01181    savecallsin[0] = '\0';
01182 
01183    /* Read in [general] section for persistence */
01184    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
01185       persistent_agents = ast_true(general_val);
01186    multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
01187 
01188    /* Read in the [agents] section */
01189    v = ast_variable_browse(cfg, "agents");
01190    while(v) {
01191       /* Create the interface list */
01192       if (!strcasecmp(v->name, "agent")) {
01193          add_agent(v->value, 0);
01194       } else if (!strcasecmp(v->name, "group")) {
01195          ast_get_group(&group, v->value);
01196       } else if (!strcasecmp(v->name, "autologoff")) {
01197          autologoff = atoi(v->value);
01198          if (autologoff < 0)
01199             autologoff = 0;
01200       } else if (!strcasecmp(v->name, "ackcall")) {
01201          if (!strcasecmp(v->value, "always"))
01202             ackcall = 2;
01203          else if (ast_true(v->value))
01204             ackcall = 1;
01205          else
01206             ackcall = 0;
01207       } else if (!strcasecmp(v->name, "endcall")) {
01208          endcall = ast_true(v->value);
01209       } else if (!strcasecmp(v->name, "wrapuptime")) {
01210          wrapuptime = atoi(v->value);
01211          if (wrapuptime < 0)
01212             wrapuptime = 0;
01213       } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
01214          maxlogintries = atoi(v->value);
01215          if (maxlogintries < 0)
01216             maxlogintries = 0;
01217       } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
01218          strcpy(agentgoodbye,v->value);
01219       } else if (!strcasecmp(v->name, "musiconhold")) {
01220          ast_copy_string(moh, v->value, sizeof(moh));
01221       } else if (!strcasecmp(v->name, "updatecdr")) {
01222          if (ast_true(v->value))
01223             updatecdr = 1;
01224          else
01225             updatecdr = 0;
01226       } else if (!strcasecmp(v->name, "autologoffunavail")) {
01227          if (ast_true(v->value))
01228             autologoffunavail = 1;
01229          else
01230             autologoffunavail = 0;
01231       } else if (!strcasecmp(v->name, "recordagentcalls")) {
01232          recordagentcalls = ast_true(v->value);
01233       } else if (!strcasecmp(v->name, "recordformat")) {
01234          ast_copy_string(recordformat, v->value, sizeof(recordformat));
01235          if (!strcasecmp(v->value, "wav49"))
01236             strcpy(recordformatext, "WAV");
01237          else
01238             ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
01239       } else if (!strcasecmp(v->name, "urlprefix")) {
01240          ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
01241          if (urlprefix[strlen(urlprefix) - 1] != '/')
01242             strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
01243       } else if (!strcasecmp(v->name, "savecallsin")) {
01244          if (v->value[0] == '/')
01245             ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
01246          else
01247             snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
01248          if (savecallsin[strlen(savecallsin) - 1] != '/')
01249             strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
01250       } else if (!strcasecmp(v->name, "custom_beep")) {
01251          ast_copy_string(beep, v->value, sizeof(beep));
01252       }
01253       v = v->next;
01254    }
01255    if ((ucfg = ast_config_load("users.conf"))) {
01256       genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
01257       catname = ast_category_browse(ucfg, NULL);
01258       while(catname) {
01259          if (strcasecmp(catname, "general")) {
01260             hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
01261             if (ast_true(hasagent) || (!hasagent && genhasagent)) {
01262                char tmp[256];
01263                const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
01264                const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
01265                if (!fullname)
01266                   fullname = "";
01267                if (!secret)
01268                   secret = "";
01269                snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
01270                add_agent(tmp, 0);
01271             }
01272          }
01273          catname = ast_category_browse(ucfg, catname);
01274       }
01275       ast_config_destroy(ucfg);
01276    }
01277    AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
01278       if (p->dead) {
01279          AST_LIST_REMOVE_CURRENT(&agents, list);
01280          /* Destroy if  appropriate */
01281          if (!p->owner) {
01282             if (!p->chan) {
01283                ast_mutex_destroy(&p->lock);
01284                ast_cond_destroy(&p->app_complete_cond);
01285                ast_cond_destroy(&p->login_wait_cond);
01286                free(p);
01287             } else {
01288                /* Cause them to hang up */
01289                ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01290             }
01291          }
01292       }
01293    }
01294    AST_LIST_TRAVERSE_SAFE_END
01295    AST_LIST_UNLOCK(&agents);
01296    ast_config_destroy(cfg);
01297    return 1;
01298 }
01299 
01300 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
01301 {
01302    struct ast_channel *chan=NULL, *parent=NULL;
01303    struct agent_pvt *p;
01304    int res;
01305 
01306    if (option_debug)
01307       ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
01308    if (needlock)
01309       AST_LIST_LOCK(&agents);
01310    AST_LIST_TRAVERSE(&agents, p, list) {
01311       if (p == newlyavailable) {
01312          continue;
01313       }
01314       ast_mutex_lock(&p->lock);
01315       if (!p->abouttograb && p->pending && ((p->group && ast_have_common_group(&newlyavailable->group, &p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01316          if (option_debug)
01317             ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01318          /* We found a pending call, time to merge */
01319          chan = agent_new(newlyavailable, AST_STATE_DOWN);
01320          parent = p->owner;
01321          p->abouttograb = 1;
01322          ast_mutex_unlock(&p->lock);
01323          break;
01324       }
01325       ast_mutex_unlock(&p->lock);
01326    }
01327    if (needlock)
01328       AST_LIST_UNLOCK(&agents);
01329    if (parent && chan)  {
01330       if (newlyavailable->ackcall > 1) {
01331          /* Don't do beep here */
01332          res = 0;
01333       } else {
01334          if (option_debug > 2)
01335             ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01336          res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01337          if (option_debug > 2)
01338             ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01339          if (!res) {
01340             res = ast_waitstream(newlyavailable->chan, "");
01341             ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01342          }
01343       }
01344       if (!res) {
01345          /* Note -- parent may have disappeared */
01346          if (p->abouttograb) {
01347             newlyavailable->acknowledged = 1;
01348             /* Safe -- agent lock already held */
01349             ast_setstate(parent, AST_STATE_UP);
01350             ast_setstate(chan, AST_STATE_UP);
01351             ast_copy_string(parent->context, chan->context, sizeof(parent->context));
01352             /* Go ahead and mark the channel as a zombie so that masquerade will
01353                destroy it for us, and we need not call ast_hangup */
01354             ast_mutex_lock(&parent->lock);
01355             ast_set_flag(chan, AST_FLAG_ZOMBIE);
01356             ast_channel_masquerade(parent, chan);
01357             ast_mutex_unlock(&parent->lock);
01358             p->abouttograb = 0;
01359          } else {
01360             if (option_debug)
01361                ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
01362             agent_cleanup(newlyavailable);
01363          }
01364       } else {
01365          if (option_debug)
01366             ast_log(LOG_DEBUG, "Ugh...  Agent hung up at exactly the wrong time\n");
01367          agent_cleanup(newlyavailable);
01368       }
01369    }
01370    return 0;
01371 }
01372 
01373 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
01374 {
01375    struct agent_pvt *p;
01376    int res=0;
01377 
01378    ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
01379    if (needlock)
01380       AST_LIST_LOCK(&agents);
01381    AST_LIST_TRAVERSE(&agents, p, list) {
01382       if (p == newlyavailable) {
01383          continue;
01384       }
01385       ast_mutex_lock(&p->lock);
01386       if (!p->abouttograb && p->pending && ((p->group && ast_have_common_group(&newlyavailable->group, &p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01387          if (option_debug)
01388             ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01389          ast_mutex_unlock(&p->lock);
01390          break;
01391       }
01392       ast_mutex_unlock(&p->lock);
01393    }
01394    if (needlock)
01395       AST_LIST_UNLOCK(&agents);
01396    if (p) {
01397       ast_mutex_unlock(&newlyavailable->lock);
01398       if (option_debug > 2)
01399          ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01400       res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01401       if (option_debug > 2)
01402          ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01403       if (!res) {
01404          res = ast_waitstream(newlyavailable->chan, "");
01405          if (option_debug)
01406             ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01407       }
01408       ast_mutex_lock(&newlyavailable->lock);
01409    }
01410    return res;
01411 }
01412 
01413 /* return 1 if multiple login is fine, 0 if it is not and we find a match, -1 if multiplelogin is not allowed and we don't find a match. */
01414 static int allow_multiple_login(char *chan, char *context)
01415 {
01416    struct agent_pvt *p;
01417    char loginchan[80];
01418 
01419    if (multiplelogin) {
01420       return 1;
01421    }
01422    if (!chan) {
01423       return 0;
01424    }
01425 
01426    snprintf(loginchan, sizeof(loginchan), "%s@%s", chan, S_OR(context, "default"));
01427    
01428    AST_LIST_TRAVERSE(&agents, p, list) {
01429       if(!strcasecmp(loginchan, p->loginchan))
01430          return 0;
01431    }
01432    return -1;
01433 }
01434 
01435 /*! \brief Part of the Asterisk PBX interface */
01436 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
01437 {
01438    struct agent_pvt *p;
01439    struct ast_channel *chan = NULL;
01440    char *s;
01441    ast_group_t groupmatch;
01442    int groupoff;
01443    int waitforagent=0;
01444    int hasagent = 0;
01445    struct timeval tv;
01446 
01447    s = data;
01448    if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01449       char tmp[12];
01450       sprintf(tmp, "%d", groupoff);
01451       ast_get_group(&groupmatch, tmp);
01452    } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01453       char tmp[12];
01454       sprintf(tmp, "%d", groupoff);
01455       ast_get_group(&groupmatch, tmp);
01456       waitforagent = 1;
01457    } else {
01458       memset(groupmatch, 0, sizeof(groupmatch));
01459    }
01460 
01461    /* Check actual logged in agents first */
01462    AST_LIST_LOCK(&agents);
01463    AST_LIST_TRAVERSE(&agents, p, list) {
01464       ast_mutex_lock(&p->lock);
01465       if (!p->pending && ((!ast_is_empty_group(&groupmatch) && ast_have_common_group(&p->group, &groupmatch)) || !strcmp(data, p->agent)) &&
01466           ast_strlen_zero(p->loginchan)) {
01467          if (p->chan)
01468             hasagent++;
01469          tv = ast_tvnow();
01470          if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
01471             p->lastdisc = ast_tv(0, 0);
01472             /* Agent must be registered, but not have any active call, and not be in a waiting state */
01473             if (!p->owner && p->chan) {
01474                /* Fixed agent */
01475                chan = agent_new(p, AST_STATE_DOWN);
01476             }
01477             if (chan) {
01478                ast_mutex_unlock(&p->lock);
01479                break;
01480             }
01481          }
01482       }
01483       ast_mutex_unlock(&p->lock);
01484    }
01485    if (!p) {
01486       AST_LIST_TRAVERSE(&agents, p, list) {
01487          ast_mutex_lock(&p->lock);
01488          if (!p->pending && ((!ast_is_empty_group(&groupmatch) && ast_have_common_group(&p->group, &groupmatch)) || !strcmp(data, p->agent))) {
01489             if (p->chan || !ast_strlen_zero(p->loginchan))
01490                hasagent++;
01491             tv = ast_tvnow();
01492 #if 0
01493             ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
01494 #endif
01495             if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
01496                p->lastdisc = ast_tv(0, 0);
01497                /* Agent must be registered, but not have any active call, and not be in a waiting state */
01498                if (!p->owner && p->chan) {
01499                   /* Could still get a fixed agent */
01500                   chan = agent_new(p, AST_STATE_DOWN);
01501                } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
01502                   /* Adjustable agent */
01503                   p->chan = ast_request("Local", format, p->loginchan, cause);
01504                   if (p->chan)
01505                      chan = agent_new(p, AST_STATE_DOWN);
01506                }
01507                if (chan) {
01508                   ast_mutex_unlock(&p->lock);
01509                   break;
01510                }
01511             }
01512          }
01513          ast_mutex_unlock(&p->lock);
01514       }
01515    }
01516 
01517    if (!chan && waitforagent) {
01518       /* No agent available -- but we're requesting to wait for one.
01519          Allocate a place holder */
01520       if (hasagent) {
01521          if (option_debug)
01522             ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s);
01523          p = add_agent(data, 1);
01524          memcpy(&p->group, &groupmatch, sizeof(p->group));
01525          chan = agent_new(p, AST_STATE_DOWN);
01526          if (!chan)
01527             ast_log(LOG_WARNING, "Weird...  Fix this to drop the unused pending agent\n");
01528       } else
01529          ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
01530    }
01531    *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
01532    AST_LIST_UNLOCK(&agents);
01533 
01534    if (chan) {
01535       ast_mutex_lock(&p->lock);
01536       if (p->pending) {
01537          ast_mutex_unlock(&p->lock);
01538          return chan;
01539       }
01540 
01541       if (!p->chan) {
01542          ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
01543          *cause = AST_CAUSE_UNREGISTERED;
01544          ast_mutex_unlock(&p->lock);
01545          agent_hangup(chan);
01546          return NULL;
01547       }
01548 
01549       /* when not in callback mode we need to take control of the channel
01550        * from the login app thread */
01551       if(ast_strlen_zero(p->loginchan)) {
01552          p->app_sleep_cond = 0;
01553          p->app_lock_flag = 1;
01554 
01555          ast_queue_frame(p->chan, &ast_null_frame);
01556          ast_cond_wait(&p->login_wait_cond, &p->lock);
01557 
01558          if (!p->chan) {
01559             ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
01560             p->app_sleep_cond = 1;
01561             p->app_lock_flag = 0;
01562             ast_cond_signal(&p->app_complete_cond);
01563             ast_mutex_unlock(&p->lock);
01564             *cause = AST_CAUSE_UNREGISTERED;
01565             agent_hangup(chan);
01566             return NULL;
01567          }
01568 
01569          ast_indicate(p->chan, AST_CONTROL_UNHOLD);
01570       }
01571       ast_mutex_unlock(&p->lock);
01572    }
01573 
01574    return chan;
01575 }
01576 
01577 /*!
01578  * Lists agents and their status to the Manager API.
01579  * It is registered on load_module() and it gets called by the manager backend.
01580  * \param s
01581  * \param m
01582  * \returns 
01583  * \sa action_agent_logoff(), action_agent_callback_login(), load_module().
01584  */
01585 static int action_agents(struct mansession *s, const struct message *m)
01586 {
01587    const char *id = astman_get_header(m,"ActionID");
01588    char idText[256] = "";
01589    char chanbuf[256];
01590    struct agent_pvt *p;
01591    char *username = NULL;
01592    char *loginChan = NULL;
01593    char *talkingtoChan = NULL;
01594    char *status = NULL;
01595 
01596    if (!ast_strlen_zero(id))
01597       snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
01598    astman_send_ack(s, m, "Agents will follow");
01599    AST_LIST_LOCK(&agents);
01600    AST_LIST_TRAVERSE(&agents, p, list) {
01601          ast_mutex_lock(&p->lock);
01602 
01603       /* Status Values:
01604          AGENT_LOGGEDOFF - Agent isn't logged in
01605          AGENT_IDLE      - Agent is logged in, and waiting for call
01606          AGENT_ONCALL    - Agent is logged in, and on a call
01607          AGENT_UNKNOWN   - Don't know anything about agent. Shouldn't ever get this. */
01608 
01609       username = S_OR(p->name, "None");
01610 
01611       /* Set a default status. It 'should' get changed. */
01612       status = "AGENT_UNKNOWN";
01613 
01614       if (!ast_strlen_zero(p->loginchan) && !p->chan) {
01615          loginChan = p->loginchan;
01616          talkingtoChan = "n/a";
01617          status = "AGENT_IDLE";
01618          if (p->acknowledged) {
01619             snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
01620             loginChan = chanbuf;
01621          }
01622       } else if (p->chan) {
01623          loginChan = ast_strdupa(p->chan->name);
01624          if (p->owner && p->owner->_bridge) {
01625             if (ast_bridged_channel(p->owner)) {
01626                talkingtoChan = ast_strdupa(S_OR(ast_bridged_channel(p->owner)->cid.cid_num, ""));
01627             } else {
01628                talkingtoChan = "n/a";
01629             }
01630                status = "AGENT_ONCALL";
01631          } else {
01632                talkingtoChan = "n/a";
01633                status = "AGENT_IDLE";
01634          }
01635       } else {
01636          loginChan = "n/a";
01637          talkingtoChan = "n/a";
01638          status = "AGENT_LOGGEDOFF";
01639       }
01640 
01641       astman_append(s, "Event: Agents\r\n"
01642          "Agent: %s\r\n"
01643          "Name: %s\r\n"
01644          "Status: %s\r\n"
01645          "LoggedInChan: %s\r\n"
01646          "LoggedInTime: %d\r\n"
01647          "TalkingTo: %s\r\n"
01648          "%s"
01649          "\r\n",
01650          p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText);
01651       ast_mutex_unlock(&p->lock);
01652    }
01653    AST_LIST_UNLOCK(&agents);
01654    astman_append(s, "Event: AgentsComplete\r\n"
01655       "%s"
01656       "\r\n",idText);
01657    return 0;
01658 }
01659 
01660 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
01661 {
01662    char *tmp = NULL;
01663    char agent[AST_MAX_AGENT];
01664 
01665    if (!ast_strlen_zero(logcommand))
01666       tmp = logcommand;
01667    else
01668       tmp = ast_strdupa("");
01669 
01670    snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01671 
01672    if (!ast_strlen_zero(uniqueid)) {
01673       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01674             "Agent: %s\r\n"
01675             "Reason: %s\r\n"
01676             "Loginchan: %s\r\n"
01677             "Logintime: %ld\r\n"
01678             "Uniqueid: %s\r\n", 
01679             p->agent, tmp, loginchan, logintime, uniqueid);
01680    } else {
01681       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01682             "Agent: %s\r\n"
01683             "Reason: %s\r\n"
01684             "Loginchan: %s\r\n"
01685             "Logintime: %ld\r\n",
01686             p->agent, tmp, loginchan, logintime);
01687    }
01688 
01689    ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
01690    set_agentbycallerid(p->logincallerid, NULL);
01691    p->loginchan[0] ='\0';
01692    p->logincallerid[0] = '\0';
01693    p->inherited_devicestate = -1;
01694    ast_device_state_changed("Agent/%s", p->agent);
01695    if (persistent_agents)
01696       dump_agents();
01697 
01698 }
01699 
01700 static int agent_logoff(const char *agent, int soft)
01701 {
01702    struct agent_pvt *p;
01703    long logintime;
01704    int ret = -1; /* Return -1 if no agent if found */
01705 
01706    AST_LIST_LOCK(&agents);
01707    AST_LIST_TRAVERSE(&agents, p, list) {
01708       if (!strcasecmp(p->agent, agent)) {
01709          ret = 0;
01710          if (p->owner || p->chan) {
01711             if (!soft) {
01712                ast_mutex_lock(&p->lock);
01713 
01714                while (p->owner && ast_channel_trylock(p->owner)) {
01715                   DEADLOCK_AVOIDANCE(&p->lock);
01716                }
01717                if (p->owner) {
01718                   ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
01719                   ast_channel_unlock(p->owner);
01720                }
01721 
01722                while (p->chan && ast_channel_trylock(p->chan)) {
01723                   DEADLOCK_AVOIDANCE(&p->lock);
01724                }
01725                if (p->chan) {
01726                   ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01727                   ast_channel_unlock(p->chan);
01728                }
01729 
01730                ast_mutex_unlock(&p->lock);
01731             } else
01732                p->deferlogoff = 1;
01733          } else {
01734             logintime = time(NULL) - p->loginstart;
01735             p->loginstart = 0;
01736             agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
01737          }
01738          break;
01739       }
01740    }
01741    AST_LIST_UNLOCK(&agents);
01742 
01743    return ret;
01744 }
01745 
01746 static int agent_logoff_cmd(int fd, int argc, char **argv)
01747 {
01748    int ret;
01749    char *agent;
01750 
01751    if (argc < 3 || argc > 4)
01752       return RESULT_SHOWUSAGE;
01753    if (argc == 4 && strcasecmp(argv[3], "soft"))
01754       return RESULT_SHOWUSAGE;
01755 
01756    agent = argv[2] + 6;
01757    ret = agent_logoff(agent, argc == 4);
01758    if (ret == 0)
01759       ast_cli(fd, "Logging out %s\n", agent);
01760 
01761    return RESULT_SUCCESS;
01762 }
01763 
01764 /*!
01765  * Sets an agent as no longer logged in in the Manager API.
01766  * It is registered on load_module() and it gets called by the manager backend.
01767  * \param s
01768  * \param m
01769  * \returns 
01770  * \sa action_agents(), action_agent_callback_login(), load_module().
01771  */
01772 static int action_agent_logoff(struct mansession *s, const struct message *m)
01773 {
01774    const char *agent = astman_get_header(m, "Agent");
01775    const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
01776    int soft;
01777    int ret; /* return value of agent_logoff */
01778 
01779    if (ast_strlen_zero(agent)) {
01780       astman_send_error(s, m, "No agent specified");
01781       return 0;
01782    }
01783 
01784    soft = ast_true(soft_s) ? 1 : 0;
01785    ret = agent_logoff(agent, soft);
01786    if (ret == 0)
01787       astman_send_ack(s, m, "Agent logged out");
01788    else
01789       astman_send_error(s, m, "No such agent");
01790 
01791    return 0;
01792 }
01793 
01794 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
01795 {
01796    char *ret = NULL;
01797 
01798    if (pos == 2) {
01799       struct agent_pvt *p;
01800       char name[AST_MAX_AGENT];
01801       int which = 0, len = strlen(word);
01802 
01803       AST_LIST_LOCK(&agents);
01804       AST_LIST_TRAVERSE(&agents, p, list) {
01805          snprintf(name, sizeof(name), "Agent/%s", p->agent);
01806          if (!strncasecmp(word, name, len) && ++which > state) {
01807             ret = ast_strdup(name);
01808             break;
01809          }
01810       }
01811       AST_LIST_UNLOCK(&agents);
01812    } else if (pos == 3 && state == 0) 
01813       return ast_strdup("soft");
01814    
01815    return ret;
01816 }
01817 
01818 /*!
01819  * Show agents in cli.
01820  */
01821 static int agents_show(int fd, int argc, char **argv)
01822 {
01823    struct agent_pvt *p;
01824    char username[AST_MAX_BUF];
01825    char location[AST_MAX_BUF] = "";
01826    char talkingto[AST_MAX_BUF] = "";
01827    char moh[AST_MAX_BUF];
01828    int count_agents = 0;      /*!< Number of agents configured */
01829    int online_agents = 0;     /*!< Number of online agents */
01830    int offline_agents = 0;    /*!< Number of offline agents */
01831    if (argc != 2)
01832       return RESULT_SHOWUSAGE;
01833    AST_LIST_LOCK(&agents);
01834    AST_LIST_TRAVERSE(&agents, p, list) {
01835       ast_mutex_lock(&p->lock);
01836       if (p->pending) {
01837          char tmp[256];
01838          if (p->group)
01839             ast_cli(fd, "-- Pending call to group %s\n", ast_print_group(tmp, sizeof(tmp), &p->group));
01840          else
01841             ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
01842       } else {
01843          if (!ast_strlen_zero(p->name))
01844             snprintf(username, sizeof(username), "(%s) ", p->name);
01845          else
01846             username[0] = '\0';
01847          if (p->chan) {
01848             snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01849             if (p->owner && ast_bridged_channel(p->owner))
01850                snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01851              else 
01852                strcpy(talkingto, " is idle");
01853             online_agents++;
01854          } else if (!ast_strlen_zero(p->loginchan)) {
01855             if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec)) 
01856                snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01857             else 
01858                snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
01859             talkingto[0] = '\0';
01860             online_agents++;
01861             if (p->acknowledged)
01862                strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01863          } else {
01864             strcpy(location, "not logged in");
01865             talkingto[0] = '\0';
01866             offline_agents++;
01867          }
01868          if (!ast_strlen_zero(p->moh))
01869             snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01870          ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, 
01871             username, location, talkingto, moh);
01872          count_agents++;
01873       }
01874       ast_mutex_unlock(&p->lock);
01875    }
01876    AST_LIST_UNLOCK(&agents);
01877    if ( !count_agents ) 
01878       ast_cli(fd, "No Agents are configured in %s\n",config);
01879    else 
01880       ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01881    ast_cli(fd, "\n");
01882                    
01883    return RESULT_SUCCESS;
01884 }
01885 
01886 
01887 static int agents_show_online(int fd, int argc, char **argv)
01888 {
01889    struct agent_pvt *p;
01890    char username[AST_MAX_BUF];
01891    char location[AST_MAX_BUF] = "";
01892    char talkingto[AST_MAX_BUF] = "";
01893    char moh[AST_MAX_BUF];
01894    int count_agents = 0;           /* Number of agents configured */
01895    int online_agents = 0;          /* Number of online agents */
01896    int agent_status = 0;           /* 0 means offline, 1 means online */
01897    if (argc != 3)
01898       return RESULT_SHOWUSAGE;
01899    AST_LIST_LOCK(&agents);
01900    AST_LIST_TRAVERSE(&agents, p, list) {
01901       agent_status = 0;       /* reset it to offline */
01902       ast_mutex_lock(&p->lock);
01903       if (!ast_strlen_zero(p->name))
01904          snprintf(username, sizeof(username), "(%s) ", p->name);
01905       else
01906          username[0] = '\0';
01907       if (p->chan) {
01908          snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01909          if (p->owner && ast_bridged_channel(p->owner)) 
01910             snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01911          else 
01912             strcpy(talkingto, " is idle");
01913          agent_status = 1;
01914          online_agents++;
01915       } else if (!ast_strlen_zero(p->loginchan)) {
01916          snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01917          talkingto[0] = '\0';
01918          agent_status = 1;
01919          online_agents++;
01920          if (p->acknowledged)
01921             strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01922       }
01923       if (!ast_strlen_zero(p->moh))
01924          snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01925       if (agent_status)
01926          ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
01927       count_agents++;
01928       ast_mutex_unlock(&p->lock);
01929    }
01930    AST_LIST_UNLOCK(&agents);
01931    if (!count_agents) 
01932       ast_cli(fd, "No Agents are configured in %s\n", config);
01933    else
01934       ast_cli(fd, "%d agents online\n", online_agents);
01935    ast_cli(fd, "\n");
01936    return RESULT_SUCCESS;
01937 }
01938 
01939 
01940 
01941 static char show_agents_usage[] = 
01942 "Usage: agent show\n"
01943 "       Provides summary information on agents.\n";
01944 
01945 static char show_agents_online_usage[] =
01946 "Usage: agent show online\n"
01947 "  Provides a list of all online agents.\n";
01948 
01949 static char agent_logoff_usage[] =
01950 "Usage: agent logoff <channel> [soft]\n"
01951 "       Sets an agent as no longer logged in.\n"
01952 "       If 'soft' is specified, do not hangup existing calls.\n";
01953 
01954 static struct ast_cli_entry cli_show_agents_deprecated = {
01955    { "show", "agents", NULL },
01956    agents_show, NULL,
01957    NULL, NULL };
01958 
01959 static struct ast_cli_entry cli_show_agents_online_deprecated = {
01960    { "show", "agents", "online" },
01961    agents_show_online, NULL,
01962    NULL, NULL };
01963 
01964 static struct ast_cli_entry cli_agents[] = {
01965    { { "agent", "show", NULL },
01966    agents_show, "Show status of agents",
01967    show_agents_usage, NULL, &cli_show_agents_deprecated },
01968 
01969    { { "agent", "show", "online" },
01970    agents_show_online, "Show all online agents",
01971    show_agents_online_usage, NULL, &cli_show_agents_online_deprecated },
01972 
01973    { { "agent", "logoff", NULL },
01974    agent_logoff_cmd, "Sets an agent offline",
01975    agent_logoff_usage, complete_agent_logoff_cmd },
01976 };
01977 
01978 /*!
01979  * \brief Log in agent application.
01980  *
01981  * \param chan
01982  * \param data
01983  * \param callbackmode non-zero for AgentCallbackLogin
01984  */
01985 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
01986 {
01987    int res=0;
01988    int tries = 0;
01989    int max_login_tries = maxlogintries;
01990    struct agent_pvt *p;
01991    struct ast_module_user *u;
01992    int login_state = 0;
01993    char user[AST_MAX_AGENT] = "";
01994    char pass[AST_MAX_AGENT];
01995    char agent[AST_MAX_AGENT] = "";
01996    char xpass[AST_MAX_AGENT] = "";
01997    char *errmsg;
01998    char *parse;
01999    AST_DECLARE_APP_ARGS(args,
02000               AST_APP_ARG(agent_id);
02001               AST_APP_ARG(options);
02002               AST_APP_ARG(extension);
02003       );
02004    const char *tmpoptions = NULL;
02005    char *context = NULL;
02006    int play_announcement = 1;
02007    char agent_goodbye[AST_MAX_FILENAME_LEN];
02008    int update_cdr = updatecdr;
02009    char *filename = "agent-loginok";
02010    char tmpchan[AST_MAX_BUF] = "";
02011 
02012    u = ast_module_user_add(chan);
02013 
02014    parse = ast_strdupa(data);
02015 
02016    AST_STANDARD_APP_ARGS(args, parse);
02017 
02018    ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
02019 
02020    ast_channel_lock(chan);
02021    /* Set Channel Specific Login Overrides */
02022    if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
02023       max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
02024       if (max_login_tries < 0)
02025          max_login_tries = 0;
02026       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
02027       if (option_verbose > 2)
02028          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);
02029    }
02030    if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
02031       if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
02032          update_cdr = 1;
02033       else
02034          update_cdr = 0;
02035       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
02036       if (option_verbose > 2)
02037          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
02038    }
02039    if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
02040       strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
02041       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
02042       if (option_verbose > 2)
02043          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
02044    }
02045    ast_channel_unlock(chan);
02046    /* End Channel Specific Login Overrides */
02047    
02048    if (callbackmode && args.extension) {
02049       parse = args.extension;
02050       args.extension = strsep(&parse, "@");
02051       context = parse;
02052    }
02053 
02054    if (!ast_strlen_zero(args.options)) {
02055       if (strchr(args.options, 's')) {
02056          play_announcement = 0;
02057       }
02058    }
02059 
02060    if (chan->_state != AST_STATE_UP)
02061       res = ast_answer(chan);
02062    if (!res) {
02063       if (!ast_strlen_zero(args.agent_id))
02064          ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
02065       else
02066          res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
02067    }
02068    while (!res && (max_login_tries==0 || tries < max_login_tries)) {
02069       tries++;
02070       /* Check for password */
02071       AST_LIST_LOCK(&agents);
02072       AST_LIST_TRAVERSE(&agents, p, list) {
02073          if (!strcmp(p->agent, user) && !p->pending)
02074             ast_copy_string(xpass, p->password, sizeof(xpass));
02075       }
02076       AST_LIST_UNLOCK(&agents);
02077       if (!res) {
02078          if (!ast_strlen_zero(xpass))
02079             res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
02080          else
02081             pass[0] = '\0';
02082       }
02083       errmsg = "agent-incorrect";
02084 
02085 #if 0
02086       ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
02087 #endif      
02088 
02089       /* Check again for accuracy */
02090       AST_LIST_LOCK(&agents);
02091       AST_LIST_TRAVERSE(&agents, p, list) {
02092          int unlock_channel = 1;
02093          ast_channel_lock(chan);
02094          ast_mutex_lock(&p->lock);
02095          if (!strcmp(p->agent, user) &&
02096              !strcmp(p->password, pass) && !p->pending) {
02097             login_state = 1; /* Successful Login */
02098 
02099             /* Ensure we can't be gotten until we're done */
02100             gettimeofday(&p->lastdisc, NULL);
02101             p->lastdisc.tv_sec++;
02102 
02103             /* Set Channel Specific Agent Overrides */
02104             if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
02105                if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
02106                   p->ackcall = 2;
02107                else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
02108                   p->ackcall = 1;
02109                else
02110                   p->ackcall = 0;
02111                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
02112                if (option_verbose > 2)
02113                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
02114             } else {
02115                p->ackcall = ackcall;
02116             }
02117             if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
02118                p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
02119                if (p->autologoff < 0)
02120                   p->autologoff = 0;
02121                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
02122                if (option_verbose > 2)
02123                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
02124             } else {
02125                p->autologoff = autologoff;
02126             }
02127             if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
02128                p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
02129                if (p->wrapuptime < 0)
02130                   p->wrapuptime = 0;
02131                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
02132                if (option_verbose > 2)
02133                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
02134             } else {
02135                p->wrapuptime = wrapuptime;
02136             }
02137             ast_channel_unlock(chan);
02138             unlock_channel = 0;
02139             /* End Channel Specific Agent Overrides */
02140             if (!p->chan) {
02141                char last_loginchan[80] = "";
02142                long logintime;
02143                snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
02144 
02145                if (callbackmode) {
02146                   int pos = 0;
02147                   /* Retrieve login chan */
02148                   for (;;) {
02149                      if (!ast_strlen_zero(args.extension)) {
02150                         ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
02151                         res = 0;
02152                      } else
02153                         res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
02154                      if (ast_strlen_zero(tmpchan) )
02155                         break;
02156                      if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
02157                         if(!allow_multiple_login(tmpchan,context) ) {
02158                            args.extension = NULL;
02159                            pos = 0;
02160                         } else
02161                            break;
02162                      }
02163                      if (args.extension) {
02164                         ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
02165                         args.extension = NULL;
02166                         pos = 0;
02167                      } else {
02168                         ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
02169                         res = ast_streamfile(chan, "invalid", chan->language);
02170                         if (!res)
02171                            res = ast_waitstream(chan, AST_DIGIT_ANY);
02172                         if (res > 0) {
02173                            tmpchan[0] = res;
02174                            tmpchan[1] = '\0';
02175                            pos = 1;
02176                         } else {
02177                            tmpchan[0] = '\0';
02178                            pos = 0;
02179                         }
02180                      }
02181                   }
02182                   args.extension = tmpchan;
02183                   if (!res) {
02184                      set_agentbycallerid(p->logincallerid, NULL);
02185                      if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
02186                         snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
02187                      else {
02188                         ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
02189                         ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
02190                      }
02191                      p->acknowledged = 0;
02192                      if (ast_strlen_zero(p->loginchan)) {
02193                         login_state = 2;
02194                         filename = "agent-loggedoff";
02195                      } else {
02196                         if (chan->cid.cid_num) {
02197                            ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
02198                            set_agentbycallerid(p->logincallerid, p->agent);
02199                         } else
02200                            p->logincallerid[0] = '\0';
02201                      }
02202 
02203                      if(update_cdr && chan->cdr)
02204                         snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02205 
02206                   }
02207                } else {
02208                   p->loginchan[0] = '\0';
02209                   p->logincallerid[0] = '\0';
02210                   p->acknowledged = 0;
02211                }
02212                ast_mutex_unlock(&p->lock);
02213                AST_LIST_UNLOCK(&agents);
02214                if( !res && play_announcement==1 )
02215                   res = ast_streamfile(chan, filename, chan->language);
02216                if (!res)
02217                   ast_waitstream(chan, "");
02218                AST_LIST_LOCK(&agents);
02219                ast_mutex_lock(&p->lock);
02220                if (!res) {
02221                   res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
02222                   if (res)
02223                      ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
02224                }
02225                if (!res) {
02226                   res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
02227                   if (res)
02228                      ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
02229                }
02230                /* Check once more just in case */
02231                if (p->chan)
02232                   res = -1;
02233                if (callbackmode && !res) {
02234                   /* Just say goodbye and be done with it */
02235                   if (!ast_strlen_zero(p->loginchan)) {
02236                      if (p->loginstart == 0)
02237                         time(&p->loginstart);
02238                      manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02239                               "Agent: %s\r\n"
02240                               "Loginchan: %s\r\n"
02241                               "Uniqueid: %s\r\n",
02242                               p->agent, p->loginchan, chan->uniqueid);
02243                      ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02244                      if (option_verbose > 1)
02245                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02246                      ast_device_state_changed("Agent/%s", p->agent);
02247                      if (persistent_agents)
02248                         dump_agents();
02249                   } else {
02250                      logintime = time(NULL) - p->loginstart;
02251                      p->loginstart = 0;
02252 
02253                      agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
02254                      if (option_verbose > 1)
02255                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
02256                   }
02257                   AST_LIST_UNLOCK(&agents);
02258                   if (!res)
02259                      res = ast_safe_sleep(chan, 500);
02260                   ast_mutex_unlock(&p->lock);
02261                } else if (!res) {
02262                   ast_indicate_data(chan, AST_CONTROL_HOLD, 
02263                      S_OR(p->moh, NULL), 
02264                      !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02265                   if (p->loginstart == 0)
02266                      time(&p->loginstart);
02267                   manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02268                            "Agent: %s\r\n"
02269                            "Channel: %s\r\n"
02270                            "Uniqueid: %s\r\n",
02271                            p->agent, chan->name, chan->uniqueid);
02272                   if (update_cdr && chan->cdr)
02273                      snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02274                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
02275                   if (option_verbose > 1)
02276                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
02277                             ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
02278                   /* Login this channel and wait for it to go away */
02279                   p->chan = chan;
02280                   if (p->ackcall > 1)
02281                      check_beep(p, 0);
02282                   else
02283                      check_availability(p, 0);
02284                   ast_mutex_unlock(&p->lock);
02285                   AST_LIST_UNLOCK(&agents);
02286                   ast_device_state_changed("Agent/%s", p->agent);
02287                   while (res >= 0) {
02288                      ast_mutex_lock(&p->lock);
02289                      if (p->deferlogoff && p->chan) {
02290                         ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
02291                         p->deferlogoff = 0;
02292                      }
02293                      if (p->chan != chan)
02294                         res = -1;
02295                      ast_mutex_unlock(&p->lock);
02296                      /* Yield here so other interested threads can kick in. */
02297                      sched_yield();
02298                      if (res)
02299                         break;
02300 
02301                      AST_LIST_LOCK(&agents);
02302                      ast_mutex_lock(&p->lock);
02303                      if (p->lastdisc.tv_sec) {
02304                         if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
02305                            if (option_debug)
02306                               ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
02307                            p->lastdisc = ast_tv(0, 0);
02308                            ast_device_state_changed("Agent/%s", p->agent);
02309                            if (p->ackcall > 1)
02310                               check_beep(p, 0);
02311                            else
02312                               check_availability(p, 0);
02313                         }
02314                      }
02315                      ast_mutex_unlock(&p->lock);
02316                      AST_LIST_UNLOCK(&agents);
02317 
02318                      /* Synchronize channel ownership between call to agent and itself. */
02319                      ast_mutex_lock(&p->lock);
02320                      if (p->app_lock_flag == 1) {
02321                         ast_cond_signal(&p->login_wait_cond);
02322                         ast_cond_wait(&p->app_complete_cond, &p->lock);
02323                      }
02324                      ast_mutex_unlock(&p->lock);
02325 
02326                      if (p->ackcall > 1) 
02327                         res = agent_ack_sleep(p);
02328                      else
02329                         res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02330                      if ((p->ackcall > 1)  && (res == 1)) {
02331                         AST_LIST_LOCK(&agents);
02332                         ast_mutex_lock(&p->lock);
02333                         check_availability(p, 0);
02334                         ast_mutex_unlock(&p->lock);
02335                         AST_LIST_UNLOCK(&agents);
02336                         res = 0;
02337                      }
02338                      sched_yield();
02339                   }
02340                   ast_mutex_lock(&p->lock);
02341                   /* Log us off if appropriate */
02342                   if (p->chan == chan) {
02343                      p->chan = NULL;
02344                      p->inherited_devicestate = -1;
02345                   }
02346 
02347                   /* Synchronize channel ownership between call to agent and itself. */
02348                   if (p->app_lock_flag == 1) {
02349                      ast_cond_signal(&p->login_wait_cond);
02350                      ast_cond_wait(&p->app_complete_cond, &p->lock);
02351                   }
02352 
02353                   if (res && p->owner)
02354                      ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
02355 
02356                   p->acknowledged = 0;
02357                   logintime = time(NULL) - p->loginstart;
02358                   p->loginstart = 0;
02359                   ast_mutex_unlock(&p->lock);
02360                   manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02361                            "Agent: %s\r\n"
02362                            "Logintime: %ld\r\n"
02363                            "Uniqueid: %s\r\n",
02364                            p->agent, logintime, chan->uniqueid);
02365                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02366                   if (option_verbose > 1)
02367                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
02368                   /* If there is no owner, go ahead and kill it now */
02369                   ast_device_state_changed("Agent/%s", p->agent);
02370                   if (p->dead && !p->owner) {
02371                      ast_mutex_destroy(&p->lock);
02372                      ast_cond_destroy(&p->app_complete_cond);
02373                      ast_cond_destroy(&p->login_wait_cond);
02374                      free(p);
02375                   }
02376                }
02377                else {
02378                   ast_mutex_unlock(&p->lock);
02379                   p = NULL;
02380                }
02381                res = -1;
02382             } else {
02383                ast_mutex_unlock(&p->lock);
02384                errmsg = "agent-alreadyon";
02385                p = NULL;
02386             }
02387             break;
02388          }
02389          ast_mutex_unlock(&p->lock);
02390          if (unlock_channel) {
02391             ast_channel_unlock(chan);
02392          }
02393       }
02394       if (!p)
02395          AST_LIST_UNLOCK(&agents);
02396 
02397       if (!res && (max_login_tries==0 || tries < max_login_tries))
02398          res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02399    }
02400       
02401    if (!res)
02402       res = ast_safe_sleep(chan, 500);
02403 
02404    /* AgentLogin() exit */
02405    if (!callbackmode) {
02406       ast_module_user_remove(u);
02407       return -1;
02408    } else { /* AgentCallbackLogin() exit*/
02409       /* Set variables */
02410       if (login_state > 0) {
02411          pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
02412          if (login_state==1) {
02413             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
02414             pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
02415          } else 
02416             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
02417       } else {
02418          pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
02419       }
02420       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
02421          ast_module_user_remove(u);
02422          return 0;
02423       }
02424       /* Do we need to play agent-goodbye now that we will be hanging up? */
02425       if (play_announcement) {
02426          if (!res)
02427             res = ast_safe_sleep(chan, 1000);
02428          res = ast_streamfile(chan, agent_goodbye, chan->language);
02429          if (!res)
02430             res = ast_waitstream(chan, "");
02431          if (!res)
02432             res = ast_safe_sleep(chan, 1000);
02433       }
02434    }
02435 
02436    ast_module_user_remove(u);
02437    
02438    /* We should never get here if next priority exists when in callbackmode */
02439    return -1;
02440 }
02441 
02442 /*!
02443  * Called by the AgentLogin application (from the dial plan).
02444  * 
02445  * \param chan
02446  * \param data
02447  * \returns
02448  * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
02449  */
02450 static int login_exec(struct ast_channel *chan, void *data)
02451 {
02452    return __login_exec(chan, data, 0);
02453 }
02454 
02455 static void callback_deprecated(void)
02456 {
02457    static int depwarning = 0;
02458 
02459    if (!depwarning) {
02460       depwarning = 1;
02461 
02462       ast_log(LOG_WARNING, "AgentCallbackLogin is deprecated and will be removed in a future release.\n");
02463       ast_log(LOG_WARNING, "See doc/queues-with-callback-members.txt for an example of how to achieve\n");
02464       ast_log(LOG_WARNING, "the same functionality using only dialplan logic.\n");
02465    }
02466 }
02467 
02468 /*!
02469  *  Called by the AgentCallbackLogin application (from the dial plan).
02470  * 
02471  * \param chan
02472  * \param data
02473  * \returns
02474  * \sa login_exec(), agentmonitoroutgoing_exec(), load_module().
02475  */
02476 static int callback_exec(struct ast_channel *chan, void *data)
02477 {
02478    callback_deprecated();
02479 
02480    return __login_exec(chan, data, 1);
02481 }
02482 
02483 /*!
02484  * Sets an agent as logged in by callback in the Manager API.
02485  * It is registered on load_module() and it gets called by the manager backend.
02486  * \param s
02487  * \param m
02488  * \returns 
02489  * \sa action_agents(), action_agent_logoff(), load_module().
02490  */
02491 static int action_agent_callback_login(struct mansession *s, const struct message *m)
02492 {
02493    const char *agent = astman_get_header(m, "Agent");
02494    const char *exten = astman_get_header(m, "Exten");
02495    const char *context = astman_get_header(m, "Context");
02496    const char *wrapuptime_s = astman_get_header(m, "WrapupTime");
02497    const char *ackcall_s = astman_get_header(m, "AckCall");
02498    struct agent_pvt *p;
02499    int login_state = 0;
02500 
02501    callback_deprecated();
02502 
02503    if (ast_strlen_zero(agent)) {
02504       astman_send_error(s, m, "No agent specified");
02505       return 0;
02506    }
02507 
02508    if (ast_strlen_zero(exten)) {
02509       astman_send_error(s, m, "No extension specified");
02510       return 0;
02511    }
02512 
02513    AST_LIST_LOCK(&agents);
02514    AST_LIST_TRAVERSE(&agents, p, list) {
02515       if (strcmp(p->agent, agent) || p->pending) 
02516          continue;
02517       if (p->chan) {
02518          login_state = 2; /* already logged in (and on the phone)*/
02519          break;
02520       }
02521       ast_mutex_lock(&p->lock);
02522       login_state = 1; /* Successful Login */
02523       
02524       if (ast_strlen_zero(context))
02525          ast_copy_string(p->loginchan, exten, sizeof(p->loginchan));
02526       else
02527          snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context);
02528 
02529       if (!ast_strlen_zero(wrapuptime_s)) {
02530          p->wrapuptime = atoi(wrapuptime_s);
02531          if (p->wrapuptime < 0)
02532             p->wrapuptime = 0;
02533       }
02534 
02535       if (!strcasecmp(ackcall_s, "always"))
02536          p->ackcall = 2;
02537       else if (ast_true(ackcall_s))
02538          p->ackcall = 1;
02539       else
02540          p->ackcall = 0;
02541 
02542       if (p->loginstart == 0)
02543          time(&p->loginstart);
02544       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02545                "Agent: %s\r\n"
02546                "Loginchan: %s\r\n",
02547                p->agent, p->loginchan);
02548       ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02549       if (option_verbose > 1)
02550          ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02551       ast_device_state_changed("Agent/%s", p->agent);
02552       ast_mutex_unlock(&p->lock);
02553       if (persistent_agents)
02554          dump_agents();
02555    }
02556    AST_LIST_UNLOCK(&agents);
02557 
02558    if (login_state == 1)
02559       astman_send_ack(s, m, "Agent logged in");
02560    else if (login_state == 0)
02561       astman_send_error(s, m, "No such agent");
02562    else if (login_state == 2)
02563       astman_send_error(s, m, "Agent already logged in");
02564 
02565    return 0;
02566 }
02567 
02568 /*!
02569  *  \brief Called by the AgentMonitorOutgoing application (from the dial plan).
02570  *
02571  * \param chan
02572  * \param data
02573  * \returns
02574  * \sa login_exec(), callback_login_exec(), load_module().
02575  */
02576 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
02577 {
02578    int exitifnoagentid = 0;
02579    int nowarnings = 0;
02580    int changeoutgoing = 0;
02581    int res = 0;
02582    char agent[AST_MAX_AGENT];
02583 
02584    if (data) {
02585       if (strchr(data, 'd'))
02586          exitifnoagentid = 1;
02587       if (strchr(data, 'n'))
02588          nowarnings = 1;
02589       if (strchr(data, 'c'))
02590          changeoutgoing = 1;
02591    }
02592    if (chan->cid.cid_num) {
02593       const char *tmp;
02594       char agentvar[AST_MAX_BUF];
02595       snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
02596       if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02597          struct agent_pvt *p;
02598          ast_copy_string(agent, tmp, sizeof(agent));
02599          AST_LIST_LOCK(&agents);
02600          AST_LIST_TRAVERSE(&agents, p, list) {
02601             if (!strcasecmp(p->agent, tmp)) {
02602                if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02603                __agent_start_monitoring(chan, p, 1);
02604                break;
02605             }
02606          }
02607          AST_LIST_UNLOCK(&agents);
02608          
02609       } else {
02610          res = -1;
02611          if (!nowarnings)
02612             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);
02613       }
02614    } else {
02615       res = -1;
02616       if (!nowarnings)
02617          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");
02618    }
02619    /* check if there is n + 101 priority */
02620    /*! \todo XXX Needs to check option priorityjump etc etc */
02621    if (res) {
02622       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
02623          chan->priority+=100;
02624          if (option_verbose > 2)
02625             ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
02626       } else if (exitifnoagentid)
02627          return res;
02628    }
02629    return 0;
02630 }
02631 
02632 /*!
02633  * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
02634  */
02635 static void dump_agents(void)
02636 {
02637    struct agent_pvt *cur_agent = NULL;
02638    char buf[256];
02639 
02640    AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02641       if (cur_agent->chan)
02642          continue;
02643 
02644       if (!ast_strlen_zero(cur_agent->loginchan)) {
02645          snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
02646          if (ast_db_put(pa_family, cur_agent->agent, buf))
02647             ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
02648          else if (option_debug)
02649             ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
02650       } else {
02651          /* Delete -  no agent or there is an error */
02652          ast_db_del(pa_family, cur_agent->agent);
02653       }
02654    }
02655 }
02656 
02657 /*!
02658  * \brief Reload the persistent agents from astdb.
02659  */
02660 static void reload_agents(void)
02661 {
02662    char *agent_num;
02663    struct ast_db_entry *db_tree;
02664    struct ast_db_entry *entry;
02665    struct agent_pvt *cur_agent;
02666    char agent_data[256];
02667    char *parse;
02668    char *agent_chan;
02669    char *agent_callerid;
02670 
02671    db_tree = ast_db_gettree(pa_family, NULL);
02672 
02673    AST_LIST_LOCK(&agents);
02674    for (entry = db_tree; entry; entry = entry->next) {
02675       agent_num = entry->key + strlen(pa_family) + 2;
02676       AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02677          ast_mutex_lock(&cur_agent->lock);
02678          if (strcmp(agent_num, cur_agent->agent) == 0)
02679             break;
02680          ast_mutex_unlock(&cur_agent->lock);
02681       }
02682       if (!cur_agent) {
02683          ast_db_del(pa_family, agent_num);
02684          continue;
02685       } else
02686          ast_mutex_unlock(&cur_agent->lock);
02687       if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
02688          if (option_debug)
02689             ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
02690          parse = agent_data;
02691          agent_chan = strsep(&parse, ";");
02692          agent_callerid = strsep(&parse, ";");
02693          ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
02694          if (agent_callerid) {
02695             ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
02696             set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
02697          } else
02698             cur_agent->logincallerid[0] = '\0';
02699          if (cur_agent->loginstart == 0)
02700             time(&cur_agent->loginstart);
02701          ast_device_state_changed("Agent/%s", cur_agent->agent);  
02702       }
02703    }
02704    AST_LIST_UNLOCK(&agents);
02705    if (db_tree) {
02706       ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
02707       ast_db_freetree(db_tree);
02708    }
02709 }
02710 
02711 /*! \brief Part of PBX channel interface */
02712 static int agent_devicestate(void *data)
02713 {
02714    struct agent_pvt *p;
02715    char *s;
02716    ast_group_t groupmatch;
02717    int groupoff;
02718    int waitforagent=0;
02719    int res = AST_DEVICE_INVALID;
02720 
02721    s = data;
02722    if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
02723       char tmp[12];
02724       sprintf(tmp, "%d", groupoff);
02725       ast_get_group(&groupmatch, tmp);
02726    } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
02727       char tmp[12];
02728       sprintf(tmp, "%d", groupoff);
02729       ast_get_group(&groupmatch, tmp);
02730       waitforagent = 1;
02731    } else {
02732       memset(&groupmatch, 0, sizeof(groupmatch));
02733    }
02734 
02735    /* Check actual logged in agents first */
02736    AST_LIST_LOCK(&agents);
02737    AST_LIST_TRAVERSE(&agents, p, list) {
02738       ast_mutex_lock(&p->lock);
02739       if (!p->pending && ((!ast_is_empty_group(&groupmatch) && ast_have_common_group(&p->group, &groupmatch)) || !strcmp(data, p->agent))) {
02740          if (p->owner) {
02741             if (res != AST_DEVICE_INUSE)
02742                res = AST_DEVICE_BUSY;
02743          } else if (p->inherited_devicestate > -1) {
02744             res = p->inherited_devicestate;
02745          } else {
02746             if (res == AST_DEVICE_BUSY)
02747                res = AST_DEVICE_INUSE;
02748             if (p->chan || !ast_strlen_zero(p->loginchan)) {
02749                if (res == AST_DEVICE_INVALID)
02750                   res = AST_DEVICE_UNKNOWN;
02751             } else if (res == AST_DEVICE_INVALID)  
02752                res = AST_DEVICE_UNAVAILABLE;
02753          }
02754          if (!strcmp(data, p->agent)) {
02755             ast_mutex_unlock(&p->lock);
02756             break;
02757          }
02758       }
02759       ast_mutex_unlock(&p->lock);
02760    }
02761    AST_LIST_UNLOCK(&agents);
02762    return res;
02763 }
02764 
02765 /*!
02766  * \note This function expects the agent list to be locked
02767  */
02768 static struct agent_pvt *find_agent(char *agentid)
02769 {
02770    struct agent_pvt *cur;
02771 
02772    AST_LIST_TRAVERSE(&agents, cur, list) {
02773       if (!strcmp(cur->agent, agentid))
02774          break;   
02775    }
02776 
02777    return cur; 
02778 }
02779 
02780 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
02781 {
02782    char *parse;    
02783    AST_DECLARE_APP_ARGS(args,
02784       AST_APP_ARG(agentid);
02785       AST_APP_ARG(item);
02786    );
02787    char *tmp;
02788    struct agent_pvt *agent;
02789 
02790    buf[0] = '\0';
02791 
02792    if (ast_strlen_zero(data)) {
02793       ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02794       return -1;
02795    }
02796 
02797    parse = ast_strdupa(data);
02798 
02799    AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02800    if (!args.item)
02801       args.item = "status";
02802 
02803    AST_LIST_LOCK(&agents);
02804 
02805    if (!(agent = find_agent(args.agentid))) {
02806       AST_LIST_UNLOCK(&agents);
02807       ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02808       return -1;
02809    }
02810 
02811    if (!strcasecmp(args.item, "status")) {
02812       char *status = "LOGGEDOUT";
02813       if (agent->chan || !ast_strlen_zero(agent->loginchan)) 
02814          status = "LOGGEDIN"; 
02815       ast_copy_string(buf, status, len);
02816    } else if (!strcasecmp(args.item, "password")) 
02817       ast_copy_string(buf, agent->password, len);
02818    else if (!strcasecmp(args.item, "name"))
02819       ast_copy_string(buf, agent->name, len);
02820    else if (!strcasecmp(args.item, "mohclass"))
02821       ast_copy_string(buf, agent->moh, len);
02822    else if (!strcasecmp(args.item, "channel")) {
02823       if (agent->chan) {
02824          ast_channel_lock(agent->chan);
02825          ast_copy_string(buf, agent->chan->name, len);
02826          ast_channel_unlock(agent->chan);
02827          tmp = strrchr(buf, '-');
02828          if (tmp)
02829             *tmp = '\0';
02830       } 
02831    } else if (!strcasecmp(args.item, "exten"))
02832       ast_copy_string(buf, agent->loginchan, len); 
02833 
02834    AST_LIST_UNLOCK(&agents);
02835 
02836    return 0;
02837 }
02838 
02839 struct ast_custom_function agent_function = {
02840    .name = "AGENT",
02841    .synopsis = "Gets information about an Agent",
02842    .syntax = "AGENT(<agentid>[:item])",
02843    .read = function_agent,
02844    .desc = "The valid items to retrieve are:\n"
02845    "- status (default)      The status of the agent\n"
02846    "                          LOGGEDIN | LOGGEDOUT\n"
02847    "- password              The password of the agent\n"
02848    "- name                  The name of the agent\n"
02849    "- mohclass              MusicOnHold class\n"
02850    "- exten                 The callback extension for the Agent (AgentCallbackLogin)\n"
02851    "- channel               The name of the active channel for the Agent (AgentLogin)\n"
02852 };
02853 
02854 
02855 /*!
02856  * \brief Initialize the Agents module.
02857  * This function is being called by Asterisk when loading the module. 
02858  * Among other things it registers applications, cli commands and reads the cofiguration file.
02859  *
02860  * \returns int Always 0.
02861  */
02862 static int load_module(void)
02863 {
02864    /* Make sure we can register our agent channel type */
02865    if (ast_channel_register(&agent_tech)) {
02866       ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
02867       return -1;
02868    }
02869    /* Read in the config */
02870    if (!read_agent_config())
02871       return AST_MODULE_LOAD_DECLINE;
02872    if (persistent_agents)
02873       reload_agents();
02874    /* Dialplan applications */
02875    ast_register_application(app, login_exec, synopsis, descrip);
02876    ast_register_application(app2, callback_exec, synopsis2, descrip2);
02877    ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
02878 
02879    /* Manager commands */
02880    ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
02881    ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
02882    ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
02883 
02884    /* CLI Commands */
02885    ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02886 
02887    /* Dialplan Functions */
02888    ast_custom_function_register(&agent_function);
02889 
02890    ast_devstate_add(agent_devicestate_cb, NULL);
02891 
02892    return 0;
02893 }
02894 
02895 static int reload(void)
02896 {
02897    read_agent_config();
02898    if (persistent_agents)
02899       reload_agents();
02900    return 0;
02901 }
02902 
02903 static int unload_module(void)
02904 {
02905    struct agent_pvt *p;
02906    /* First, take us out of the channel loop */
02907    ast_channel_unregister(&agent_tech);
02908    /* Delete devicestate subscription */
02909    ast_devstate_del(agent_devicestate_cb, NULL);
02910    /* Unregister dialplan functions */
02911    ast_custom_function_unregister(&agent_function);   
02912    /* Unregister CLI commands */
02913    ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02914    /* Unregister dialplan applications */
02915    ast_unregister_application(app);
02916    ast_unregister_application(app2);
02917    ast_unregister_application(app3);
02918    /* Unregister manager command */
02919    ast_manager_unregister("Agents");
02920    ast_manager_unregister("AgentLogoff");
02921    ast_manager_unregister("AgentCallbackLogin");
02922    /* Unregister channel */
02923    AST_LIST_LOCK(&agents);
02924    /* Hangup all interfaces if they have an owner */
02925    while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
02926       if (p->owner)
02927          ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02928       free(p);
02929    }
02930    AST_LIST_UNLOCK(&agents);
02931    return 0;
02932 }
02933 
02934 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
02935       .load = load_module,
02936       .unload = unload_module,
02937       .reload = reload,
02938           );

Generated on 31 Jan 2014 for Asterisk - the Open Source PBX by  doxygen 1.6.1