Tue Aug 20 16:34:25 2013

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: 380364 $")
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->nativeformats != p->chan->nativeformats) { \
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->nativeformats), ast_getformatname_multiple(tmp2, sizeof(tmp2), p->chan->nativeformats)); \
00301          /* Native formats changed, reset things */ \
00302          ast->nativeformats = p->chan->nativeformats; \
00303          ast_debug(1, "Resetting read to '%s' and write to '%s'\n", ast_getformatname_multiple(tmp1, sizeof(tmp1), ast->readformat), ast_getformatname_multiple(tmp2, sizeof(tmp2), ast->writeformat));\
00304          ast_set_read_format(ast, ast->readformat); \
00305          ast_set_write_format(ast, ast->writeformat); \
00306       } \
00307       if (p->chan->readformat != ast->rawreadformat && !p->chan->generator)  \
00308          ast_set_read_format(p->chan, ast->rawreadformat); \
00309       if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
00310          ast_set_write_format(p->chan, ast->rawwriteformat); \
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, p->chan->fds[x]); \
00324       } \
00325       ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \
00326    } \
00327 } while(0)
00328 
00329 /*--- Forward declarations */
00330 static struct ast_channel *agent_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
00331 static int agent_devicestate(void *data);
00332 static int agent_digit_begin(struct ast_channel *ast, char digit);
00333 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00334 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
00335 static int agent_hangup(struct ast_channel *ast);
00336 static int agent_answer(struct ast_channel *ast);
00337 static struct ast_frame *agent_read(struct ast_channel *ast);
00338 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
00339 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00340 static int agent_sendtext(struct ast_channel *ast, const char *text);
00341 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00342 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00343 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00344 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
00345 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
00346 static int agent_logoff(const char *agent, int soft);
00347 
00348 /*! \brief Channel interface description for PBX integration */
00349 static const struct ast_channel_tech agent_tech = {
00350    .type = "Agent",
00351    .description = tdesc,
00352    .capabilities = -1,
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       chan->tech_pvt = 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->monitor) {
00582       snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
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->cdr)
00594          ast->cdr = 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->tech_pvt, needlock);
00605 }
00606 
00607 static struct ast_frame *agent_read(struct ast_channel *ast)
00608 {
00609    struct agent_pvt *p = ast->tech_pvt;
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(p->chan, ast, AST_FLAG_EXCEPTION);
00624       p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
00625       f = ast_read(p->chan);
00626       ast->fdno = -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 && (p->chan->_state == 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", p->chan->name, 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", p->chan->name);
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 && !p->chan->_bridge) {
00715       if (strcasecmp(p->chan->tech->type, "Local")) {
00716          p->chan->_bridge = ast;
00717          ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
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->tech_pvt;
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->tech_pvt;
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->tech_pvt;
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           (f->subclass.codec == p->chan->writeformat)) {
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->name, p->chan->name);
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 = newchan->tech_pvt;
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->tech_pvt;
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 = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
00797       ast_channel_unlock(p->chan);
00798       ast_mutex_unlock(&p->lock);
00799       ast_channel_lock(ast);
00800    } else {
00801       ast_mutex_unlock(&p->lock);
00802       res = 0;
00803    }
00804    return res;
00805 }
00806 
00807 static int agent_digit_begin(struct ast_channel *ast, char digit)
00808 {
00809    struct agent_pvt *p = ast->tech_pvt;
00810    ast_mutex_lock(&p->lock);
00811    if (p->chan) {
00812       ast_senddigit_begin(p->chan, digit);
00813    }
00814    ast_mutex_unlock(&p->lock);
00815    return 0;
00816 }
00817 
00818 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00819 {
00820    struct agent_pvt *p = ast->tech_pvt;
00821    ast_mutex_lock(&p->lock);
00822    if (p->chan) {
00823       ast_senddigit_end(p->chan, digit, duration);
00824    }
00825    ast_mutex_unlock(&p->lock);
00826    return 0;
00827 }
00828 
00829 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
00830 {
00831    struct agent_pvt *p = ast->tech_pvt;
00832    int res;
00833    int newstate=0;
00834 
00835    ast_mutex_lock(&p->lock);
00836    p->acknowledged = 0;
00837 
00838    if (p->pending) {
00839       ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
00840       ast_mutex_unlock(&p->lock);
00841       ast_setstate(ast, AST_STATE_DIALING);
00842       return 0;
00843    }
00844 
00845    ast_assert(p->chan != NULL);
00846    ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
00847    ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
00848 
00849    ast_mutex_unlock(&p->lock);
00850 
00851    res = ast_streamfile(p->chan, beep, p->chan->language);
00852    ast_debug(3, "Played beep, result '%d'\n", res);
00853    if (!res) {
00854       res = ast_waitstream(p->chan, "");
00855       ast_debug(3, "Waited for stream, result '%d'\n", res);
00856    }
00857    
00858    ast_mutex_lock(&p->lock);
00859 
00860    if (!res) {
00861       res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
00862       ast_debug(3, "Set read format, result '%d'\n", res);
00863       if (res)
00864          ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00865    }
00866 
00867    if (!res) {
00868       res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
00869       ast_debug(3, "Set write format, result '%d'\n", res);
00870       if (res)
00871          ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00872    }
00873    if(!res) {
00874       /* Call is immediately up, or might need ack */
00875       if (p->ackcall) {
00876          newstate = AST_STATE_RINGING;
00877       } else {
00878          newstate = AST_STATE_UP;
00879          if (recordagentcalls)
00880             agent_start_monitoring(ast, 0);
00881          p->acknowledged = 1;
00882       }
00883    }
00884    CLEANUP(ast, p);
00885    ast_mutex_unlock(&p->lock);
00886    if (newstate)
00887       ast_setstate(ast, newstate);
00888    return res ? -1 : 0;
00889 }
00890 
00891 /*! \brief return the channel or base channel if one exists.  This function assumes the channel it is called on is already locked */
00892 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
00893 {
00894    struct agent_pvt *p;
00895    struct ast_channel *base = chan;
00896 
00897    /* chan is locked by the calling function */
00898    if (!chan || !chan->tech_pvt) {
00899       ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL);
00900       return NULL;
00901    }
00902    p = chan->tech_pvt;
00903    if (p->chan) 
00904       base = p->chan;
00905    return base;
00906 }
00907 
00908 static int agent_hangup(struct ast_channel *ast)
00909 {
00910    struct agent_pvt *p = ast->tech_pvt;
00911    struct ast_channel *indicate_chan = NULL;
00912    char *tmp_moh; /* moh buffer for indicating after unlocking p */
00913 
00914    if (p->pending) {
00915       AST_LIST_LOCK(&agents);
00916       AST_LIST_REMOVE(&agents, p, list);
00917       AST_LIST_UNLOCK(&agents);
00918    }
00919 
00920    ast_mutex_lock(&p->lock);
00921    p->owner = NULL;
00922    ast->tech_pvt = NULL;
00923    p->acknowledged = 0;
00924 
00925    /* if they really are hung up then set start to 0 so the test
00926     * later if we're called on an already downed channel
00927     * doesn't cause an agent to be logged out like when
00928     * agent_request() is followed immediately by agent_hangup()
00929     * as in apps/app_chanisavail.c:chanavail_exec()
00930     */
00931 
00932    ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
00933    p->start = 0;
00934    if (p->chan) {
00935       p->chan->_bridge = NULL;
00936       /* If they're dead, go ahead and hang up on the agent now */
00937       if (p->dead) {
00938          ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00939       } else if (p->loginstart) {
00940          indicate_chan = ast_channel_ref(p->chan);
00941          tmp_moh = ast_strdupa(p->moh);
00942       }
00943    }
00944    ast_mutex_unlock(&p->lock);
00945 
00946    if (indicate_chan) {
00947       ast_indicate_data(indicate_chan, AST_CONTROL_HOLD,
00948          S_OR(tmp_moh, NULL),
00949          !ast_strlen_zero(tmp_moh) ? strlen(tmp_moh) + 1 : 0);
00950       indicate_chan = ast_channel_unref(indicate_chan);
00951    }
00952 
00953    ast_mutex_lock(&p->lock);
00954    if (p->abouttograb) {
00955       /* Let the "about to grab" thread know this isn't valid anymore, and let it
00956          kill it later */
00957       p->abouttograb = 0;
00958    } else if (p->dead) {
00959       ast_mutex_unlock(&p->lock);
00960       agent_pvt_destroy(p);
00961       return 0;
00962    } else {
00963       /* Store last disconnect time */
00964       p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00965    }
00966 
00967    /* Release ownership of the agent to other threads (presumably running the login app). */
00968    p->app_sleep_cond = 1;
00969    p->app_lock_flag = 0;
00970    ast_cond_signal(&p->app_complete_cond);
00971 
00972    ast_mutex_unlock(&p->lock);
00973    return 0;
00974 }
00975 
00976 static int agent_cont_sleep(void *data)
00977 {
00978    struct agent_pvt *p;
00979    int res;
00980 
00981    p = (struct agent_pvt *) data;
00982 
00983    ast_mutex_lock(&p->lock);
00984    res = p->app_sleep_cond;
00985    if (res && p->lastdisc.tv_sec) {
00986       if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
00987          res = 0;
00988       }
00989    }
00990    ast_mutex_unlock(&p->lock);
00991 
00992    if (!res) {
00993       ast_debug(5, "agent_cont_sleep() returning %d\n", res);
00994    }
00995 
00996    return res;
00997 }
00998 
00999 static int agent_ack_sleep(struct agent_pvt *p)
01000 {
01001    int digit;
01002    int to = 1000;
01003    struct ast_frame *f;
01004    struct timeval start = ast_tvnow();
01005    int ms;
01006 
01007    /* Wait a second and look for something */
01008    while ((ms = ast_remaining_ms(start, to))) {
01009       ms = ast_waitfor(p->chan, ms);
01010       if (ms < 0) {
01011          return -1;
01012       }
01013       if (ms == 0) {
01014          return 0;
01015       }
01016       f = ast_read(p->chan);
01017       if (!f) {
01018          return -1;
01019       }
01020       if (f->frametype == AST_FRAME_DTMF) {
01021          digit = f->subclass.integer;
01022       } else {
01023          digit = 0;
01024       }
01025       ast_frfree(f);
01026       ast_mutex_lock(&p->lock);
01027       if (!p->app_sleep_cond) {
01028          ast_mutex_unlock(&p->lock);
01029          return 0;
01030       }
01031       if (digit == p->acceptdtmf) {
01032          ast_mutex_unlock(&p->lock);
01033          return 1;
01034       }
01035       if (p->lastdisc.tv_sec) {
01036          if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
01037             ast_mutex_unlock(&p->lock);
01038             return 0;
01039          }
01040       }
01041       ast_mutex_unlock(&p->lock);
01042    }
01043    return 0;
01044 }
01045 
01046 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
01047 {
01048    struct agent_pvt *p = bridge->tech_pvt;
01049    struct ast_channel *ret = NULL;
01050 
01051    if (p) {
01052       if (chan == p->chan)
01053          ret = bridge->_bridge;
01054       else if (chan == bridge->_bridge)
01055          ret = p->chan;
01056    }
01057 
01058    ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
01059    return ret;
01060 }
01061 
01062 /*! \brief Create new agent channel */
01063 static struct ast_channel *agent_new(struct agent_pvt *p, int state, const char *linkedid)
01064 {
01065    struct ast_channel *tmp;
01066 #if 0
01067    if (!p->chan) {
01068       ast_log(LOG_WARNING, "No channel? :(\n");
01069       return NULL;
01070    }
01071 #endif   
01072    if (p->pending)
01073       tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff);
01074    else
01075       tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/%s", p->agent);
01076    if (!tmp) {
01077       ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
01078       return NULL;
01079    }
01080 
01081    tmp->tech = &agent_tech;
01082    if (p->chan) {
01083       tmp->nativeformats = p->chan->nativeformats;
01084       tmp->writeformat = p->chan->writeformat;
01085       tmp->rawwriteformat = p->chan->writeformat;
01086       tmp->readformat = p->chan->readformat;
01087       tmp->rawreadformat = p->chan->readformat;
01088       ast_string_field_set(tmp, language, p->chan->language);
01089       ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
01090       ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
01091       /* XXX Is this really all we copy form the originating channel?? */
01092    } else {
01093       tmp->nativeformats = AST_FORMAT_SLINEAR;
01094       tmp->writeformat = AST_FORMAT_SLINEAR;
01095       tmp->rawwriteformat = AST_FORMAT_SLINEAR;
01096       tmp->readformat = AST_FORMAT_SLINEAR;
01097       tmp->rawreadformat = AST_FORMAT_SLINEAR;
01098    }
01099    /* Safe, agentlock already held */
01100    tmp->tech_pvt = p;
01101    p->owner = tmp;
01102    tmp->priority = 1;
01103    return tmp;
01104 }
01105 
01106 
01107 /*!
01108  * Read configuration data. The file named agents.conf.
01109  *
01110  * \returns Always 0, or so it seems.
01111  */
01112 static int read_agent_config(int reload)
01113 {
01114    struct ast_config *cfg;
01115    struct ast_config *ucfg;
01116    struct ast_variable *v;
01117    struct agent_pvt *p;
01118    const char *catname;
01119    const char *hasagent;
01120    int genhasagent;
01121    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01122 
01123    group = 0;
01124    autologoff = 0;
01125    wrapuptime = 0;
01126    ackcall = 0;
01127    endcall = 1;
01128    cfg = ast_config_load(config, config_flags);
01129    if (!cfg) {
01130       ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
01131       return 0;
01132    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01133       return -1;
01134    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01135       ast_log(LOG_ERROR, "%s contains a parsing error.  Aborting\n", config);
01136       return 0;
01137    }
01138    if ((ucfg = ast_config_load("users.conf", config_flags))) {
01139       if (ucfg == CONFIG_STATUS_FILEUNCHANGED) {
01140          ucfg = NULL;
01141       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
01142          ast_log(LOG_ERROR, "users.conf contains a parsing error.  Aborting\n");
01143          return 0;
01144       }
01145    }
01146 
01147    AST_LIST_LOCK(&agents);
01148    AST_LIST_TRAVERSE(&agents, p, list) {
01149       p->dead = 1;
01150    }
01151    strcpy(moh, "default");
01152    /* set the default recording values */
01153    recordagentcalls = 0;
01154    strcpy(recordformat, "wav");
01155    strcpy(recordformatext, "wav");
01156    urlprefix[0] = '\0';
01157    savecallsin[0] = '\0';
01158 
01159    /* Read in the [agents] section */
01160    v = ast_variable_browse(cfg, "agents");
01161    while(v) {
01162       /* Create the interface list */
01163       if (!strcasecmp(v->name, "agent")) {
01164          add_agent(v->value, 0);
01165       } else if (!strcasecmp(v->name, "group")) {
01166          group = ast_get_group(v->value);
01167       } else if (!strcasecmp(v->name, "autologoff")) {
01168          autologoff = atoi(v->value);
01169          if (autologoff < 0)
01170             autologoff = 0;
01171       } else if (!strcasecmp(v->name, "ackcall")) {
01172          if (ast_true(v->value) || !strcasecmp(v->value, "always")) {
01173             ackcall = 1;
01174          }
01175       } else if (!strcasecmp(v->name, "endcall")) {
01176          endcall = ast_true(v->value);
01177       } else if (!strcasecmp(v->name, "acceptdtmf")) {
01178          acceptdtmf = *(v->value);
01179          ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf);
01180       } else if (!strcasecmp(v->name, "enddtmf")) {
01181          enddtmf = *(v->value);
01182       } else if (!strcasecmp(v->name, "wrapuptime")) {
01183          wrapuptime = atoi(v->value);
01184          if (wrapuptime < 0)
01185             wrapuptime = 0;
01186       } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
01187          maxlogintries = atoi(v->value);
01188          if (maxlogintries < 0)
01189             maxlogintries = 0;
01190       } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
01191          strcpy(agentgoodbye,v->value);
01192       } else if (!strcasecmp(v->name, "musiconhold")) {
01193          ast_copy_string(moh, v->value, sizeof(moh));
01194       } else if (!strcasecmp(v->name, "updatecdr")) {
01195          if (ast_true(v->value))
01196             updatecdr = 1;
01197          else
01198             updatecdr = 0;
01199       } else if (!strcasecmp(v->name, "autologoffunavail")) {
01200          if (ast_true(v->value))
01201             autologoffunavail = 1;
01202          else
01203             autologoffunavail = 0;
01204       } else if (!strcasecmp(v->name, "recordagentcalls")) {
01205          recordagentcalls = ast_true(v->value);
01206       } else if (!strcasecmp(v->name, "recordformat")) {
01207          ast_copy_string(recordformat, v->value, sizeof(recordformat));
01208          if (!strcasecmp(v->value, "wav49"))
01209             strcpy(recordformatext, "WAV");
01210          else
01211             ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
01212       } else if (!strcasecmp(v->name, "urlprefix")) {
01213          ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
01214          if (urlprefix[strlen(urlprefix) - 1] != '/')
01215             strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
01216       } else if (!strcasecmp(v->name, "savecallsin")) {
01217          if (v->value[0] == '/')
01218             ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
01219          else
01220             snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
01221          if (savecallsin[strlen(savecallsin) - 1] != '/')
01222             strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
01223       } else if (!strcasecmp(v->name, "custom_beep")) {
01224          ast_copy_string(beep, v->value, sizeof(beep));
01225       }
01226       v = v->next;
01227    }
01228    if (ucfg) {
01229       genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
01230       catname = ast_category_browse(ucfg, NULL);
01231       while(catname) {
01232          if (strcasecmp(catname, "general")) {
01233             hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
01234             if (ast_true(hasagent) || (!hasagent && genhasagent)) {
01235                char tmp[256];
01236                const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
01237                const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
01238                if (!fullname)
01239                   fullname = "";
01240                if (!secret)
01241                   secret = "";
01242                snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
01243                add_agent(tmp, 0);
01244             }
01245          }
01246          catname = ast_category_browse(ucfg, catname);
01247       }
01248       ast_config_destroy(ucfg);
01249    }
01250    AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
01251       if (p->dead) {
01252          AST_LIST_REMOVE_CURRENT(list);
01253          /* Destroy if  appropriate */
01254          if (!p->owner) {
01255             if (!p->chan) {
01256                agent_pvt_destroy(p);
01257             } else {
01258                /* Cause them to hang up */
01259                ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01260             }
01261          }
01262       }
01263    }
01264    AST_LIST_TRAVERSE_SAFE_END;
01265    AST_LIST_UNLOCK(&agents);
01266    ast_config_destroy(cfg);
01267    return 1;
01268 }
01269 
01270 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
01271 {
01272    struct ast_channel *chan=NULL, *parent=NULL;
01273    struct agent_pvt *p;
01274    int res;
01275 
01276    ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
01277    if (needlock)
01278       AST_LIST_LOCK(&agents);
01279    AST_LIST_TRAVERSE(&agents, p, list) {
01280       if (p == newlyavailable) {
01281          continue;
01282       }
01283       ast_mutex_lock(&p->lock);
01284       if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01285          ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01286          /* We found a pending call, time to merge */
01287          chan = agent_new(newlyavailable, AST_STATE_DOWN, p->owner ? p->owner->linkedid : NULL);
01288          parent = p->owner;
01289          p->abouttograb = 1;
01290          ast_mutex_unlock(&p->lock);
01291          break;
01292       }
01293       ast_mutex_unlock(&p->lock);
01294    }
01295    if (needlock)
01296       AST_LIST_UNLOCK(&agents);
01297    if (parent && chan)  {
01298       if (newlyavailable->ackcall) {
01299          /* Don't do beep here */
01300          res = 0;
01301       } else {
01302          ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01303          res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01304          ast_debug(3, "Played beep, result '%d'\n", res);
01305          if (!res) {
01306             res = ast_waitstream(newlyavailable->chan, "");
01307             ast_debug(1, "Waited for stream, result '%d'\n", res);
01308          }
01309       }
01310       if (!res) {
01311          /* Note -- parent may have disappeared */
01312          if (p->abouttograb) {
01313             newlyavailable->acknowledged = 1;
01314             /* Safe -- agent lock already held */
01315             ast_setstate(parent, AST_STATE_UP);
01316             ast_setstate(chan, AST_STATE_UP);
01317             ast_copy_string(parent->context, chan->context, sizeof(parent->context));
01318             ast_channel_masquerade(parent, chan);
01319             ast_hangup(chan);
01320             p->abouttograb = 0;
01321          } else {
01322             ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
01323             agent_cleanup(newlyavailable);
01324          }
01325       } else {
01326          ast_debug(1, "Ugh...  Agent hung up at exactly the wrong time\n");
01327          agent_cleanup(newlyavailable);
01328       }
01329    }
01330    return 0;
01331 }
01332 
01333 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
01334 {
01335    struct agent_pvt *p;
01336    int res=0;
01337 
01338    ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
01339    if (needlock)
01340       AST_LIST_LOCK(&agents);
01341    AST_LIST_TRAVERSE(&agents, p, list) {
01342       if (p == newlyavailable) {
01343          continue;
01344       }
01345       ast_mutex_lock(&p->lock);
01346       if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01347          ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01348          ast_mutex_unlock(&p->lock);
01349          break;
01350       }
01351       ast_mutex_unlock(&p->lock);
01352    }
01353    if (needlock)
01354       AST_LIST_UNLOCK(&agents);
01355    if (p) {
01356       ast_mutex_unlock(&newlyavailable->lock);
01357       ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01358       res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01359       ast_debug(1, "Played beep, result '%d'\n", res);
01360       if (!res) {
01361          res = ast_waitstream(newlyavailable->chan, "");
01362          ast_debug(1, "Waited for stream, result '%d'\n", res);
01363       }
01364       ast_mutex_lock(&newlyavailable->lock);
01365    }
01366    return res;
01367 }
01368 
01369 /*! \brief Part of the Asterisk PBX interface */
01370 static struct ast_channel *agent_request(const char *type, format_t format, const struct ast_channel* requestor, void *data, int *cause)
01371 {
01372    struct agent_pvt *p;
01373    struct ast_channel *chan = NULL;
01374    char *s;
01375    ast_group_t groupmatch;
01376    int groupoff;
01377    int waitforagent=0;
01378    int hasagent = 0;
01379    struct timeval now;
01380 
01381    s = data;
01382    if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01383       groupmatch = (1 << groupoff);
01384    } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01385       groupmatch = (1 << groupoff);
01386       waitforagent = 1;
01387    } else 
01388       groupmatch = 0;
01389 
01390    /* Check actual logged in agents first */
01391    AST_LIST_LOCK(&agents);
01392    AST_LIST_TRAVERSE(&agents, p, list) {
01393       ast_mutex_lock(&p->lock);
01394       if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
01395          if (p->chan) {
01396             hasagent++;
01397          }
01398          now = ast_tvnow();
01399          if (p->loginstart
01400             && (!p->lastdisc.tv_sec || ast_tvdiff_ms(now, p->lastdisc) > 0)) {
01401             p->lastdisc = ast_tv(0, 0);
01402             /* Agent must be registered, but not have any active call, and not be in a waiting state */
01403             if (!p->owner && p->chan) {
01404                /* Fixed agent */
01405                chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
01406             }
01407             if (chan) {
01408                ast_mutex_unlock(&p->lock);
01409                break;
01410             }
01411          }
01412       }
01413       ast_mutex_unlock(&p->lock);
01414    }
01415 
01416    if (!chan && waitforagent) {
01417       /* No agent available -- but we're requesting to wait for one.
01418          Allocate a place holder */
01419       if (hasagent) {
01420          ast_debug(1, "Creating place holder for '%s'\n", s);
01421          p = add_agent(data, 1);
01422          if (p) {
01423             p->group = groupmatch;
01424             chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
01425             if (!chan) {
01426                AST_LIST_REMOVE(&agents, p, list);
01427                agent_pvt_destroy(p);
01428             }
01429          }
01430       } else {
01431          ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
01432       }
01433    }
01434    *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
01435    AST_LIST_UNLOCK(&agents);
01436 
01437    if (chan) {
01438       ast_mutex_lock(&p->lock);
01439       if (p->pending) {
01440          ast_mutex_unlock(&p->lock);
01441          return chan;
01442       }
01443 
01444       if (!p->chan) {
01445          ast_debug(1, "Agent disconnected before we could connect the call\n");
01446          ast_mutex_unlock(&p->lock);
01447          ast_hangup(chan);
01448          *cause = AST_CAUSE_UNREGISTERED;
01449          return NULL;
01450       }
01451 
01452       /* we need to take control of the channel from the login app
01453        * thread */
01454       p->app_sleep_cond = 0;
01455       p->app_lock_flag = 1;
01456       ast_queue_frame(p->chan, &ast_null_frame);
01457       ast_cond_wait(&p->login_wait_cond, &p->lock);
01458 
01459       if (!p->chan) {
01460          ast_debug(1, "Agent disconnected while we were connecting the call\n");
01461          ast_mutex_unlock(&p->lock);
01462          ast_hangup(chan);
01463          *cause = AST_CAUSE_UNREGISTERED;
01464          return NULL;
01465       }
01466 
01467       ast_indicate(p->chan, AST_CONTROL_UNHOLD);
01468       ast_mutex_unlock(&p->lock);
01469    }
01470 
01471    return chan;
01472 }
01473 
01474 static force_inline int powerof(unsigned int d)
01475 {
01476    int x = ffs(d);
01477 
01478    if (x)
01479       return x - 1;
01480 
01481    return 0;
01482 }
01483 
01484 /*!
01485  * Lists agents and their status to the Manager API.
01486  * It is registered on load_module() and it gets called by the manager backend.
01487  * This function locks both the pvt and the channel that owns it for a while, but
01488  * does not keep these locks.
01489  * \param s
01490  * \param m
01491  * \returns 
01492  * \sa action_agent_logoff(), load_module().
01493  */
01494 static int action_agents(struct mansession *s, const struct message *m)
01495 {
01496    const char *id = astman_get_header(m,"ActionID");
01497    char idText[256] = "";
01498    struct agent_pvt *p;
01499    char *username = NULL;
01500    char *loginChan = NULL;
01501    char *talkingto = NULL;
01502    char *talkingtoChan = NULL;
01503    char *status = NULL;
01504    struct ast_channel *bridge;
01505 
01506    if (!ast_strlen_zero(id))
01507       snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
01508    astman_send_ack(s, m, "Agents will follow");
01509    AST_LIST_LOCK(&agents);
01510    AST_LIST_TRAVERSE(&agents, p, list) {
01511       struct ast_channel *owner;
01512       ast_mutex_lock(&p->lock);
01513       owner = agent_lock_owner(p);
01514 
01515       /* Status Values:
01516          AGENT_LOGGEDOFF - Agent isn't logged in
01517          AGENT_IDLE      - Agent is logged in, and waiting for call
01518          AGENT_ONCALL    - Agent is logged in, and on a call
01519          AGENT_UNKNOWN   - Don't know anything about agent. Shouldn't ever get this. */
01520 
01521       username = S_OR(p->name, "None");
01522 
01523       /* Set a default status. It 'should' get changed. */
01524       status = "AGENT_UNKNOWN";
01525 
01526       if (p->chan) {
01527          loginChan = ast_strdupa(p->chan->name);
01528          if (owner && owner->_bridge) {
01529             talkingto = S_COR(p->chan->caller.id.number.valid,
01530                p->chan->caller.id.number.str, "n/a");
01531             if ((bridge = ast_bridged_channel(owner))) {
01532                talkingtoChan = ast_strdupa(bridge->name);
01533             } else {
01534                talkingtoChan = "n/a";
01535             }
01536             status = "AGENT_ONCALL";
01537          } else {
01538             talkingto = "n/a";
01539             talkingtoChan = "n/a";
01540             status = "AGENT_IDLE";
01541          }
01542       } else {
01543          loginChan = "n/a";
01544          talkingto = "n/a";
01545          talkingtoChan = "n/a";
01546          status = "AGENT_LOGGEDOFF";
01547       }
01548 
01549       if (owner) {
01550          ast_channel_unlock(owner);
01551          owner = ast_channel_unref(owner);
01552       }
01553 
01554       astman_append(s, "Event: Agents\r\n"
01555          "Agent: %s\r\n"
01556          "Name: %s\r\n"
01557          "Status: %s\r\n"
01558          "LoggedInChan: %s\r\n"
01559          "LoggedInTime: %d\r\n"
01560          "TalkingTo: %s\r\n"
01561          "TalkingToChan: %s\r\n"
01562          "%s"
01563          "\r\n",
01564          p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
01565       ast_mutex_unlock(&p->lock);
01566    }
01567    AST_LIST_UNLOCK(&agents);
01568    astman_append(s, "Event: AgentsComplete\r\n"
01569       "%s"
01570       "\r\n",idText);
01571    return 0;
01572 }
01573 
01574 static int agent_logoff(const char *agent, int soft)
01575 {
01576    struct agent_pvt *p;
01577    int ret = -1; /* Return -1 if no agent if found */
01578 
01579    AST_LIST_LOCK(&agents);
01580    AST_LIST_TRAVERSE(&agents, p, list) {
01581       if (!strcasecmp(p->agent, agent)) {
01582          ret = 0;
01583          if (p->owner || p->chan) {
01584             if (!soft) {
01585                struct ast_channel *owner;
01586                ast_mutex_lock(&p->lock);
01587                owner = agent_lock_owner(p);
01588 
01589                if (owner) {
01590                   ast_softhangup(owner, AST_SOFTHANGUP_EXPLICIT);
01591                   ast_channel_unlock(owner);
01592                   owner = ast_channel_unref(owner);
01593                }
01594 
01595                while (p->chan && ast_channel_trylock(p->chan)) {
01596                   DEADLOCK_AVOIDANCE(&p->lock);
01597                }
01598                if (p->chan) {
01599                   ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01600                   ast_channel_unlock(p->chan);
01601                }
01602 
01603                ast_mutex_unlock(&p->lock);
01604             } else
01605                p->deferlogoff = 1;
01606          }
01607          break;
01608       }
01609    }
01610    AST_LIST_UNLOCK(&agents);
01611 
01612    return ret;
01613 }
01614 
01615 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01616 {
01617    int ret;
01618    const char *agent;
01619 
01620    switch (cmd) {
01621    case CLI_INIT:
01622       e->command = "agent logoff";
01623       e->usage =
01624          "Usage: agent logoff <channel> [soft]\n"
01625          "       Sets an agent as no longer logged in.\n"
01626          "       If 'soft' is specified, do not hangup existing calls.\n";
01627       return NULL;
01628    case CLI_GENERATE:
01629       return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n); 
01630    }
01631 
01632    if (a->argc < 3 || a->argc > 4)
01633       return CLI_SHOWUSAGE;
01634    if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
01635       return CLI_SHOWUSAGE;
01636 
01637    agent = a->argv[2] + 6;
01638    ret = agent_logoff(agent, a->argc == 4);
01639    if (ret == 0)
01640       ast_cli(a->fd, "Logging out %s\n", agent);
01641 
01642    return CLI_SUCCESS;
01643 }
01644 
01645 /*!
01646  * Sets an agent as no longer logged in in the Manager API.
01647  * It is registered on load_module() and it gets called by the manager backend.
01648  * \param s
01649  * \param m
01650  * \returns 
01651  * \sa action_agents(), load_module().
01652  */
01653 static int action_agent_logoff(struct mansession *s, const struct message *m)
01654 {
01655    const char *agent = astman_get_header(m, "Agent");
01656    const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
01657    int soft;
01658    int ret; /* return value of agent_logoff */
01659 
01660    if (ast_strlen_zero(agent)) {
01661       astman_send_error(s, m, "No agent specified");
01662       return 0;
01663    }
01664 
01665    soft = ast_true(soft_s) ? 1 : 0;
01666    ret = agent_logoff(agent, soft);
01667    if (ret == 0)
01668       astman_send_ack(s, m, "Agent logged out");
01669    else
01670       astman_send_error(s, m, "No such agent");
01671 
01672    return 0;
01673 }
01674 
01675 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
01676 {
01677    char *ret = NULL;
01678 
01679    if (pos == 2) {
01680       struct agent_pvt *p;
01681       char name[AST_MAX_AGENT];
01682       int which = 0, len = strlen(word);
01683 
01684       AST_LIST_LOCK(&agents);
01685       AST_LIST_TRAVERSE(&agents, p, list) {
01686          snprintf(name, sizeof(name), "Agent/%s", p->agent);
01687          if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
01688             ret = ast_strdup(name);
01689             break;
01690          }
01691       }
01692       AST_LIST_UNLOCK(&agents);
01693    } else if (pos == 3 && state == 0) 
01694       return ast_strdup("soft");
01695    
01696    return ret;
01697 }
01698 
01699 /*!
01700  * Show agents in cli.
01701  */
01702 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01703 {
01704    struct agent_pvt *p;
01705    char username[AST_MAX_BUF];
01706    char location[AST_MAX_BUF] = "";
01707    char talkingto[AST_MAX_BUF] = "";
01708    char music[AST_MAX_BUF];
01709    int count_agents = 0;      /*!< Number of agents configured */
01710    int online_agents = 0;     /*!< Number of online agents */
01711    int offline_agents = 0;    /*!< Number of offline agents */
01712 
01713    switch (cmd) {
01714    case CLI_INIT:
01715       e->command = "agent show";
01716       e->usage =
01717          "Usage: agent show\n"
01718          "       Provides summary information on agents.\n";
01719       return NULL;
01720    case CLI_GENERATE:
01721       return NULL;
01722    }
01723 
01724    if (a->argc != 2)
01725       return CLI_SHOWUSAGE;
01726 
01727    AST_LIST_LOCK(&agents);
01728    AST_LIST_TRAVERSE(&agents, p, list) {
01729       struct ast_channel *owner;
01730       ast_mutex_lock(&p->lock);
01731       owner = agent_lock_owner(p);
01732       if (p->pending) {
01733          if (p->group)
01734             ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
01735          else
01736             ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
01737       } else {
01738          if (!ast_strlen_zero(p->name))
01739             snprintf(username, sizeof(username), "(%s) ", p->name);
01740          else
01741             username[0] = '\0';
01742          if (p->chan) {
01743             snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01744             if (owner && ast_bridged_channel(owner)) {
01745                snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01746             } else {
01747                strcpy(talkingto, " is idle");
01748             }
01749             online_agents++;
01750          } else {
01751             strcpy(location, "not logged in");
01752             talkingto[0] = '\0';
01753             offline_agents++;
01754          }
01755          if (!ast_strlen_zero(p->moh))
01756             snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
01757          ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, 
01758             username, location, talkingto, music);
01759          count_agents++;
01760       }
01761 
01762       if (owner) {
01763          ast_channel_unlock(owner);
01764          owner = ast_channel_unref(owner);
01765       }
01766       ast_mutex_unlock(&p->lock);
01767    }
01768    AST_LIST_UNLOCK(&agents);
01769    if ( !count_agents ) 
01770       ast_cli(a->fd, "No Agents are configured in %s\n",config);
01771    else 
01772       ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01773    ast_cli(a->fd, "\n");
01774                    
01775    return CLI_SUCCESS;
01776 }
01777 
01778 
01779 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01780 {
01781    struct agent_pvt *p;
01782    char username[AST_MAX_BUF];
01783    char location[AST_MAX_BUF] = "";
01784    char talkingto[AST_MAX_BUF] = "";
01785    char music[AST_MAX_BUF];
01786    int count_agents = 0;           /* Number of agents configured */
01787    int online_agents = 0;          /* Number of online agents */
01788    int agent_status = 0;           /* 0 means offline, 1 means online */
01789 
01790    switch (cmd) {
01791    case CLI_INIT:
01792       e->command = "agent show online";
01793       e->usage =
01794          "Usage: agent show online\n"
01795          "       Provides a list of all online agents.\n";
01796       return NULL;
01797    case CLI_GENERATE:
01798       return NULL;
01799    }
01800 
01801    if (a->argc != 3)
01802       return CLI_SHOWUSAGE;
01803 
01804    AST_LIST_LOCK(&agents);
01805    AST_LIST_TRAVERSE(&agents, p, list) {
01806       struct ast_channel *owner;
01807 
01808       agent_status = 0;       /* reset it to offline */
01809       ast_mutex_lock(&p->lock);
01810       owner = agent_lock_owner(p);
01811 
01812       if (!ast_strlen_zero(p->name))
01813          snprintf(username, sizeof(username), "(%s) ", p->name);
01814       else
01815          username[0] = '\0';
01816       if (p->chan) {
01817          snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01818          if (owner && ast_bridged_channel(owner)) {
01819             snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(owner)->name);
01820          } else {
01821             strcpy(talkingto, " is idle");
01822          }
01823          agent_status = 1;
01824          online_agents++;
01825       }
01826 
01827       if (owner) {
01828          ast_channel_unlock(owner);
01829          owner = ast_channel_unref(owner);
01830       }
01831 
01832       if (!ast_strlen_zero(p->moh))
01833          snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
01834       if (agent_status)
01835          ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, music);
01836       count_agents++;
01837       ast_mutex_unlock(&p->lock);
01838    }
01839    AST_LIST_UNLOCK(&agents);
01840    if (!count_agents) 
01841       ast_cli(a->fd, "No Agents are configured in %s\n", config);
01842    else
01843       ast_cli(a->fd, "%d agents online\n", online_agents);
01844    ast_cli(a->fd, "\n");
01845    return CLI_SUCCESS;
01846 }
01847 
01848 static const char agent_logoff_usage[] =
01849 "Usage: agent logoff <channel> [soft]\n"
01850 "       Sets an agent as no longer logged in.\n"
01851 "       If 'soft' is specified, do not hangup existing calls.\n";
01852 
01853 static struct ast_cli_entry cli_agents[] = {
01854    AST_CLI_DEFINE(agents_show, "Show status of agents"),
01855    AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
01856    AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
01857 };
01858 
01859 /*!
01860  * Called by the AgentLogin application (from the dial plan).
01861  * 
01862  * \brief Log in agent application.
01863  *
01864  * \param chan
01865  * \param data
01866  * \returns
01867  * \sa agentmonitoroutgoing_exec(), load_module().
01868  */
01869 static int login_exec(struct ast_channel *chan, const char *data)
01870 {
01871    int res=0;
01872    int tries = 0;
01873    int max_login_tries = maxlogintries;
01874    struct agent_pvt *p;
01875    char user[AST_MAX_AGENT];
01876    char pass[AST_MAX_AGENT];
01877    char xpass[AST_MAX_AGENT];
01878    char *errmsg;
01879    char *parse;
01880    AST_DECLARE_APP_ARGS(args,
01881               AST_APP_ARG(agent_id);
01882               AST_APP_ARG(options);
01883               AST_APP_ARG(extension);
01884       );
01885    const char *tmpoptions = NULL;
01886    int play_announcement = 1;
01887    char agent_goodbye[AST_MAX_FILENAME_LEN];
01888    int update_cdr = updatecdr;
01889 
01890    user[0] = '\0';
01891    xpass[0] = '\0';
01892 
01893    parse = ast_strdupa(data);
01894 
01895    AST_STANDARD_APP_ARGS(args, parse);
01896 
01897    ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01898 
01899    ast_channel_lock(chan);
01900    /* Set Channel Specific Login Overrides */
01901    if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01902       max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01903       if (max_login_tries < 0)
01904          max_login_tries = 0;
01905       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01906       ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
01907    }
01908    if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01909       if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01910          update_cdr = 1;
01911       else
01912          update_cdr = 0;
01913       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01914       ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
01915    }
01916    if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01917       strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01918       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01919       ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
01920    }
01921    ast_channel_unlock(chan);
01922    /* End Channel Specific Login Overrides */
01923    
01924    if (!ast_strlen_zero(args.options)) {
01925       if (strchr(args.options, 's')) {
01926          play_announcement = 0;
01927       }
01928    }
01929 
01930    if (chan->_state != AST_STATE_UP)
01931       res = ast_answer(chan);
01932    if (!res) {
01933       if (!ast_strlen_zero(args.agent_id))
01934          ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
01935       else
01936          res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
01937    }
01938    while (!res && (max_login_tries==0 || tries < max_login_tries)) {
01939       tries++;
01940       /* Check for password */
01941       AST_LIST_LOCK(&agents);
01942       AST_LIST_TRAVERSE(&agents, p, list) {
01943          if (!strcmp(p->agent, user) && !p->pending)
01944             ast_copy_string(xpass, p->password, sizeof(xpass));
01945       }
01946       AST_LIST_UNLOCK(&agents);
01947       if (!res) {
01948          if (!ast_strlen_zero(xpass))
01949             res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
01950          else
01951             pass[0] = '\0';
01952       }
01953       errmsg = "agent-incorrect";
01954 
01955 #if 0
01956       ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
01957 #endif      
01958 
01959       /* Check again for accuracy */
01960       AST_LIST_LOCK(&agents);
01961       AST_LIST_TRAVERSE(&agents, p, list) {
01962          int unlock_channel = 1;
01963 
01964          ast_channel_lock(chan);
01965          ast_mutex_lock(&p->lock);
01966          if (!strcmp(p->agent, user) &&
01967              !strcmp(p->password, pass) && !p->pending) {
01968 
01969             /* Set Channel Specific Agent Overrides */
01970             if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01971                if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01972                   p->ackcall = 1;
01973                } else {
01974                   p->ackcall = 0;
01975                }
01976                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
01977                ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent);
01978                ast_set_flag(p, AGENT_FLAG_ACKCALL);
01979             } else {
01980                p->ackcall = ackcall;
01981             }
01982             if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
01983                p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
01984                if (p->autologoff < 0)
01985                   p->autologoff = 0;
01986                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
01987                ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent);
01988                ast_set_flag(p, AGENT_FLAG_AUTOLOGOFF);
01989             } else {
01990                p->autologoff = autologoff;
01991             }
01992             if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
01993                p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
01994                if (p->wrapuptime < 0)
01995                   p->wrapuptime = 0;
01996                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
01997                ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent);
01998                ast_set_flag(p, AGENT_FLAG_WRAPUPTIME);
01999             } else {
02000                p->wrapuptime = wrapuptime;
02001             }
02002             tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
02003             if (!ast_strlen_zero(tmpoptions)) {
02004                p->acceptdtmf = *tmpoptions;
02005                ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent);
02006                ast_set_flag(p, AGENT_FLAG_ACCEPTDTMF);
02007             }
02008             tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF");
02009             if (!ast_strlen_zero(tmpoptions)) {
02010                p->enddtmf = *tmpoptions;
02011                ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent);
02012                ast_set_flag(p, AGENT_FLAG_ENDDTMF);
02013             }
02014             ast_channel_unlock(chan);
02015             unlock_channel = 0;
02016             /* End Channel Specific Agent Overrides */
02017 
02018             if (!p->chan) {
02019                /* Ensure nobody else can be this agent until we're done. */
02020                p->chan = chan;
02021 
02022                p->acknowledged = 0;
02023 
02024                if (!res) {
02025                   res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
02026                   if (res)
02027                      ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats)));
02028                }
02029                if (!res) {
02030                   res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
02031                   if (res)
02032                      ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats)));
02033                }
02034                if (!res && play_announcement == 1) {
02035                   ast_mutex_unlock(&p->lock);
02036                   AST_LIST_UNLOCK(&agents);
02037                   res = ast_streamfile(chan, "agent-loginok", chan->language);
02038                   if (!res) {
02039                      ast_waitstream(chan, "");
02040                   }
02041                   AST_LIST_LOCK(&agents);
02042                   ast_mutex_lock(&p->lock);
02043                }
02044 
02045                if (!res) {
02046                   long logintime;
02047                   char agent[AST_MAX_AGENT];
02048 
02049                   snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
02050 
02051                   /* Login this channel and wait for it to go away */
02052                   ast_indicate_data(chan, AST_CONTROL_HOLD, 
02053                      S_OR(p->moh, NULL), 
02054                      !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02055 
02056                   /* Must be done after starting HOLD. */
02057                   p->lastdisc = ast_tvnow();
02058                   time(&p->loginstart);
02059 
02060                   manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02061                            "Agent: %s\r\n"
02062                            "Channel: %s\r\n"
02063                            "Uniqueid: %s\r\n",
02064                            p->agent, chan->name, chan->uniqueid);
02065                   if (update_cdr && chan->cdr)
02066                      snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "%s", agent);
02067                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
02068                   ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
02069                             ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
02070 
02071                   ast_mutex_unlock(&p->lock);
02072                   AST_LIST_UNLOCK(&agents);
02073 
02074                   while (res >= 0) {
02075                      ast_mutex_lock(&p->lock);
02076                      if (p->deferlogoff) {
02077                         p->deferlogoff = 0;
02078                         ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
02079                         ast_mutex_unlock(&p->lock);
02080                         break;
02081                      }
02082                      ast_mutex_unlock(&p->lock);
02083 
02084                      AST_LIST_LOCK(&agents);
02085                      ast_mutex_lock(&p->lock);
02086                      if (p->lastdisc.tv_sec) {
02087                         if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
02088                            ast_debug(1, "Wrapup time for %s expired!\n", agent);
02089                            p->lastdisc = ast_tv(0, 0);
02090                            ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s", agent);
02091                            if (p->ackcall) {
02092                               check_beep(p, 0);
02093                            } else {
02094                               check_availability(p, 0);
02095                            }
02096                         }
02097                      }
02098                      ast_mutex_unlock(&p->lock);
02099                      AST_LIST_UNLOCK(&agents);
02100 
02101                      /* Synchronize channel ownership between call to agent and itself. */
02102                      ast_mutex_lock(&p->lock);
02103                      if (p->app_lock_flag) {
02104                         ast_cond_signal(&p->login_wait_cond);
02105                         ast_cond_wait(&p->app_complete_cond, &p->lock);
02106                         if (ast_check_hangup(chan)) {
02107                            /* Agent hungup */
02108                            ast_mutex_unlock(&p->lock);
02109                            break;
02110                         }
02111                      }
02112                      ast_mutex_unlock(&p->lock);
02113 
02114                      if (p->ackcall) {
02115                         res = agent_ack_sleep(p);
02116                         if (res == 1) {
02117                            AST_LIST_LOCK(&agents);
02118                            ast_mutex_lock(&p->lock);
02119                            check_availability(p, 0);
02120                            ast_mutex_unlock(&p->lock);
02121                            AST_LIST_UNLOCK(&agents);
02122                         }
02123                      } else {
02124                         res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02125                      }
02126                   }
02127                   ast_mutex_lock(&p->lock);
02128 
02129                   /* Logoff this channel */
02130                   p->chan = NULL;
02131                   logintime = time(NULL) - p->loginstart;
02132                   p->loginstart = 0;
02133 
02134                   /* Synchronize channel ownership between call to agent and itself. */
02135                   if (p->app_lock_flag) {
02136                      ast_cond_signal(&p->login_wait_cond);
02137                      ast_cond_wait(&p->app_complete_cond, &p->lock);
02138                   }
02139 
02140                   if (p->owner) {
02141                      ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
02142                   }
02143 
02144                   p->acknowledged = 0;
02145                   ast_mutex_unlock(&p->lock);
02146 
02147                   ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s", agent);
02148                   manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02149                            "Agent: %s\r\n"
02150                            "Logintime: %ld\r\n"
02151                            "Uniqueid: %s\r\n",
02152                            p->agent, logintime, chan->uniqueid);
02153                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02154                   ast_verb(2, "Agent '%s' logged out\n", p->agent);
02155 
02156                   /* If there is no owner, go ahead and kill it now */
02157                   if (p->dead && !p->owner) {
02158                      agent_pvt_destroy(p);
02159                   }
02160                   AST_LIST_LOCK(&agents);
02161                } else {
02162                   /* Agent hung up before could be logged in. */
02163                   p->chan = NULL;
02164 
02165                   ast_mutex_unlock(&p->lock);
02166                }
02167                res = -1;
02168             } else {
02169                ast_mutex_unlock(&p->lock);
02170                errmsg = "agent-alreadyon";
02171             }
02172             break;
02173          }
02174          ast_mutex_unlock(&p->lock);
02175          if (unlock_channel) {
02176             ast_channel_unlock(chan);
02177          }
02178       }
02179       AST_LIST_UNLOCK(&agents);
02180 
02181       if (!res && (max_login_tries==0 || tries < max_login_tries))
02182          res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02183    }
02184       
02185    if (!res)
02186       res = ast_safe_sleep(chan, 500);
02187 
02188    return -1;
02189 }
02190 
02191 /*!
02192  *  \brief Called by the AgentMonitorOutgoing application (from the dial plan).
02193  *
02194  * \param chan
02195  * \param data
02196  * \returns
02197  * \sa login_exec(), load_module().
02198  */
02199 static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
02200 {
02201    int exitifnoagentid = 0;
02202    int nowarnings = 0;
02203    int changeoutgoing = 0;
02204    int res = 0;
02205    char agent[AST_MAX_AGENT];
02206 
02207    if (data) {
02208       if (strchr(data, 'd'))
02209          exitifnoagentid = 1;
02210       if (strchr(data, 'n'))
02211          nowarnings = 1;
02212       if (strchr(data, 'c'))
02213          changeoutgoing = 1;
02214    }
02215    if (chan->caller.id.number.valid
02216       && !ast_strlen_zero(chan->caller.id.number.str)) {
02217       const char *tmp;
02218       char agentvar[AST_MAX_BUF];
02219       snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID,
02220          chan->caller.id.number.str);
02221       if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02222          struct agent_pvt *p;
02223          ast_copy_string(agent, tmp, sizeof(agent));
02224          AST_LIST_LOCK(&agents);
02225          AST_LIST_TRAVERSE(&agents, p, list) {
02226             if (!strcasecmp(p->agent, tmp)) {
02227                if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02228                __agent_start_monitoring(chan, p, 1);
02229                break;
02230             }
02231          }
02232          AST_LIST_UNLOCK(&agents);
02233          
02234       } else {
02235          res = -1;
02236          if (!nowarnings)
02237             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);
02238       }
02239    } else {
02240       res = -1;
02241       if (!nowarnings)
02242          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");
02243    }
02244    if (res) {
02245       if (exitifnoagentid)
02246          return res;
02247    }
02248    return 0;
02249 }
02250 
02251 /*! \brief Part of PBX channel interface */
02252 static int agent_devicestate(void *data)
02253 {
02254    struct agent_pvt *p;
02255    const char *device = data;
02256    int res = AST_DEVICE_INVALID;
02257 
02258    if (device[0] == '@' || device[0] == ':') {
02259       /* Device state of groups not supported. */
02260       return AST_DEVICE_INVALID;
02261    }
02262 
02263    /* Want device state of a specific agent. */
02264    AST_LIST_LOCK(&agents);
02265    AST_LIST_TRAVERSE(&agents, p, list) {
02266       ast_mutex_lock(&p->lock);
02267       if (!p->pending && !strcmp(device, p->agent)) {
02268          if (p->owner) {
02269             res = AST_DEVICE_BUSY;
02270          } else if (p->chan) {
02271             if (p->lastdisc.tv_sec || p->deferlogoff) {
02272                /* Agent is in wrapup time so unavailable for another call. */
02273                res = AST_DEVICE_INUSE;
02274             } else {
02275                res = AST_DEVICE_NOT_INUSE;
02276             }
02277          } else {
02278             res = AST_DEVICE_UNAVAILABLE;
02279          }
02280          ast_mutex_unlock(&p->lock);
02281          break;
02282       }
02283       ast_mutex_unlock(&p->lock);
02284    }
02285    AST_LIST_UNLOCK(&agents);
02286    return res;
02287 }
02288 
02289 /*!
02290  * \note This function expects the agent list to be locked
02291  */
02292 static struct agent_pvt *find_agent(char *agentid)
02293 {
02294    struct agent_pvt *cur;
02295 
02296    AST_LIST_TRAVERSE(&agents, cur, list) {
02297       if (!strcmp(cur->agent, agentid))
02298          break;   
02299    }
02300 
02301    return cur; 
02302 }
02303 
02304 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
02305 {
02306    char *parse;    
02307    AST_DECLARE_APP_ARGS(args,
02308       AST_APP_ARG(agentid);
02309       AST_APP_ARG(item);
02310    );
02311    char *tmp;
02312    struct agent_pvt *agent;
02313 
02314    buf[0] = '\0';
02315 
02316    if (ast_strlen_zero(data)) {
02317       ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02318       return -1;
02319    }
02320 
02321    parse = ast_strdupa(data);
02322 
02323    AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02324    if (!args.item)
02325       args.item = "status";
02326 
02327    AST_LIST_LOCK(&agents);
02328 
02329    if (!(agent = find_agent(args.agentid))) {
02330       AST_LIST_UNLOCK(&agents);
02331       ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02332       return -1;
02333    }
02334 
02335    if (!strcasecmp(args.item, "status")) {
02336       char *status = "LOGGEDOUT";
02337       if (agent->chan) {
02338          status = "LOGGEDIN";
02339       }
02340       ast_copy_string(buf, status, len);
02341    } else if (!strcasecmp(args.item, "password")) 
02342       ast_copy_string(buf, agent->password, len);
02343    else if (!strcasecmp(args.item, "name"))
02344       ast_copy_string(buf, agent->name, len);
02345    else if (!strcasecmp(args.item, "mohclass"))
02346       ast_copy_string(buf, agent->moh, len);
02347    else if (!strcasecmp(args.item, "channel")) {
02348       if (agent->chan) {
02349          ast_channel_lock(agent->chan);
02350          ast_copy_string(buf, agent->chan->name, len);
02351          ast_channel_unlock(agent->chan);
02352          tmp = strrchr(buf, '-');
02353          if (tmp)
02354             *tmp = '\0';
02355       } 
02356    } else if (!strcasecmp(args.item, "fullchannel")) {
02357       if (agent->chan) {
02358          ast_channel_lock(agent->chan);
02359          ast_copy_string(buf, agent->chan->name, len);
02360          ast_channel_unlock(agent->chan);
02361       } 
02362    } else if (!strcasecmp(args.item, "exten")) {
02363       buf[0] = '\0';
02364    }
02365 
02366    AST_LIST_UNLOCK(&agents);
02367 
02368    return 0;
02369 }
02370 
02371 static struct ast_custom_function agent_function = {
02372    .name = "AGENT",
02373    .read = function_agent,
02374 };
02375 
02376 /*!
02377  * \internal
02378  * \brief Callback used to generate the agents tree.
02379  * \param[in] search The search pattern tree.
02380  * \retval NULL on error.
02381  * \retval non-NULL The generated tree.
02382  */
02383 static int agents_data_provider_get(const struct ast_data_search *search,
02384    struct ast_data *data_root)
02385 {
02386    struct agent_pvt *p;
02387    struct ast_data *data_agent, *data_channel, *data_talkingto;
02388 
02389    AST_LIST_LOCK(&agents);
02390    AST_LIST_TRAVERSE(&agents, p, list) {
02391       struct ast_channel *owner;
02392 
02393       data_agent = ast_data_add_node(data_root, "agent");
02394       if (!data_agent) {
02395          continue;
02396       }
02397 
02398       ast_mutex_lock(&p->lock);
02399       owner = agent_lock_owner(p);
02400 
02401       if (!(p->pending)) {
02402          ast_data_add_str(data_agent, "id", p->agent);
02403          ast_data_add_structure(agent_pvt, data_agent, p);
02404 
02405          ast_data_add_bool(data_agent, "logged", p->chan ? 1 : 0);
02406          if (p->chan) {
02407             data_channel = ast_data_add_node(data_agent, "loggedon");
02408             if (!data_channel) {
02409                ast_mutex_unlock(&p->lock);
02410                ast_data_remove_node(data_root, data_agent);
02411                if (owner) {
02412                   ast_channel_unlock(owner);
02413                   owner = ast_channel_unref(owner);
02414                }
02415                continue;
02416             }
02417             ast_channel_data_add_structure(data_channel, p->chan, 0);
02418             if (owner && ast_bridged_channel(owner)) {
02419                data_talkingto = ast_data_add_node(data_agent, "talkingto");
02420                if (!data_talkingto) {
02421                   ast_mutex_unlock(&p->lock);
02422                   ast_data_remove_node(data_root, data_agent);
02423                   if (owner) {
02424                      ast_channel_unlock(owner);
02425                      owner = ast_channel_unref(owner);
02426                   }
02427                   continue;
02428                }
02429                ast_channel_data_add_structure(data_talkingto, ast_bridged_channel(owner), 0);
02430             }
02431          } else {
02432             ast_data_add_node(data_agent, "talkingto");
02433             ast_data_add_node(data_agent, "loggedon");
02434          }
02435          ast_data_add_str(data_agent, "musiconhold", p->moh);
02436       }
02437 
02438       if (owner) {
02439          ast_channel_unlock(owner);
02440          owner = ast_channel_unref(owner);
02441       }
02442 
02443       ast_mutex_unlock(&p->lock);
02444 
02445       /* if this agent doesn't match remove the added agent. */
02446       if (!ast_data_search_match(search, data_agent)) {
02447          ast_data_remove_node(data_root, data_agent);
02448       }
02449    }
02450    AST_LIST_UNLOCK(&agents);
02451 
02452    return 0;
02453 }
02454 
02455 static const struct ast_data_handler agents_data_provider = {
02456    .version = AST_DATA_HANDLER_VERSION,
02457    .get = agents_data_provider_get
02458 };
02459 
02460 static const struct ast_data_entry agents_data_providers[] = {
02461    AST_DATA_ENTRY("asterisk/channel/agent/list", &agents_data_provider),
02462 };
02463 
02464 /*!
02465  * \brief Initialize the Agents module.
02466  * This function is being called by Asterisk when loading the module. 
02467  * Among other things it registers applications, cli commands and reads the cofiguration file.
02468  *
02469  * \returns int Always 0.
02470  */
02471 static int load_module(void)
02472 {
02473    /* Make sure we can register our agent channel type */
02474    if (ast_channel_register(&agent_tech)) {
02475       ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
02476       return AST_MODULE_LOAD_FAILURE;
02477    }
02478    /* Read in the config */
02479    if (!read_agent_config(0))
02480       return AST_MODULE_LOAD_DECLINE;
02481    /* Dialplan applications */
02482    ast_register_application_xml(app, login_exec);
02483    ast_register_application_xml(app3, agentmonitoroutgoing_exec);
02484 
02485    /* data tree */
02486    ast_data_register_multiple(agents_data_providers, ARRAY_LEN(agents_data_providers));
02487 
02488    /* Manager commands */
02489    ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
02490    ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
02491 
02492    /* CLI Commands */
02493    ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
02494 
02495    /* Dialplan Functions */
02496    ast_custom_function_register(&agent_function);
02497 
02498    return AST_MODULE_LOAD_SUCCESS;
02499 }
02500 
02501 static int reload(void)
02502 {
02503    return read_agent_config(1);
02504 }
02505 
02506 static int unload_module(void)
02507 {
02508    struct agent_pvt *p;
02509    /* First, take us out of the channel loop */
02510    ast_channel_unregister(&agent_tech);
02511    /* Unregister dialplan functions */
02512    ast_custom_function_unregister(&agent_function);   
02513    /* Unregister CLI commands */
02514    ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
02515    /* Unregister dialplan applications */
02516    ast_unregister_application(app);
02517    ast_unregister_application(app3);
02518    /* Unregister manager command */
02519    ast_manager_unregister("Agents");
02520    ast_manager_unregister("AgentLogoff");
02521    /* Unregister the data tree */
02522    ast_data_unregister(NULL);
02523    /* Unregister channel */
02524    AST_LIST_LOCK(&agents);
02525    /* Hangup all interfaces if they have an owner */
02526    while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
02527       if (p->owner)
02528          ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02529       ast_free(p);
02530    }
02531    AST_LIST_UNLOCK(&agents);
02532    return 0;
02533 }
02534 
02535 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Agent Proxy Channel",
02536       .load = load_module,
02537       .unload = unload_module,
02538       .reload = reload,
02539       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02540       .nonoptreq = "res_monitor,chan_local",
02541           );

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1