Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:32 2007

Asterisk developer's documentation :: Codename Pineapple


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

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen