Sun Oct 16 2011 08:41:32

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