Fri Jul 15 2011 11:57:23

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