Mon Mar 19 11:30:23 2012

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

Generated on Mon Mar 19 11:30:23 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7