Thu Oct 8 21:55:55 2009

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: 118953 $")
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, NULL, NULL, 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          ast_assert(0);
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(S_OR(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                   DEADLOCK_AVOIDANCE(&p->lock);
01592                }
01593                if (p->owner) {
01594                   ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
01595                   ast_channel_unlock(p->owner);
01596                }
01597 
01598                while (p->chan && ast_channel_trylock(p->chan)) {
01599                   DEADLOCK_AVOIDANCE(&p->lock);
01600                }
01601                if (p->chan) {
01602                   ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01603                   ast_channel_unlock(p->chan);
01604                }
01605 
01606                ast_mutex_unlock(&p->lock);
01607             } else
01608                p->deferlogoff = 1;
01609          } else {
01610             logintime = time(NULL) - p->loginstart;
01611             p->loginstart = 0;
01612             agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
01613          }
01614          break;
01615       }
01616    }
01617    AST_LIST_UNLOCK(&agents);
01618 
01619    return ret;
01620 }
01621 
01622 static int agent_logoff_cmd(int fd, int argc, char **argv)
01623 {
01624    int ret;
01625    char *agent;
01626 
01627    if (argc < 3 || argc > 4)
01628       return RESULT_SHOWUSAGE;
01629    if (argc == 4 && strcasecmp(argv[3], "soft"))
01630       return RESULT_SHOWUSAGE;
01631 
01632    agent = argv[2] + 6;
01633    ret = agent_logoff(agent, argc == 4);
01634    if (ret == 0)
01635       ast_cli(fd, "Logging out %s\n", agent);
01636 
01637    return RESULT_SUCCESS;
01638 }
01639 
01640 /*!
01641  * Sets an agent as no longer logged in in the Manager API.
01642  * It is registered on load_module() and it gets called by the manager backend.
01643  * \param s
01644  * \param m
01645  * \returns 
01646  * \sa action_agents(), action_agent_callback_login(), load_module().
01647  */
01648 static int action_agent_logoff(struct mansession *s, const struct message *m)
01649 {
01650    const char *agent = astman_get_header(m, "Agent");
01651    const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
01652    int soft;
01653    int ret; /* return value of agent_logoff */
01654 
01655    if (ast_strlen_zero(agent)) {
01656       astman_send_error(s, m, "No agent specified");
01657       return 0;
01658    }
01659 
01660    soft = ast_true(soft_s) ? 1 : 0;
01661    ret = agent_logoff(agent, soft);
01662    if (ret == 0)
01663       astman_send_ack(s, m, "Agent logged out");
01664    else
01665       astman_send_error(s, m, "No such agent");
01666 
01667    return 0;
01668 }
01669 
01670 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
01671 {
01672    char *ret = NULL;
01673 
01674    if (pos == 2) {
01675       struct agent_pvt *p;
01676       char name[AST_MAX_AGENT];
01677       int which = 0, len = strlen(word);
01678 
01679       AST_LIST_LOCK(&agents);
01680       AST_LIST_TRAVERSE(&agents, p, list) {
01681          snprintf(name, sizeof(name), "Agent/%s", p->agent);
01682          if (!strncasecmp(word, name, len) && ++which > state) {
01683             ret = ast_strdup(name);
01684             break;
01685          }
01686       }
01687       AST_LIST_UNLOCK(&agents);
01688    } else if (pos == 3 && state == 0) 
01689       return ast_strdup("soft");
01690    
01691    return ret;
01692 }
01693 
01694 /*!
01695  * Show agents in cli.
01696  */
01697 static int agents_show(int fd, int argc, char **argv)
01698 {
01699    struct agent_pvt *p;
01700    char username[AST_MAX_BUF];
01701    char location[AST_MAX_BUF] = "";
01702    char talkingto[AST_MAX_BUF] = "";
01703    char moh[AST_MAX_BUF];
01704    int count_agents = 0;      /*!< Number of agents configured */
01705    int online_agents = 0;     /*!< Number of online agents */
01706    int offline_agents = 0;    /*!< Number of offline agents */
01707    if (argc != 2)
01708       return RESULT_SHOWUSAGE;
01709    AST_LIST_LOCK(&agents);
01710    AST_LIST_TRAVERSE(&agents, p, list) {
01711       ast_mutex_lock(&p->lock);
01712       if (p->pending) {
01713          if (p->group)
01714             ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
01715          else
01716             ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
01717       } else {
01718          if (!ast_strlen_zero(p->name))
01719             snprintf(username, sizeof(username), "(%s) ", p->name);
01720          else
01721             username[0] = '\0';
01722          if (p->chan) {
01723             snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01724             if (p->owner && ast_bridged_channel(p->owner))
01725                snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01726              else 
01727                strcpy(talkingto, " is idle");
01728             online_agents++;
01729          } else if (!ast_strlen_zero(p->loginchan)) {
01730             if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec)) 
01731                snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01732             else 
01733                snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
01734             talkingto[0] = '\0';
01735             online_agents++;
01736             if (p->acknowledged)
01737                strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01738          } else {
01739             strcpy(location, "not logged in");
01740             talkingto[0] = '\0';
01741             offline_agents++;
01742          }
01743          if (!ast_strlen_zero(p->moh))
01744             snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01745          ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, 
01746             username, location, talkingto, moh);
01747          count_agents++;
01748       }
01749       ast_mutex_unlock(&p->lock);
01750    }
01751    AST_LIST_UNLOCK(&agents);
01752    if ( !count_agents ) 
01753       ast_cli(fd, "No Agents are configured in %s\n",config);
01754    else 
01755       ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01756    ast_cli(fd, "\n");
01757                    
01758    return RESULT_SUCCESS;
01759 }
01760 
01761 
01762 static int agents_show_online(int fd, int argc, char **argv)
01763 {
01764    struct agent_pvt *p;
01765    char username[AST_MAX_BUF];
01766    char location[AST_MAX_BUF] = "";
01767    char talkingto[AST_MAX_BUF] = "";
01768    char moh[AST_MAX_BUF];
01769    int count_agents = 0;           /* Number of agents configured */
01770    int online_agents = 0;          /* Number of online agents */
01771    int agent_status = 0;           /* 0 means offline, 1 means online */
01772    if (argc != 3)
01773       return RESULT_SHOWUSAGE;
01774    AST_LIST_LOCK(&agents);
01775    AST_LIST_TRAVERSE(&agents, p, list) {
01776       agent_status = 0;       /* reset it to offline */
01777       ast_mutex_lock(&p->lock);
01778       if (!ast_strlen_zero(p->name))
01779          snprintf(username, sizeof(username), "(%s) ", p->name);
01780       else
01781          username[0] = '\0';
01782       if (p->chan) {
01783          snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01784          if (p->owner && ast_bridged_channel(p->owner)) 
01785             snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01786          else 
01787             strcpy(talkingto, " is idle");
01788          agent_status = 1;
01789          online_agents++;
01790       } else if (!ast_strlen_zero(p->loginchan)) {
01791          snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01792          talkingto[0] = '\0';
01793          agent_status = 1;
01794          online_agents++;
01795          if (p->acknowledged)
01796             strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01797       }
01798       if (!ast_strlen_zero(p->moh))
01799          snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01800       if (agent_status)
01801          ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
01802       count_agents++;
01803       ast_mutex_unlock(&p->lock);
01804    }
01805    AST_LIST_UNLOCK(&agents);
01806    if (!count_agents) 
01807       ast_cli(fd, "No Agents are configured in %s\n", config);
01808    else
01809       ast_cli(fd, "%d agents online\n", online_agents);
01810    ast_cli(fd, "\n");
01811    return RESULT_SUCCESS;
01812 }
01813 
01814 
01815 
01816 static char show_agents_usage[] = 
01817 "Usage: agent show\n"
01818 "       Provides summary information on agents.\n";
01819 
01820 static char show_agents_online_usage[] =
01821 "Usage: agent show online\n"
01822 "  Provides a list of all online agents.\n";
01823 
01824 static char agent_logoff_usage[] =
01825 "Usage: agent logoff <channel> [soft]\n"
01826 "       Sets an agent as no longer logged in.\n"
01827 "       If 'soft' is specified, do not hangup existing calls.\n";
01828 
01829 static struct ast_cli_entry cli_show_agents_deprecated = {
01830    { "show", "agents", NULL },
01831    agents_show, NULL,
01832    NULL, NULL };
01833 
01834 static struct ast_cli_entry cli_show_agents_online_deprecated = {
01835    { "show", "agents", "online" },
01836    agents_show_online, NULL,
01837    NULL, NULL };
01838 
01839 static struct ast_cli_entry cli_agents[] = {
01840    { { "agent", "show", NULL },
01841    agents_show, "Show status of agents",
01842    show_agents_usage, NULL, &cli_show_agents_deprecated },
01843 
01844    { { "agent", "show", "online" },
01845    agents_show_online, "Show all online agents",
01846    show_agents_online_usage, NULL, &cli_show_agents_online_deprecated },
01847 
01848    { { "agent", "logoff", NULL },
01849    agent_logoff_cmd, "Sets an agent offline",
01850    agent_logoff_usage, complete_agent_logoff_cmd },
01851 };
01852 
01853 /*!
01854  * \brief Log in agent application.
01855  *
01856  * \param chan
01857  * \param data
01858  * \param callbackmode non-zero for AgentCallbackLogin
01859  */
01860 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
01861 {
01862    int res=0;
01863    int tries = 0;
01864    int max_login_tries = maxlogintries;
01865    struct agent_pvt *p;
01866    struct ast_module_user *u;
01867    int login_state = 0;
01868    char user[AST_MAX_AGENT] = "";
01869    char pass[AST_MAX_AGENT];
01870    char agent[AST_MAX_AGENT] = "";
01871    char xpass[AST_MAX_AGENT] = "";
01872    char *errmsg;
01873    char *parse;
01874    AST_DECLARE_APP_ARGS(args,
01875               AST_APP_ARG(agent_id);
01876               AST_APP_ARG(options);
01877               AST_APP_ARG(extension);
01878       );
01879    const char *tmpoptions = NULL;
01880    char *context = NULL;
01881    int play_announcement = 1;
01882    char agent_goodbye[AST_MAX_FILENAME_LEN];
01883    int update_cdr = updatecdr;
01884    char *filename = "agent-loginok";
01885    char tmpchan[AST_MAX_BUF] = "";
01886 
01887    u = ast_module_user_add(chan);
01888 
01889    parse = ast_strdupa(data);
01890 
01891    AST_STANDARD_APP_ARGS(args, parse);
01892 
01893    ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01894 
01895    /* Set Channel Specific Login Overrides */
01896    if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01897       max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01898       if (max_login_tries < 0)
01899          max_login_tries = 0;
01900       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01901       if (option_verbose > 2)
01902          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);
01903    }
01904    if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01905       if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01906          update_cdr = 1;
01907       else
01908          update_cdr = 0;
01909       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01910       if (option_verbose > 2)
01911          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
01912    }
01913    if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01914       strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01915       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01916       if (option_verbose > 2)
01917          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
01918    }
01919    /* End Channel Specific Login Overrides */
01920    
01921    if (callbackmode && args.extension) {
01922       parse = args.extension;
01923       args.extension = strsep(&parse, "@");
01924       context = parse;
01925    }
01926 
01927    if (!ast_strlen_zero(args.options)) {
01928       if (strchr(args.options, 's')) {
01929          play_announcement = 0;
01930       }
01931    }
01932 
01933    if (chan->_state != AST_STATE_UP)
01934       res = ast_answer(chan);
01935    if (!res) {
01936       if (!ast_strlen_zero(args.agent_id))
01937          ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
01938       else
01939          res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
01940    }
01941    while (!res && (max_login_tries==0 || tries < max_login_tries)) {
01942       tries++;
01943       /* Check for password */
01944       AST_LIST_LOCK(&agents);
01945       AST_LIST_TRAVERSE(&agents, p, list) {
01946          if (!strcmp(p->agent, user) && !p->pending)
01947             ast_copy_string(xpass, p->password, sizeof(xpass));
01948       }
01949       AST_LIST_UNLOCK(&agents);
01950       if (!res) {
01951          if (!ast_strlen_zero(xpass))
01952             res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
01953          else
01954             pass[0] = '\0';
01955       }
01956       errmsg = "agent-incorrect";
01957 
01958 #if 0
01959       ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
01960 #endif      
01961 
01962       /* Check again for accuracy */
01963       AST_LIST_LOCK(&agents);
01964       AST_LIST_TRAVERSE(&agents, p, list) {
01965          ast_mutex_lock(&p->lock);
01966          if (!strcmp(p->agent, user) &&
01967              !strcmp(p->password, pass) && !p->pending) {
01968             login_state = 1; /* Successful Login */
01969 
01970             /* Ensure we can't be gotten until we're done */
01971             gettimeofday(&p->lastdisc, NULL);
01972             p->lastdisc.tv_sec++;
01973 
01974             /* Set Channel Specific Agent Overrides */
01975             if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01976                if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
01977                   p->ackcall = 2;
01978                else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
01979                   p->ackcall = 1;
01980                else
01981                   p->ackcall = 0;
01982                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
01983                if (option_verbose > 2)
01984                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
01985             }
01986             if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
01987                p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
01988                if (p->autologoff < 0)
01989                   p->autologoff = 0;
01990                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
01991                if (option_verbose > 2)
01992                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
01993             }
01994             if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
01995                p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
01996                if (p->wrapuptime < 0)
01997                   p->wrapuptime = 0;
01998                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
01999                if (option_verbose > 2)
02000                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
02001             }
02002             /* End Channel Specific Agent Overrides */
02003             if (!p->chan) {
02004                char last_loginchan[80] = "";
02005                long logintime;
02006                snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
02007 
02008                if (callbackmode) {
02009                   int pos = 0;
02010                   /* Retrieve login chan */
02011                   for (;;) {
02012                      if (!ast_strlen_zero(args.extension)) {
02013                         ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
02014                         res = 0;
02015                      } else
02016                         res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
02017                      if (ast_strlen_zero(tmpchan) )
02018                         break;
02019                      if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
02020                         if(!allow_multiple_login(tmpchan,context) ) {
02021                            args.extension = NULL;
02022                            pos = 0;
02023                         } else
02024                            break;
02025                      }
02026                      if (args.extension) {
02027                         ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
02028                         args.extension = NULL;
02029                         pos = 0;
02030                      } else {
02031                         ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
02032                         res = ast_streamfile(chan, "invalid", chan->language);
02033                         if (!res)
02034                            res = ast_waitstream(chan, AST_DIGIT_ANY);
02035                         if (res > 0) {
02036                            tmpchan[0] = res;
02037                            tmpchan[1] = '\0';
02038                            pos = 1;
02039                         } else {
02040                            tmpchan[0] = '\0';
02041                            pos = 0;
02042                         }
02043                      }
02044                   }
02045                   args.extension = tmpchan;
02046                   if (!res) {
02047                      set_agentbycallerid(p->logincallerid, NULL);
02048                      if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
02049                         snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
02050                      else {
02051                         ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
02052                         ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
02053                      }
02054                      p->acknowledged = 0;
02055                      if (ast_strlen_zero(p->loginchan)) {
02056                         login_state = 2;
02057                         filename = "agent-loggedoff";
02058                      } else {
02059                         if (chan->cid.cid_num) {
02060                            ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
02061                            set_agentbycallerid(p->logincallerid, p->agent);
02062                         } else
02063                            p->logincallerid[0] = '\0';
02064                      }
02065 
02066                      if(update_cdr && chan->cdr)
02067                         snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02068 
02069                   }
02070                } else {
02071                   p->loginchan[0] = '\0';
02072                   p->logincallerid[0] = '\0';
02073                   p->acknowledged = 0;
02074                }
02075                ast_mutex_unlock(&p->lock);
02076                AST_LIST_UNLOCK(&agents);
02077                if( !res && play_announcement==1 )
02078                   res = ast_streamfile(chan, filename, chan->language);
02079                if (!res)
02080                   ast_waitstream(chan, "");
02081                AST_LIST_LOCK(&agents);
02082                ast_mutex_lock(&p->lock);
02083                if (!res) {
02084                   res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
02085                   if (res)
02086                      ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
02087                }
02088                if (!res) {
02089                   res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
02090                   if (res)
02091                      ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
02092                }
02093                /* Check once more just in case */
02094                if (p->chan)
02095                   res = -1;
02096                if (callbackmode && !res) {
02097                   /* Just say goodbye and be done with it */
02098                   if (!ast_strlen_zero(p->loginchan)) {
02099                      if (p->loginstart == 0)
02100                         time(&p->loginstart);
02101                      manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02102                               "Agent: %s\r\n"
02103                               "Loginchan: %s\r\n"
02104                               "Uniqueid: %s\r\n",
02105                               p->agent, p->loginchan, chan->uniqueid);
02106                      ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02107                      if (option_verbose > 1)
02108                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02109                      ast_device_state_changed("Agent/%s", p->agent);
02110                      if (persistent_agents)
02111                         dump_agents();
02112                   } else {
02113                      logintime = time(NULL) - p->loginstart;
02114                      p->loginstart = 0;
02115 
02116                      agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
02117                      if (option_verbose > 1)
02118                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
02119                   }
02120                   AST_LIST_UNLOCK(&agents);
02121                   if (!res)
02122                      res = ast_safe_sleep(chan, 500);
02123                   ast_mutex_unlock(&p->lock);
02124                } else if (!res) {
02125                   ast_indicate_data(chan, AST_CONTROL_HOLD, 
02126                      S_OR(p->moh, NULL), 
02127                      !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02128                   if (p->loginstart == 0)
02129                      time(&p->loginstart);
02130                   manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02131                            "Agent: %s\r\n"
02132                            "Channel: %s\r\n"
02133                            "Uniqueid: %s\r\n",
02134                            p->agent, chan->name, chan->uniqueid);
02135                   if (update_cdr && chan->cdr)
02136                      snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02137                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
02138                   if (option_verbose > 1)
02139                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
02140                             ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
02141                   /* Login this channel and wait for it to go away */
02142                   p->chan = chan;
02143                   if (p->ackcall > 1)
02144                      check_beep(p, 0);
02145                   else
02146                      check_availability(p, 0);
02147                   ast_mutex_unlock(&p->lock);
02148                   AST_LIST_UNLOCK(&agents);
02149                   ast_device_state_changed("Agent/%s", p->agent);
02150                   while (res >= 0) {
02151                      ast_mutex_lock(&p->lock);
02152                      if (p->deferlogoff && p->chan) {
02153                         ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
02154                         p->deferlogoff = 0;
02155                      }
02156                      if (p->chan != chan)
02157                         res = -1;
02158                      ast_mutex_unlock(&p->lock);
02159                      /* Yield here so other interested threads can kick in. */
02160                      sched_yield();
02161                      if (res)
02162                         break;
02163 
02164                      AST_LIST_LOCK(&agents);
02165                      ast_mutex_lock(&p->lock);
02166                      if (p->lastdisc.tv_sec) {
02167                         if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
02168                            if (option_debug)
02169                               ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
02170                            p->lastdisc = ast_tv(0, 0);
02171                            ast_device_state_changed("Agent/%s", p->agent);
02172                            if (p->ackcall > 1)
02173                               check_beep(p, 0);
02174                            else
02175                               check_availability(p, 0);
02176                         }
02177                      }
02178                      ast_mutex_unlock(&p->lock);
02179                      AST_LIST_UNLOCK(&agents);
02180                      /* Synchronize channel ownership between call to agent and itself. */
02181                      ast_mutex_lock( &p->app_lock );
02182                      ast_mutex_lock(&p->lock);
02183                      p->owning_app = pthread_self();
02184                      ast_mutex_unlock(&p->lock);
02185                      if (p->ackcall > 1) 
02186                         res = agent_ack_sleep(p);
02187                      else
02188                         res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02189                      ast_mutex_unlock( &p->app_lock );
02190                      if ((p->ackcall > 1)  && (res == 1)) {
02191                         AST_LIST_LOCK(&agents);
02192                         ast_mutex_lock(&p->lock);
02193                         check_availability(p, 0);
02194                         ast_mutex_unlock(&p->lock);
02195                         AST_LIST_UNLOCK(&agents);
02196                         res = 0;
02197                      }
02198                      sched_yield();
02199                   }
02200                   ast_mutex_lock(&p->lock);
02201                   if (res && p->owner) 
02202                      ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
02203                   /* Log us off if appropriate */
02204                   if (p->chan == chan)
02205                      p->chan = NULL;
02206                   p->acknowledged = 0;
02207                   logintime = time(NULL) - p->loginstart;
02208                   p->loginstart = 0;
02209                   ast_mutex_unlock(&p->lock);
02210                   manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02211                            "Agent: %s\r\n"
02212                            "Logintime: %ld\r\n"
02213                            "Uniqueid: %s\r\n",
02214                            p->agent, logintime, chan->uniqueid);
02215                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02216                   if (option_verbose > 1)
02217                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
02218                   /* If there is no owner, go ahead and kill it now */
02219                   ast_device_state_changed("Agent/%s", p->agent);
02220                   if (p->dead && !p->owner) {
02221                      ast_mutex_destroy(&p->lock);
02222                      ast_mutex_destroy(&p->app_lock);
02223                      free(p);
02224                   }
02225                }
02226                else {
02227                   ast_mutex_unlock(&p->lock);
02228                   p = NULL;
02229                }
02230                res = -1;
02231             } else {
02232                ast_mutex_unlock(&p->lock);
02233                errmsg = "agent-alreadyon";
02234                p = NULL;
02235             }
02236             break;
02237          }
02238          ast_mutex_unlock(&p->lock);
02239       }
02240       if (!p)
02241          AST_LIST_UNLOCK(&agents);
02242 
02243       if (!res && (max_login_tries==0 || tries < max_login_tries))
02244          res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02245    }
02246       
02247    if (!res)
02248       res = ast_safe_sleep(chan, 500);
02249 
02250    /* AgentLogin() exit */
02251    if (!callbackmode) {
02252       ast_module_user_remove(u);
02253       return -1;
02254    } else { /* AgentCallbackLogin() exit*/
02255       /* Set variables */
02256       if (login_state > 0) {
02257          pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
02258          if (login_state==1) {
02259             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
02260             pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
02261          } else 
02262             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
02263       } else {
02264          pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
02265       }
02266       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
02267          ast_module_user_remove(u);
02268          return 0;
02269       }
02270       /* Do we need to play agent-goodbye now that we will be hanging up? */
02271       if (play_announcement) {
02272          if (!res)
02273             res = ast_safe_sleep(chan, 1000);
02274          res = ast_streamfile(chan, agent_goodbye, chan->language);
02275          if (!res)
02276             res = ast_waitstream(chan, "");
02277          if (!res)
02278             res = ast_safe_sleep(chan, 1000);
02279       }
02280    }
02281 
02282    ast_module_user_remove(u);
02283    
02284    /* We should never get here if next priority exists when in callbackmode */
02285    return -1;
02286 }
02287 
02288 /*!
02289  * Called by the AgentLogin application (from the dial plan).
02290  * 
02291  * \param chan
02292  * \param data
02293  * \returns
02294  * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
02295  */
02296 static int login_exec(struct ast_channel *chan, void *data)
02297 {
02298    return __login_exec(chan, data, 0);
02299 }
02300 
02301 static void callback_deprecated(void)
02302 {
02303    static int depwarning = 0;
02304 
02305    if (!depwarning) {
02306       depwarning = 1;
02307 
02308       ast_log(LOG_WARNING, "AgentCallbackLogin is deprecated and will be removed in a future release.\n");
02309       ast_log(LOG_WARNING, "See doc/queues-with-callback-members.txt for an example of how to achieve\n");
02310       ast_log(LOG_WARNING, "the same functionality using only dialplan logic.\n");
02311    }
02312 }
02313 
02314 /*!
02315  *  Called by the AgentCallbackLogin application (from the dial plan).
02316  * 
02317  * \param chan
02318  * \param data
02319  * \returns
02320  * \sa login_exec(), agentmonitoroutgoing_exec(), load_module().
02321  */
02322 static int callback_exec(struct ast_channel *chan, void *data)
02323 {
02324    callback_deprecated();
02325 
02326    return __login_exec(chan, data, 1);
02327 }
02328 
02329 /*!
02330  * Sets an agent as logged in by callback in the Manager API.
02331  * It is registered on load_module() and it gets called by the manager backend.
02332  * \param s
02333  * \param m
02334  * \returns 
02335  * \sa action_agents(), action_agent_logoff(), load_module().
02336  */
02337 static int action_agent_callback_login(struct mansession *s, const struct message *m)
02338 {
02339    const char *agent = astman_get_header(m, "Agent");
02340    const char *exten = astman_get_header(m, "Exten");
02341    const char *context = astman_get_header(m, "Context");
02342    const char *wrapuptime_s = astman_get_header(m, "WrapupTime");
02343    const char *ackcall_s = astman_get_header(m, "AckCall");
02344    struct agent_pvt *p;
02345    int login_state = 0;
02346 
02347    callback_deprecated();
02348 
02349    if (ast_strlen_zero(agent)) {
02350       astman_send_error(s, m, "No agent specified");
02351       return 0;
02352    }
02353 
02354    if (ast_strlen_zero(exten)) {
02355       astman_send_error(s, m, "No extension specified");
02356       return 0;
02357    }
02358 
02359    AST_LIST_LOCK(&agents);
02360    AST_LIST_TRAVERSE(&agents, p, list) {
02361       if (strcmp(p->agent, agent) || p->pending) 
02362          continue;
02363       if (p->chan) {
02364          login_state = 2; /* already logged in (and on the phone)*/
02365          break;
02366       }
02367       ast_mutex_lock(&p->lock);
02368       login_state = 1; /* Successful Login */
02369       
02370       if (ast_strlen_zero(context))
02371          ast_copy_string(p->loginchan, exten, sizeof(p->loginchan));
02372       else
02373          snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context);
02374 
02375       if (!ast_strlen_zero(wrapuptime_s)) {
02376          p->wrapuptime = atoi(wrapuptime_s);
02377          if (p->wrapuptime < 0)
02378             p->wrapuptime = 0;
02379       }
02380 
02381       if (ast_true(ackcall_s))
02382          p->ackcall = 1;
02383       else
02384          p->ackcall = 0;
02385 
02386       if (p->loginstart == 0)
02387          time(&p->loginstart);
02388       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02389                "Agent: %s\r\n"
02390                "Loginchan: %s\r\n",
02391                p->agent, p->loginchan);
02392       ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02393       if (option_verbose > 1)
02394          ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02395       ast_device_state_changed("Agent/%s", p->agent);
02396       ast_mutex_unlock(&p->lock);
02397       if (persistent_agents)
02398          dump_agents();
02399    }
02400    AST_LIST_UNLOCK(&agents);
02401 
02402    if (login_state == 1)
02403       astman_send_ack(s, m, "Agent logged in");
02404    else if (login_state == 0)
02405       astman_send_error(s, m, "No such agent");
02406    else if (login_state == 2)
02407       astman_send_error(s, m, "Agent already logged in");
02408 
02409    return 0;
02410 }
02411 
02412 /*!
02413  *  \brief Called by the AgentMonitorOutgoing application (from the dial plan).
02414  *
02415  * \param chan
02416  * \param data
02417  * \returns
02418  * \sa login_exec(), callback_login_exec(), load_module().
02419  */
02420 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
02421 {
02422    int exitifnoagentid = 0;
02423    int nowarnings = 0;
02424    int changeoutgoing = 0;
02425    int res = 0;
02426    char agent[AST_MAX_AGENT];
02427 
02428    if (data) {
02429       if (strchr(data, 'd'))
02430          exitifnoagentid = 1;
02431       if (strchr(data, 'n'))
02432          nowarnings = 1;
02433       if (strchr(data, 'c'))
02434          changeoutgoing = 1;
02435    }
02436    if (chan->cid.cid_num) {
02437       const char *tmp;
02438       char agentvar[AST_MAX_BUF];
02439       snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
02440       if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02441          struct agent_pvt *p;
02442          ast_copy_string(agent, tmp, sizeof(agent));
02443          AST_LIST_LOCK(&agents);
02444          AST_LIST_TRAVERSE(&agents, p, list) {
02445             if (!strcasecmp(p->agent, tmp)) {
02446                if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02447                __agent_start_monitoring(chan, p, 1);
02448                break;
02449             }
02450          }
02451          AST_LIST_UNLOCK(&agents);
02452          
02453       } else {
02454          res = -1;
02455          if (!nowarnings)
02456             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);
02457       }
02458    } else {
02459       res = -1;
02460       if (!nowarnings)
02461          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");
02462    }
02463    /* check if there is n + 101 priority */
02464    /*! \todo XXX Needs to check option priorityjump etc etc */
02465    if (res) {
02466       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
02467          chan->priority+=100;
02468          if (option_verbose > 2)
02469             ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
02470       } else if (exitifnoagentid)
02471          return res;
02472    }
02473    return 0;
02474 }
02475 
02476 /*!
02477  * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
02478  */
02479 static void dump_agents(void)
02480 {
02481    struct agent_pvt *cur_agent = NULL;
02482    char buf[256];
02483 
02484    AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02485       if (cur_agent->chan)
02486          continue;
02487 
02488       if (!ast_strlen_zero(cur_agent->loginchan)) {
02489          snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
02490          if (ast_db_put(pa_family, cur_agent->agent, buf))
02491             ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
02492          else if (option_debug)
02493             ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
02494       } else {
02495          /* Delete -  no agent or there is an error */
02496          ast_db_del(pa_family, cur_agent->agent);
02497       }
02498    }
02499 }
02500 
02501 /*!
02502  * \brief Reload the persistent agents from astdb.
02503  */
02504 static void reload_agents(void)
02505 {
02506    char *agent_num;
02507    struct ast_db_entry *db_tree;
02508    struct ast_db_entry *entry;
02509    struct agent_pvt *cur_agent;
02510    char agent_data[256];
02511    char *parse;
02512    char *agent_chan;
02513    char *agent_callerid;
02514 
02515    db_tree = ast_db_gettree(pa_family, NULL);
02516 
02517    AST_LIST_LOCK(&agents);
02518    for (entry = db_tree; entry; entry = entry->next) {
02519       agent_num = entry->key + strlen(pa_family) + 2;
02520       AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02521          ast_mutex_lock(&cur_agent->lock);
02522          if (strcmp(agent_num, cur_agent->agent) == 0)
02523             break;
02524          ast_mutex_unlock(&cur_agent->lock);
02525       }
02526       if (!cur_agent) {
02527          ast_db_del(pa_family, agent_num);
02528          continue;
02529       } else
02530          ast_mutex_unlock(&cur_agent->lock);
02531       if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
02532          if (option_debug)
02533             ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
02534          parse = agent_data;
02535          agent_chan = strsep(&parse, ";");
02536          agent_callerid = strsep(&parse, ";");
02537          ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
02538          if (agent_callerid) {
02539             ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
02540             set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
02541          } else
02542             cur_agent->logincallerid[0] = '\0';
02543          if (cur_agent->loginstart == 0)
02544             time(&cur_agent->loginstart);
02545          ast_device_state_changed("Agent/%s", cur_agent->agent);  
02546       }
02547    }
02548    AST_LIST_UNLOCK(&agents);
02549    if (db_tree) {
02550       ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
02551       ast_db_freetree(db_tree);
02552    }
02553 }
02554 
02555 /*! \brief Part of PBX channel interface */
02556 static int agent_devicestate(void *data)
02557 {
02558    struct agent_pvt *p;
02559    char *s;
02560    ast_group_t groupmatch;
02561    int groupoff;
02562    int waitforagent=0;
02563    int res = AST_DEVICE_INVALID;
02564    
02565    s = data;
02566    if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
02567       groupmatch = (1 << groupoff);
02568    else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
02569       groupmatch = (1 << groupoff);
02570       waitforagent = 1;
02571    } else 
02572       groupmatch = 0;
02573 
02574    /* Check actual logged in agents first */
02575    AST_LIST_LOCK(&agents);
02576    AST_LIST_TRAVERSE(&agents, p, list) {
02577       ast_mutex_lock(&p->lock);
02578       if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
02579          if (p->owner) {
02580             if (res != AST_DEVICE_INUSE)
02581                res = AST_DEVICE_BUSY;
02582          } else {
02583             if (res == AST_DEVICE_BUSY)
02584                res = AST_DEVICE_INUSE;
02585             if (p->chan || !ast_strlen_zero(p->loginchan)) {
02586                if (res == AST_DEVICE_INVALID)
02587                   res = AST_DEVICE_UNKNOWN;
02588             } else if (res == AST_DEVICE_INVALID)  
02589                res = AST_DEVICE_UNAVAILABLE;
02590          }
02591          if (!strcmp(data, p->agent)) {
02592             ast_mutex_unlock(&p->lock);
02593             break;
02594          }
02595       }
02596       ast_mutex_unlock(&p->lock);
02597    }
02598    AST_LIST_UNLOCK(&agents);
02599    return res;
02600 }
02601 
02602 /*!
02603  * \note This function expects the agent list to be locked
02604  */
02605 static struct agent_pvt *find_agent(char *agentid)
02606 {
02607    struct agent_pvt *cur;
02608 
02609    AST_LIST_TRAVERSE(&agents, cur, list) {
02610       if (!strcmp(cur->agent, agentid))
02611          break;   
02612    }
02613 
02614    return cur; 
02615 }
02616 
02617 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
02618 {
02619    char *parse;    
02620    AST_DECLARE_APP_ARGS(args,
02621       AST_APP_ARG(agentid);
02622       AST_APP_ARG(item);
02623    );
02624    char *tmp;
02625    struct agent_pvt *agent;
02626 
02627    buf[0] = '\0';
02628 
02629    if (ast_strlen_zero(data)) {
02630       ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02631       return -1;
02632    }
02633 
02634    parse = ast_strdupa(data);
02635 
02636    AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02637    if (!args.item)
02638       args.item = "status";
02639 
02640    AST_LIST_LOCK(&agents);
02641 
02642    if (!(agent = find_agent(args.agentid))) {
02643       AST_LIST_UNLOCK(&agents);
02644       ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02645       return -1;
02646    }
02647 
02648    if (!strcasecmp(args.item, "status")) {
02649       char *status = "LOGGEDOUT";
02650       if (agent->chan || !ast_strlen_zero(agent->loginchan)) 
02651          status = "LOGGEDIN"; 
02652       ast_copy_string(buf, status, len);
02653    } else if (!strcasecmp(args.item, "password")) 
02654       ast_copy_string(buf, agent->password, len);
02655    else if (!strcasecmp(args.item, "name"))
02656       ast_copy_string(buf, agent->name, len);
02657    else if (!strcasecmp(args.item, "mohclass"))
02658       ast_copy_string(buf, agent->moh, len);
02659    else if (!strcasecmp(args.item, "channel")) {
02660       if (agent->chan) {
02661          ast_copy_string(buf, agent->chan->name, len);
02662          tmp = strrchr(buf, '-');
02663          if (tmp)
02664             *tmp = '\0';
02665       } 
02666    } else if (!strcasecmp(args.item, "exten"))
02667       ast_copy_string(buf, agent->loginchan, len); 
02668 
02669    AST_LIST_UNLOCK(&agents);
02670 
02671    return 0;
02672 }
02673 
02674 struct ast_custom_function agent_function = {
02675    .name = "AGENT",
02676    .synopsis = "Gets information about an Agent",
02677    .syntax = "AGENT(<agentid>[:item])",
02678    .read = function_agent,
02679    .desc = "The valid items to retrieve are:\n"
02680    "- status (default)      The status of the agent\n"
02681    "                          LOGGEDIN | LOGGEDOUT\n"
02682    "- password              The password of the agent\n"
02683    "- name                  The name of the agent\n"
02684    "- mohclass              MusicOnHold class\n"
02685    "- exten                 The callback extension for the Agent (AgentCallbackLogin)\n"
02686    "- channel               The name of the active channel for the Agent (AgentLogin)\n"
02687 };
02688 
02689 
02690 /*!
02691  * \brief Initialize the Agents module.
02692  * This function is being called by Asterisk when loading the module. 
02693  * Among other things it registers applications, cli commands and reads the cofiguration file.
02694  *
02695  * \returns int Always 0.
02696  */
02697 static int load_module(void)
02698 {
02699    /* Make sure we can register our agent channel type */
02700    if (ast_channel_register(&agent_tech)) {
02701       ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
02702       return -1;
02703    }
02704    /* Read in the config */
02705    if (!read_agent_config())
02706       return AST_MODULE_LOAD_DECLINE;
02707    if (persistent_agents)
02708       reload_agents();
02709    /* Dialplan applications */
02710    ast_register_application(app, login_exec, synopsis, descrip);
02711    ast_register_application(app2, callback_exec, synopsis2, descrip2);
02712    ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
02713 
02714    /* Manager commands */
02715    ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
02716    ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
02717    ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
02718 
02719    /* CLI Commands */
02720    ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02721 
02722    /* Dialplan Functions */
02723    ast_custom_function_register(&agent_function);
02724 
02725    return 0;
02726 }
02727 
02728 static int reload(void)
02729 {
02730    read_agent_config();
02731    if (persistent_agents)
02732       reload_agents();
02733    return 0;
02734 }
02735 
02736 static int unload_module(void)
02737 {
02738    struct agent_pvt *p;
02739    /* First, take us out of the channel loop */
02740    ast_channel_unregister(&agent_tech);
02741    /* Unregister dialplan functions */
02742    ast_custom_function_unregister(&agent_function);   
02743    /* Unregister CLI commands */
02744    ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02745    /* Unregister dialplan applications */
02746    ast_unregister_application(app);
02747    ast_unregister_application(app2);
02748    ast_unregister_application(app3);
02749    /* Unregister manager command */
02750    ast_manager_unregister("Agents");
02751    ast_manager_unregister("AgentLogoff");
02752    ast_manager_unregister("AgentCallbackLogin");
02753    /* Unregister channel */
02754    AST_LIST_LOCK(&agents);
02755    /* Hangup all interfaces if they have an owner */
02756    while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
02757       if (p->owner)
02758          ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02759       free(p);
02760    }
02761    AST_LIST_UNLOCK(&agents);
02762    AST_LIST_HEAD_DESTROY(&agents);
02763    return 0;
02764 }
02765 
02766 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
02767       .load = load_module,
02768       .unload = unload_module,
02769       .reload = reload,
02770           );

Generated on Thu Oct 8 21:55:56 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6