Mon Jun 18 2012 03:55:23

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