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

Generated on Mon Oct 8 12:38:58 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7