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

Generated on Sat Feb 11 06:33:06 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6