Thu Apr 28 2011 17:15:16

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