Mon Mar 31 07:37:57 2008

Asterisk developer's documentation


chan_agent.c

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

Generated on Mon Mar 31 07:37:57 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1