Sat Nov 1 06:28:23 2008

Asterisk developer's documentation


chan_agent.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 
00020 /*! \file
00021  * 
00022  * \brief Implementation of Agents (proxy channel)
00023  *
00024  * \author Mark Spencer <markster@digium.com>
00025  *
00026  * This file is the implementation of Agents modules.
00027  * It is a dynamic module that is loaded by Asterisk. 
00028  * \par See also
00029  * \arg \ref Config_agent
00030  *
00031  * \ingroup channel_drivers
00032  */
00033 /*** MODULEINFO
00034         <depend>chan_local</depend>
00035  ***/
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 141366 $")
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    int inherited_devicestate;     /*!< Does the underlying channel have a devicestate to pass? */
00197    ast_mutex_t app_lock;          /**< Synchronization between owning applications */
00198    volatile pthread_t owning_app; /**< Owning application thread id */
00199    volatile int app_sleep_cond;   /**< Sleep condition for the login app */
00200    struct ast_channel *owner;     /**< Agent */
00201    char loginchan[80];            /**< channel they logged in from */
00202    char logincallerid[80];        /**< Caller ID they had when they logged in */
00203    struct ast_channel *chan;      /**< Channel we use */
00204    AST_LIST_ENTRY(agent_pvt) list;  /**< Next Agent in the linked list. */
00205 };
00206 
00207 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
00208 
00209 #define CHECK_FORMATS(ast, p) do { \
00210    if (p->chan) {\
00211       if (ast->nativeformats != p->chan->nativeformats) { \
00212          ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
00213          /* Native formats changed, reset things */ \
00214          ast->nativeformats = p->chan->nativeformats; \
00215          ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
00216          ast_set_read_format(ast, ast->readformat); \
00217          ast_set_write_format(ast, ast->writeformat); \
00218       } \
00219       if (p->chan->readformat != ast->rawreadformat && !p->chan->generator)  \
00220          ast_set_read_format(p->chan, ast->rawreadformat); \
00221       if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
00222          ast_set_write_format(p->chan, ast->rawwriteformat); \
00223    } \
00224 } while(0)
00225 
00226 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
00227    properly for a timingfd XXX This might need more work if agents were logged in as agents or other
00228    totally impractical combinations XXX */
00229 
00230 #define CLEANUP(ast, p) do { \
00231    int x; \
00232    if (p->chan) { \
00233       for (x=0;x<AST_MAX_FDS;x++) {\
00234          if (x != AST_TIMING_FD) \
00235             ast->fds[x] = p->chan->fds[x]; \
00236       } \
00237       ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
00238    } \
00239 } while(0)
00240 
00241 /*--- Forward declarations */
00242 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
00243 static int agent_devicestate(void *data);
00244 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
00245 static int agent_digit_begin(struct ast_channel *ast, char digit);
00246 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00247 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
00248 static int agent_hangup(struct ast_channel *ast);
00249 static int agent_answer(struct ast_channel *ast);
00250 static struct ast_frame *agent_read(struct ast_channel *ast);
00251 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
00252 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00253 static int agent_sendtext(struct ast_channel *ast, const char *text);
00254 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00255 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00256 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00257 static void set_agentbycallerid(const char *callerid, const char *agent);
00258 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
00259 static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
00260 
00261 /*! \brief Channel interface description for PBX integration */
00262 static const struct ast_channel_tech agent_tech = {
00263    .type = "Agent",
00264    .description = tdesc,
00265    .capabilities = -1,
00266    .requester = agent_request,
00267    .devicestate = agent_devicestate,
00268    .send_digit_begin = agent_digit_begin,
00269    .send_digit_end = agent_digit_end,
00270    .call = agent_call,
00271    .hangup = agent_hangup,
00272    .answer = agent_answer,
00273    .read = agent_read,
00274    .write = agent_write,
00275    .write_video = agent_write,
00276    .send_html = agent_sendhtml,
00277    .send_text = agent_sendtext,
00278    .exception = agent_read,
00279    .indicate = agent_indicate,
00280    .fixup = agent_fixup,
00281    .bridged_channel = agent_bridgedchannel,
00282    .get_base_channel = agent_get_base_channel,
00283    .set_base_channel = agent_set_base_channel,
00284 };
00285 
00286 static int agent_devicestate_cb(const char *dev, int state, void *data)
00287 {
00288    int res, i;
00289    struct agent_pvt *p;
00290    char basename[AST_CHANNEL_NAME], *tmp;
00291 
00292    /* Skip Agent status */
00293    if (!strncasecmp(dev, "Agent/", 6)) {
00294       return 0;
00295    }
00296 
00297    /* Try to be safe, but don't deadlock */
00298    for (i = 0; i < 10; i++) {
00299       if ((res = AST_LIST_TRYLOCK(&agents)) == 0) {
00300          break;
00301       }
00302    }
00303    if (res) {
00304       return -1;
00305    }
00306 
00307    AST_LIST_TRAVERSE(&agents, p, list) {
00308       ast_mutex_lock(&p->lock);
00309       if (p->chan) {
00310          ast_copy_string(basename, p->chan->name, sizeof(basename));
00311          if ((tmp = strrchr(basename, '-'))) {
00312             *tmp = '\0';
00313          }
00314          if (strcasecmp(p->chan->name, dev) == 0 || strcasecmp(basename, dev) == 0) {
00315             p->inherited_devicestate = state;
00316             ast_device_state_changed("Agent/%s", p->agent);
00317          }
00318       }
00319       ast_mutex_unlock(&p->lock);
00320    }
00321    AST_LIST_UNLOCK(&agents);
00322    return 0;
00323 }
00324 
00325 /*!
00326  * Adds an agent to the global list of agents.
00327  *
00328  * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
00329  * \param pending If it is pending or not.
00330  * @return The just created agent.
00331  * \sa agent_pvt, agents.
00332  */
00333 static struct agent_pvt *add_agent(char *agent, int pending)
00334 {
00335    char *parse;
00336    AST_DECLARE_APP_ARGS(args,
00337       AST_APP_ARG(agt);
00338       AST_APP_ARG(password);
00339       AST_APP_ARG(name);
00340    );
00341    char *password = NULL;
00342    char *name = NULL;
00343    char *agt = NULL;
00344    struct agent_pvt *p;
00345 
00346    parse = ast_strdupa(agent);
00347 
00348    /* Extract username (agt), password and name from agent (args). */
00349    AST_NONSTANDARD_APP_ARGS(args, parse, ',');
00350 
00351    if(args.argc == 0) {
00352       ast_log(LOG_WARNING, "A blank agent line!\n");
00353       return NULL;
00354    }
00355 
00356    if(ast_strlen_zero(args.agt) ) {
00357       ast_log(LOG_WARNING, "An agent line with no agentid!\n");
00358       return NULL;
00359    } else
00360       agt = args.agt;
00361 
00362    if(!ast_strlen_zero(args.password)) {
00363       password = args.password;
00364       while (*password && *password < 33) password++;
00365    }
00366    if(!ast_strlen_zero(args.name)) {
00367       name = args.name;
00368       while (*name && *name < 33) name++;
00369    }
00370    
00371    /* Are we searching for the agent here ? To see if it exists already ? */
00372    AST_LIST_TRAVERSE(&agents, p, list) {
00373       if (!pending && !strcmp(p->agent, agt))
00374          break;
00375    }
00376    if (!p) {
00377       // Build the agent.
00378       if (!(p = ast_calloc(1, sizeof(*p))))
00379          return NULL;
00380       ast_copy_string(p->agent, agt, sizeof(p->agent));
00381       ast_mutex_init(&p->lock);
00382       ast_mutex_init(&p->app_lock);
00383       p->owning_app = (pthread_t) -1;
00384       p->app_sleep_cond = 1;
00385       p->group = group;
00386       p->pending = pending;
00387       p->inherited_devicestate = -1;
00388       AST_LIST_INSERT_TAIL(&agents, p, list);
00389    }
00390    
00391    ast_copy_string(p->password, password ? password : "", sizeof(p->password));
00392    ast_copy_string(p->name, name ? name : "", sizeof(p->name));
00393    ast_copy_string(p->moh, moh, sizeof(p->moh));
00394    p->ackcall = ackcall;
00395    p->autologoff = autologoff;
00396 
00397    /* If someone reduces the wrapuptime and reloads, we want it
00398     * to change the wrapuptime immediately on all calls */
00399    if (p->wrapuptime > wrapuptime) {
00400       struct timeval now = ast_tvnow();
00401       /* XXX check what is this exactly */
00402 
00403       /* We won't be pedantic and check the tv_usec val */
00404       if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
00405          p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
00406          p->lastdisc.tv_usec = now.tv_usec;
00407       }
00408    }
00409    p->wrapuptime = wrapuptime;
00410 
00411    if (pending)
00412       p->dead = 1;
00413    else
00414       p->dead = 0;
00415    return p;
00416 }
00417 
00418 /*!
00419  * Deletes an agent after doing some clean up.
00420  * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
00421  * \param p Agent to be deleted.
00422  * \returns Always 0.
00423  */
00424 static int agent_cleanup(struct agent_pvt *p)
00425 {
00426    struct ast_channel *chan = p->owner;
00427    p->owner = NULL;
00428    chan->tech_pvt = NULL;
00429    p->app_sleep_cond = 1;
00430    /* Release ownership of the agent to other threads (presumably running the login app). */
00431    ast_mutex_unlock(&p->app_lock);
00432    if (chan)
00433       ast_channel_free(chan);
00434    if (p->dead) {
00435       ast_mutex_destroy(&p->lock);
00436       ast_mutex_destroy(&p->app_lock);
00437       free(p);
00438         }
00439    return 0;
00440 }
00441 
00442 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
00443 
00444 static int agent_answer(struct ast_channel *ast)
00445 {
00446    ast_log(LOG_WARNING, "Huh?  Agent is being asked to answer?\n");
00447    return -1;
00448 }
00449 
00450 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
00451 {
00452    char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
00453    char filename[AST_MAX_BUF];
00454    int res = -1;
00455    if (!p)
00456       return -1;
00457    if (!ast->monitor) {
00458       snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
00459       /* substitute . for - */
00460       if ((pointer = strchr(filename, '.')))
00461          *pointer = '-';
00462       snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
00463       ast_monitor_start(ast, recordformat, tmp, needlock);
00464       ast_monitor_setjoinfiles(ast, 1);
00465       snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
00466 #if 0
00467       ast_verbose("name is %s, link is %s\n",tmp, tmp2);
00468 #endif
00469       if (!ast->cdr)
00470          ast->cdr = ast_cdr_alloc();
00471       ast_cdr_setuserfield(ast, tmp2);
00472       res = 0;
00473    } else
00474       ast_log(LOG_ERROR, "Recording already started on that call.\n");
00475    return res;
00476 }
00477 
00478 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
00479 {
00480    return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
00481 }
00482 
00483 static struct ast_frame *agent_read(struct ast_channel *ast)
00484 {
00485    struct agent_pvt *p = ast->tech_pvt;
00486    struct ast_frame *f = NULL;
00487    static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00488    const char *status;
00489    ast_mutex_lock(&p->lock); 
00490    CHECK_FORMATS(ast, p);
00491    if (p->chan) {
00492       ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
00493       p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
00494       f = ast_read(p->chan);
00495    } else
00496       f = &ast_null_frame;
00497    if (!f) {
00498       /* If there's a channel, hang it up (if it's on a callback) make it NULL */
00499       if (p->chan) {
00500          p->chan->_bridge = NULL;
00501          /* Note that we don't hangup if it's not a callback because Asterisk will do it
00502             for us when the PBX instance that called login finishes */
00503          if (!ast_strlen_zero(p->loginchan)) {
00504             if (p->chan)
00505                ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name);
00506             if (p->owner->_state != AST_STATE_UP) {
00507                int howlong = time(NULL) - p->start;
00508                if (p->autologoff && howlong > p->autologoff) {
00509                   long logintime = time(NULL) - p->loginstart;
00510                   p->loginstart = 0;
00511                      ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00512                   agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
00513                   if (persistent_agents)
00514                      dump_agents();
00515                }
00516             }
00517             status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00518             if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00519                long logintime = time(NULL) - p->loginstart;
00520                p->loginstart = 0;
00521                ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
00522                agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00523             }
00524             ast_hangup(p->chan);
00525             if (p->wrapuptime && p->acknowledged)
00526                p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00527          }
00528          p->chan = NULL;
00529          p->inherited_devicestate = -1;
00530          ast_device_state_changed("Agent/%s", p->agent);
00531          p->acknowledged = 0;
00532       }
00533    } else {
00534       /* if acknowledgement is not required, and the channel is up, we may have missed
00535          an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
00536       if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
00537          p->acknowledged = 1;
00538       switch (f->frametype) {
00539       case AST_FRAME_CONTROL:
00540          if (f->subclass == AST_CONTROL_ANSWER) {
00541             if (p->ackcall) {
00542                if (option_verbose > 2)
00543                   ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
00544                /* Don't pass answer along */
00545                ast_frfree(f);
00546                f = &ast_null_frame;
00547             } else {
00548                p->acknowledged = 1;
00549                /* Use the builtin answer frame for the 
00550                   recording start check below. */
00551                ast_frfree(f);
00552                f = &answer_frame;
00553             }
00554          }
00555          break;
00556       case AST_FRAME_DTMF_BEGIN:
00557          /*ignore DTMF begin's as it can cause issues with queue announce files*/
00558          if((!p->acknowledged && f->subclass == '#') || (f->subclass == '*' && endcall)){
00559             ast_frfree(f);
00560             f = &ast_null_frame;
00561          }
00562          break;
00563       case AST_FRAME_DTMF_END:
00564          if (!p->acknowledged && (f->subclass == '#')) {
00565             if (option_verbose > 2)
00566                ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
00567             p->acknowledged = 1;
00568             ast_frfree(f);
00569             f = &answer_frame;
00570          } else if (f->subclass == '*' && endcall) {
00571             /* terminates call */
00572             ast_frfree(f);
00573             f = NULL;
00574          }
00575          break;
00576       case AST_FRAME_VOICE:
00577       case AST_FRAME_VIDEO:
00578          /* don't pass voice or video until the call is acknowledged */
00579          if (!p->acknowledged) {
00580             ast_frfree(f);
00581             f = &ast_null_frame;
00582          }
00583       default:
00584          /* pass everything else on through */
00585          break;
00586       }
00587    }
00588 
00589    CLEANUP(ast,p);
00590    if (p->chan && !p->chan->_bridge) {
00591       if (strcasecmp(p->chan->tech->type, "Local")) {
00592          p->chan->_bridge = ast;
00593          if (p->chan)
00594             ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
00595       }
00596    }
00597    ast_mutex_unlock(&p->lock);
00598    if (recordagentcalls && f == &answer_frame)
00599       agent_start_monitoring(ast,0);
00600    return f;
00601 }
00602 
00603 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00604 {
00605    struct agent_pvt *p = ast->tech_pvt;
00606    int res = -1;
00607    ast_mutex_lock(&p->lock);
00608    if (p->chan) 
00609       res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
00610    ast_mutex_unlock(&p->lock);
00611    return res;
00612 }
00613 
00614 static int agent_sendtext(struct ast_channel *ast, const char *text)
00615 {
00616    struct agent_pvt *p = ast->tech_pvt;
00617    int res = -1;
00618    ast_mutex_lock(&p->lock);
00619    if (p->chan) 
00620       res = ast_sendtext(p->chan, text);
00621    ast_mutex_unlock(&p->lock);
00622    return res;
00623 }
00624 
00625 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
00626 {
00627    struct agent_pvt *p = ast->tech_pvt;
00628    int res = -1;
00629    CHECK_FORMATS(ast, p);
00630    ast_mutex_lock(&p->lock);
00631    if (!p->chan) 
00632       res = 0;
00633    else {
00634       if ((f->frametype != AST_FRAME_VOICE) ||
00635           (f->frametype != AST_FRAME_VIDEO) ||
00636           (f->subclass == p->chan->writeformat)) {
00637          res = ast_write(p->chan, f);
00638       } else {
00639          ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n", 
00640             f->frametype == AST_FRAME_VOICE ? "audio" : "video",
00641             ast->name, p->chan->name);
00642          res = 0;
00643       }
00644    }
00645    CLEANUP(ast, p);
00646    ast_mutex_unlock(&p->lock);
00647    return res;
00648 }
00649 
00650 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00651 {
00652    struct agent_pvt *p = newchan->tech_pvt;
00653    ast_mutex_lock(&p->lock);
00654    if (p->owner != oldchan) {
00655       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
00656       ast_mutex_unlock(&p->lock);
00657       return -1;
00658    }
00659    p->owner = newchan;
00660    ast_mutex_unlock(&p->lock);
00661    return 0;
00662 }
00663 
00664 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00665 {
00666    struct agent_pvt *p = ast->tech_pvt;
00667    int res = -1;
00668    ast_mutex_lock(&p->lock);
00669    if (p->chan && !ast_check_hangup(p->chan))
00670       res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
00671    else
00672       res = 0;
00673    ast_mutex_unlock(&p->lock);
00674    return res;
00675 }
00676 
00677 static int agent_digit_begin(struct ast_channel *ast, char digit)
00678 {
00679    struct agent_pvt *p = ast->tech_pvt;
00680    ast_mutex_lock(&p->lock);
00681    if (p->chan) {
00682       ast_senddigit_begin(p->chan, digit);
00683    }
00684    ast_mutex_unlock(&p->lock);
00685    return 0;
00686 }
00687 
00688 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00689 {
00690    struct agent_pvt *p = ast->tech_pvt;
00691    ast_mutex_lock(&p->lock);
00692    if (p->chan) {
00693       ast_senddigit_end(p->chan, digit, duration);
00694    }
00695    ast_mutex_unlock(&p->lock);
00696    return 0;
00697 }
00698 
00699 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
00700 {
00701    struct agent_pvt *p = ast->tech_pvt;
00702    int res = -1;
00703    int newstate=0;
00704    ast_mutex_lock(&p->lock);
00705    p->acknowledged = 0;
00706    if (!p->chan) {
00707       if (p->pending) {
00708          ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
00709          newstate = AST_STATE_DIALING;
00710          res = 0;
00711       } else {
00712          ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call...  what are the odds of that?\n");
00713          res = -1;
00714       }
00715       ast_mutex_unlock(&p->lock);
00716       if (newstate)
00717          ast_setstate(ast, newstate);
00718       return res;
00719    } else if (!ast_strlen_zero(p->loginchan)) {
00720       time(&p->start);
00721       /* Call on this agent */
00722       if (option_verbose > 2)
00723          ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
00724       ast_set_callerid(p->chan,
00725          ast->cid.cid_num, ast->cid.cid_name, NULL);
00726       ast_channel_inherit_variables(ast, p->chan);
00727       res = ast_call(p->chan, p->loginchan, 0);
00728       CLEANUP(ast,p);
00729       ast_mutex_unlock(&p->lock);
00730       return res;
00731    }
00732    ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
00733    if (option_debug > 2)
00734       ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language);
00735    res = ast_streamfile(p->chan, beep, p->chan->language);
00736    if (option_debug > 2)
00737       ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
00738    if (!res) {
00739       res = ast_waitstream(p->chan, "");
00740       if (option_debug > 2)
00741          ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
00742    }
00743    if (!res) {
00744       res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
00745       if (option_debug > 2)
00746          ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res);
00747       if (res)
00748          ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00749    } else {
00750       /* Agent hung-up */
00751       p->chan = NULL;
00752       p->inherited_devicestate = -1;
00753       ast_device_state_changed("Agent/%s", p->agent);
00754    }
00755 
00756    if (!res) {
00757       res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
00758       if (option_debug > 2)
00759          ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res);
00760       if (res)
00761          ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00762    }
00763    if(!res) {
00764       /* Call is immediately up, or might need ack */
00765       if (p->ackcall > 1)
00766          newstate = AST_STATE_RINGING;
00767       else {
00768          newstate = AST_STATE_UP;
00769          if (recordagentcalls)
00770             agent_start_monitoring(ast, 0);
00771          p->acknowledged = 1;
00772       }
00773       res = 0;
00774    }
00775    CLEANUP(ast, p);
00776    ast_mutex_unlock(&p->lock);
00777    if (newstate)
00778       ast_setstate(ast, newstate);
00779    return res;
00780 }
00781 
00782 /*! \brief store/clear the global variable that stores agentid based on the callerid */
00783 static void set_agentbycallerid(const char *callerid, const char *agent)
00784 {
00785    char buf[AST_MAX_BUF];
00786 
00787    /* if there is no Caller ID, nothing to do */
00788    if (ast_strlen_zero(callerid))
00789       return;
00790 
00791    snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
00792    pbx_builtin_setvar_helper(NULL, buf, agent);