Sat Apr 26 2014 22:01:30

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