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

Generated on Thu Oct 11 06:33:37 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6