Mon Nov 24 15:34:10 2008

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

Generated on Mon Nov 24 15:34:10 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7