Wed Nov 28 2012 04:18:04

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