Codename Pineapple

Home page | Mailing list | Docs

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

Asterisk developer's documentation :: Codename Pineapple


chan_agent.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 
00020 /*! \file
00021  * 
00022  * \brief Implementation of Agents (proxy channel)
00023  *
00024  * \author Mark Spencer <markster@digium.com>
00025  *
00026  * This file is the implementation of Agents modules.
00027  * It is a dynamic module that is loaded by Asterisk. 
00028  * \par See also
00029  * \arg \ref Config_agent
00030  *
00031  * \ingroup channel_drivers
00032  */
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 51385 $")
00037 
00038 #include <stdio.h>
00039 #include <string.h>
00040 #include <errno.h>
00041 #include <unistd.h>
00042 #include <sys/socket.h>
00043 #include <stdlib.h>
00044 #include <fcntl.h>
00045 #include <netdb.h>
00046 #include <netinet/in.h>
00047 #include <arpa/inet.h>
00048 #include <sys/signal.h>
00049 
00050 #include "asterisk/lock.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/config.h"
00053 #include "asterisk/logger.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/pbx.h"
00056 #include "asterisk/options.h"
00057 #include "asterisk/lock.h"
00058 #include "asterisk/sched.h"
00059 #include "asterisk/io.h"
00060 #include "asterisk/rtp.h"
00061 #include "asterisk/acl.h"
00062 #include "asterisk/callerid.h"
00063 #include "asterisk/file.h"
00064 #include "asterisk/cli.h"
00065 #include "asterisk/app.h"
00066 #include "asterisk/musiconhold.h"
00067 #include "asterisk/manager.h"
00068 #include "asterisk/features.h"
00069 #include "asterisk/utils.h"
00070 #include "asterisk/causes.h"
00071 #include "asterisk/astdb.h"
00072 #include "asterisk/devicestate.h"
00073 #include "asterisk/monitor.h"
00074 #include "asterisk/stringfields.h"
00075 
00076 static const char tdesc[] = "Call Agent Proxy Channel";
00077 static const char config[] = "agents.conf";
00078 
00079 static const char app[] = "AgentLogin";
00080 static const char app3[] = "AgentMonitorOutgoing";
00081 
00082 static const char synopsis[] = "Call agent login";
00083 static const char synopsis3[] = "Record agent's outgoing call";
00084 
00085 static const char descrip[] =
00086 "  AgentLogin([AgentNo][|options]):\n"
00087 "Asks the agent to login to the system.  Always returns -1.  While\n"
00088 "logged in, the agent can receive calls and will hear a 'beep'\n"
00089 "when a new call comes in. The agent can dump the call by pressing\n"
00090 "the star key.\n"
00091 "The option string may contain zero or more of the following characters:\n"
00092 "      's' -- silent login - do not announce the login ok segment after agent logged in/off\n";
00093 
00094 static const char descrip3[] =
00095 "  AgentMonitorOutgoing([options]):\n"
00096 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
00097 "comparison of the callerid of the current interface and the global variable \n"
00098 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
00099 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
00100 "instead of Monitor application. That have to be configured in the agents.conf file.\n"
00101 "\nReturn value:\n"
00102 "Normally the app returns 0 unless the options are passed. Also if the callerid or\n"
00103 "the agentid are not specified it'll look for n+101 priority.\n"
00104 "\nOptions:\n"
00105 "  'd' - make the app return -1 if there is an error condition and there is\n"
00106 "        no extension n+101\n"
00107 "  'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
00108 "  'n' - don't generate the warnings when there is no callerid or the\n"
00109 "        agentid is not known.\n"
00110 "             It's handy if you want to have one context for agent and non-agent calls.\n";
00111 
00112 static const char mandescr_agents[] =
00113 "Description: Will list info about all possible agents.\n"
00114 "Variables: NONE\n";
00115 
00116 static const char mandescr_agent_logoff[] =
00117 "Description: Sets an agent as no longer logged in.\n"
00118 "Variables: (Names marked with * are required)\n"
00119 "  *Agent: Agent ID of the agent to log off\n"
00120 "  Soft: Set to 'true' to not hangup existing calls\n";
00121 
00122 static const char mandescr_agent_callback_login[] =
00123 "Description: Sets an agent as logged in with callback.\n"
00124 "Variables: (Names marked with * are required)\n"
00125 "  *Agent: Agent ID of the agent to login\n"
00126 "  *Exten: Extension to use for callback\n"
00127 "  Context: Context to use for callback\n"
00128 "  AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n"
00129 "  WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n";
00130 
00131 static char moh[80] = "default";
00132 
00133 #define AST_MAX_AGENT   80                          /*!< Agent ID or Password max length */
00134 #define AST_MAX_BUF  256
00135 #define AST_MAX_FILENAME_LEN  256
00136 
00137 static const char pa_family[] = "/Agents";          /*!< Persistent Agents astdb family */
00138 #define PA_MAX_LEN 2048                             /*!< The maximum length of each persistent member agent database entry */
00139 
00140 static int persistent_agents = 0;                   /*!< queues.conf [general] option */
00141 static void dump_agents(void);
00142 
00143 static ast_group_t group;
00144 static int autologoff;
00145 static int wrapuptime;
00146 static int ackcall;
00147 static int endcall;
00148 static int multiplelogin = 1;
00149 static int autologoffunavail = 0;
00150 
00151 static int maxlogintries = 3;
00152 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
00153 
00154 static int recordagentcalls = 0;
00155 static char recordformat[AST_MAX_BUF] = "";
00156 static char recordformatext[AST_MAX_BUF] = "";
00157 static char urlprefix[AST_MAX_BUF] = "";
00158 static char savecallsin[AST_MAX_BUF] = "";
00159 static int updatecdr = 0;
00160 static char beep[AST_MAX_BUF] = "beep";
00161 
00162 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
00163 
00164 /*! \brief Structure representing an agent.  */
00165 struct agent_pvt {
00166    ast_mutex_t lock;              /*!< Channel private lock */
00167    int dead;                      /*!< Poised for destruction? */
00168    int pending;                   /*!< Not a real agent -- just pending a match */
00169    int abouttograb;               /*!< About to grab */
00170    int autologoff;                /*!< Auto timeout time */
00171    int ackcall;                   /*!< ackcall */
00172    time_t loginstart;             /*!< When agent first logged in (0 when logged off) */
00173    time_t start;                  /*!< When call started */
00174    struct timeval lastdisc;       /*!< When last disconnected */
00175    int wrapuptime;                /*!< Wrapup time in ms */
00176    ast_group_t group;             /*!< Group memberships */
00177    int acknowledged;              /*!< Acknowledged */
00178    char moh[80];                  /*!< Which music on hold */
00179    char agent[AST_MAX_AGENT];     /*!< Agent ID */
00180    char password[AST_MAX_AGENT];  /*!< Password for Agent login */
00181    char name[AST_MAX_AGENT];
00182    ast_mutex_t app_lock;          /**< Synchronization between owning applications */
00183    volatile pthread_t owning_app; /**< Owning application thread id */
00184    volatile int app_sleep_cond;   /**< Sleep condition for the login app */
00185    struct ast_channel *owner;     /**< Agent */
00186    char loginchan[80];            /**< channel they logged in from */
00187    char logincallerid[80];        /**< Caller ID they had when they logged in */
00188    struct ast_channel *chan;      /**< Channel we use */
00189    AST_LIST_ENTRY(agent_pvt) list;  /**< Next Agent in the linked list. */
00190 };
00191 
00192 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
00193 
00194 #define CHECK_FORMATS(ast, p) do { \
00195    if (p->chan) {\
00196       if (ast->nativeformats != p->chan->nativeformats) { \
00197          if (option_debug) \
00198             ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
00199          /* Native formats changed, reset things */ \
00200          ast->nativeformats = p->chan->nativeformats; \
00201          if (option_debug) \
00202             ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
00203          ast_set_read_format(ast, ast->readformat); \
00204          ast_set_write_format(ast, ast->writeformat); \
00205       } \
00206       if (p->chan->readformat != ast->rawreadformat)  \
00207          ast_set_read_format(p->chan, ast->rawreadformat); \
00208       if (p->chan->writeformat != ast->rawwriteformat) \
00209          ast_set_write_format(p->chan, ast->rawwriteformat); \
00210    } \
00211 } while(0)
00212 
00213 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
00214    properly for a timingfd XXX This might need more work if agents were logged in as agents or other
00215    totally impractical combinations XXX */
00216 
00217 #define CLEANUP(ast, p) do { \
00218    int x; \
00219    if (p->chan) { \
00220       for (x=0;x<AST_MAX_FDS;x++) {\
00221          if (x != AST_TIMING_FD) \
00222             ast->fds[x] = p->chan->fds[x]; \
00223       } \
00224       ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
00225    } \
00226 } while(0)
00227 
00228 /*--- Forward declarations */
00229 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
00230 static int agent_devicestate(void *data);
00231 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
00232 static int agent_digit_begin(struct ast_channel *ast, char digit);
00233 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00234 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
00235 static int agent_hangup(struct ast_channel *ast);
00236 static int agent_answer(struct ast_channel *ast);
00237 static struct ast_frame *agent_read(struct ast_channel *ast);
00238 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
00239 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00240 static int agent_sendtext(struct ast_channel *ast, const char *text);
00241 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00242 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00243 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00244 static void set_agentbycallerid(const char *callerid, const char *agent);
00245 
00246 /*! \brief Channel interface description for PBX integration */
00247 static const struct ast_channel_tech agent_tech = {
00248    .type = "Agent",
00249    .description = tdesc,
00250    .capabilities = -1,
00251    .requester = agent_request,
00252    .devicestate = agent_devicestate,
00253    .send_digit_begin = agent_digit_begin,
00254    .send_digit_end = agent_digit_end,
00255    .call = agent_call,
00256    .hangup = agent_hangup,
00257    .answer = agent_answer,
00258    .read = agent_read,
00259    .write = agent_write,
00260    .write_video = agent_write,
00261    .send_html = agent_sendhtml,
00262    .send_text = agent_sendtext,
00263    .exception = agent_read,
00264    .indicate = agent_indicate,
00265    .fixup = agent_fixup,
00266    .bridged_channel = agent_bridgedchannel,
00267 };
00268 
00269 /*!
00270  * Adds an agent to the global list of agents.
00271  *
00272  * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
00273  * \param pending If it is pending or not.
00274  * @return The just created agent.
00275  * \sa agent_pvt, agents.
00276  */
00277 static struct agent_pvt *add_agent(char *agent, int pending)
00278 {
00279    char *parse;
00280    AST_DECLARE_APP_ARGS(args,
00281       AST_APP_ARG(agt);
00282       AST_APP_ARG(password);
00283       AST_APP_ARG(name);
00284    );
00285    char *password = NULL;
00286    char *name = NULL;
00287    char *agt = NULL;
00288    struct agent_pvt *p;
00289 
00290    parse = ast_strdupa(agent);
00291 
00292    /* Extract username (agt), password and name from agent (args). */
00293    AST_NONSTANDARD_APP_ARGS(args, parse, ',');
00294 
00295    if(args.argc == 0) {
00296       ast_log(LOG_WARNING, "A blank agent line!\n");
00297       return NULL;
00298    }
00299 
00300    if(ast_strlen_zero(args.agt) ) {
00301       ast_log(LOG_WARNING, "An agent line with no agentid!\n");
00302       return NULL;
00303    } else
00304       agt = args.agt;
00305 
00306    if(!ast_strlen_zero(args.password)) {
00307       password = args.password;
00308       while (*password && *password < 33) password++;
00309    }
00310    if(!ast_strlen_zero(args.name)) {
00311       name = args.name;
00312       while (*name && *name < 33) name++;
00313    }
00314    
00315    /* Are we searching for the agent here ? To see if it exists already ? */
00316    AST_LIST_TRAVERSE(&agents, p, list) {
00317       if (!pending && !strcmp(p->agent, agt))
00318          break;
00319    }
00320    if (!p) {
00321       // Build the agent.
00322       if (!(p = ast_calloc(1, sizeof(*p))))
00323          return NULL;
00324       ast_copy_string(p->agent, agt, sizeof(p->agent));
00325       ast_mutex_init(&p->lock);
00326       ast_mutex_init(&p->app_lock);
00327       p->owning_app = (pthread_t) -1;
00328       p->app_sleep_cond = 1;
00329       p->group = group;
00330       p->pending = pending;
00331       AST_LIST_INSERT_TAIL(&agents, p, list);
00332    }
00333    
00334    ast_copy_string(p->password, password ? password : "", sizeof(p->password));
00335    ast_copy_string(p->name, name ? name : "", sizeof(p->name));
00336    ast_copy_string(p->moh, moh, sizeof(p->moh));
00337    p->ackcall = ackcall;
00338    p->autologoff = autologoff;
00339 
00340    /* If someone reduces the wrapuptime and reloads, we want it
00341     * to change the wrapuptime immediately on all calls */
00342    if (p->wrapuptime > wrapuptime) {
00343       struct timeval now = ast_tvnow();
00344       /* XXX check what is this exactly */
00345 
00346       /* We won't be pedantic and check the tv_usec val */
00347       if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
00348          p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
00349          p->lastdisc.tv_usec = now.tv_usec;
00350       }
00351    }
00352    p->wrapuptime = wrapuptime;
00353 
00354    if (pending)
00355       p->dead = 1;
00356    else
00357       p->dead = 0;
00358    return p;
00359 }
00360 
00361 /*!
00362  * Deletes an agent after doing some clean up.
00363  * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
00364  * \param p Agent to be deleted.
00365  * \returns Always 0.
00366  */
00367 static int agent_cleanup(struct agent_pvt *p)
00368 {
00369    struct ast_channel *chan = p->owner;
00370    p->owner = NULL;
00371    chan->tech_pvt = NULL;
00372    p->app_sleep_cond = 1;
00373    /* Release ownership of the agent to other threads (presumably running the login app). */
00374    ast_mutex_unlock(&p->app_lock);
00375    if (chan)
00376       ast_channel_free(chan);
00377    if (p->dead) {
00378       ast_mutex_destroy(&p->lock);
00379       ast_mutex_destroy(&p->app_lock);
00380       free(p);
00381         }
00382    return 0;
00383 }
00384 
00385 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
00386 
00387 static int agent_answer(struct ast_channel *ast)
00388 {
00389    ast_log(LOG_WARNING, "Huh?  Agent is being asked to answer?\n");
00390    return -1;
00391 }
00392 
00393 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
00394 {
00395    char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
00396    char filename[AST_MAX_BUF];
00397    int res = -1;
00398    if (!p)
00399       return -1;
00400    if (!ast->monitor) {
00401       snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
00402       /* substitute . for - */
00403       if ((pointer = strchr(filename, '.')))
00404          *pointer = '-';
00405       snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename);
00406       ast_monitor_start(ast, recordformat, tmp, needlock);
00407       ast_monitor_setjoinfiles(ast, 1);
00408       snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext);
00409 #if 0
00410       ast_verbose("name is %s, link is %s\n",tmp, tmp2);
00411 #endif
00412       if (!ast->cdr)
00413          ast->cdr = ast_cdr_alloc();
00414       ast_cdr_setuserfield(ast, tmp2);
00415       res = 0;
00416    } else
00417       ast_log(LOG_ERROR, "Recording already started on that call.\n");
00418    return res;
00419 }
00420 
00421 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
00422 {
00423    return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
00424 }
00425 
00426 static struct ast_frame *agent_read(struct ast_channel *ast)
00427 {
00428    struct agent_pvt *p = ast->tech_pvt;
00429    struct ast_frame *f = NULL;
00430    static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00431    const char *status;
00432    ast_mutex_lock(&p->lock); 
00433    CHECK_FORMATS(ast, p);
00434    if (p->chan) {
00435       ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
00436       p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
00437       f = ast_read(p->chan);
00438    } else
00439       f = &ast_null_frame;
00440    if (!f) {
00441       /* If there's a channel, hang it up (if it's on a callback) make it NULL */
00442       if (p->chan) {
00443          p->chan->_bridge = NULL;
00444          /* Note that we don't hangup if it's not a callback because Asterisk will do it
00445             for us when the PBX instance that called login finishes */
00446          if (!ast_strlen_zero(p->loginchan)) {
00447             if (p->chan && option_debug)
00448                ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name);
00449 
00450             status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00451             if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00452                long logintime = time(NULL) - p->loginstart;
00453                p->loginstart = 0;
00454                ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
00455                agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00456             }
00457             ast_hangup(p->chan);
00458             if (p->wrapuptime && p->acknowledged)
00459                p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00460          }
00461          p->chan = NULL;
00462          p->acknowledged = 0;
00463       }
00464    } else {
00465       /* if acknowledgement is not required, and the channel is up, we may have missed
00466          an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
00467       if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
00468          p->acknowledged = 1;
00469       switch (f->frametype) {
00470       case AST_FRAME_CONTROL:
00471          if (f->subclass == AST_CONTROL_ANSWER) {
00472             if (p->ackcall) {
00473                if (option_verbose > 2)
00474                   ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
00475                /* Don't pass answer along */
00476                ast_frfree(f);
00477                f = &ast_null_frame;
00478             } else {
00479                p->acknowledged = 1;
00480                /* Use the builtin answer frame for the 
00481                   recording start check below. */
00482                ast_frfree(f);
00483                f = &answer_frame;
00484             }
00485          }
00486          break;
00487       case AST_FRAME_DTMF_BEGIN:
00488       case AST_FRAME_DTMF_END:
00489          if (!p->acknowledged && (f->subclass == '#')) {
00490             if (option_verbose > 2)
00491                ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
00492             p->acknowledged = 1;
00493             ast_frfree(f);
00494             f = &answer_frame;
00495          } else if (f->subclass == '*' && endcall) {
00496             /* terminates call */
00497             ast_frfree(f);
00498             f = NULL;
00499          }
00500          break;
00501       case AST_FRAME_VOICE:
00502       case AST_FRAME_VIDEO:
00503          /* don't pass voice or video until the call is acknowledged */
00504          if (!p->acknowledged) {
00505             ast_frfree(f);
00506             f = &ast_null_frame;
00507          }
00508       default:
00509          /* pass everything else on through */
00510          break;
00511       }
00512    }
00513 
00514    CLEANUP(ast,p);
00515    if (p->chan && !p->chan->_bridge) {
00516       if (strcasecmp(p->chan->tech->type, "Local")) {
00517          p->chan->_bridge = ast;
00518          if (p->chan && option_debug)
00519             ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
00520       }
00521    }
00522    ast_mutex_unlock(&p->lock);
00523    if (recordagentcalls && f == &answer_frame)
00524       agent_start_monitoring(ast,0);
00525    return f;
00526 }
00527 
00528 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00529 {
00530    struct agent_pvt *p = ast->tech_pvt;
00531    int res = -1;
00532    ast_mutex_lock(&p->lock);
00533    if (p->chan) 
00534       res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
00535    ast_mutex_unlock(&p->lock);
00536    return res;
00537 }
00538 
00539 static int agent_sendtext(struct ast_channel *ast, const char *text)
00540 {
00541    struct agent_pvt *p = ast->tech_pvt;
00542    int res = -1;
00543    ast_mutex_lock(&p->lock);
00544    if (p->chan) 
00545       res = ast_sendtext(p->chan, text);
00546    ast_mutex_unlock(&p->lock);
00547    return res;
00548 }
00549 
00550 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
00551 {
00552    struct agent_pvt *p = ast->tech_pvt;
00553    int res = -1;
00554    CHECK_FORMATS(ast, p);
00555    ast_mutex_lock(&p->lock);
00556    if (!p->chan) 
00557       res = 0;
00558    else {
00559       if ((f->frametype != AST_FRAME_VOICE) ||
00560           (f->frametype != AST_FRAME_VIDEO) ||
00561           (f->subclass == p->chan->writeformat)) {
00562          res = ast_write(p->chan, f);
00563       } else {
00564          if (option_debug)
00565             ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n", 
00566                f->frametype == AST_FRAME_VOICE ? "audio" : "video",
00567                ast->name, p->chan->name);
00568          res = 0;
00569       }
00570    }
00571    CLEANUP(ast, p);
00572    ast_mutex_unlock(&p->lock);
00573    return res;
00574 }
00575 
00576 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00577 {
00578    struct agent_pvt *p = newchan->tech_pvt;
00579    ast_mutex_lock(&p->lock);
00580    if (p->owner != oldchan) {
00581       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
00582       ast_mutex_unlock(&p->lock);
00583       return -1;
00584    }
00585    p->owner = newchan;
00586    ast_mutex_unlock(&p->lock);
00587    return 0;
00588 }
00589 
00590 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00591 {
00592    struct agent_pvt *p = ast->tech_pvt;
00593    int res = -1;
00594    ast_mutex_lock(&p->lock);
00595    if (p->chan)
00596       res = ast_indicate_data(p->chan, condition, data, datalen);
00597    else
00598       res = 0;
00599    ast_mutex_unlock(&p->lock);
00600    return res;
00601 }
00602 
00603 static int agent_digit_begin(struct ast_channel *ast, char digit)
00604 {
00605    struct agent_pvt *p = ast->tech_pvt;
00606    int res = -1;
00607    ast_mutex_lock(&p->lock);
00608    ast_senddigit_begin(p->chan, digit);
00609    ast_mutex_unlock(&p->lock);
00610    return res;
00611 }
00612 
00613 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00614 {
00615    struct agent_pvt *p = ast->tech_pvt;
00616    int res = -1;
00617    ast_mutex_lock(&p->lock);
00618    ast_senddigit_end(p->chan, digit, duration);
00619    ast_mutex_unlock(&p->lock);
00620    return res;
00621 }
00622 
00623 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
00624 {
00625    struct agent_pvt *p = ast->tech_pvt;
00626    int res = -1;
00627    int newstate=0;
00628    ast_mutex_lock(&p->lock);
00629    p->acknowledged = 0;
00630    if (!p->chan) {
00631       if (p->pending) {
00632          if (option_debug)
00633             ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
00634          newstate = AST_STATE_DIALING;
00635          res = 0;
00636       } else {
00637          ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call...  what are the odds of that?\n");
00638          res = -1;
00639       }
00640       ast_mutex_unlock(&p->lock);
00641       if (newstate)
00642          ast_setstate(ast, newstate);
00643       return res;
00644    } else if (!ast_strlen_zero(p->loginchan)) {
00645       time(&p->start);
00646       /* Call on this agent */
00647       if (option_verbose > 2)
00648          ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
00649       ast_set_callerid(p->chan,
00650          ast->cid.cid_num, ast->cid.cid_name, NULL);
00651       ast_channel_inherit_variables(ast, p->chan);
00652       res = ast_call(p->chan, p->loginchan, 0);
00653       CLEANUP(ast,p);
00654       ast_mutex_unlock(&p->lock);
00655       return res;
00656    }
00657    ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
00658    if (option_debug > 2)
00659       ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language);
00660    res = ast_streamfile(p->chan, beep, p->chan->language);
00661    if (option_debug > 2)
00662       ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
00663    if (!res) {
00664       res = ast_waitstream(p->chan, "");
00665       if (option_debug > 2)
00666          ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
00667    }
00668    if (!res) {
00669       res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
00670       if (option_debug > 2)
00671          ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res);
00672       if (res)
00673          ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00674    } else {
00675       /* Agent hung-up */
00676       p->chan = NULL;
00677    }
00678 
00679    if (!res) {
00680       res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
00681       if (option_debug > 2)
00682          ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res);
00683       if (res)
00684          ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00685    }
00686    if(!res) {
00687       /* Call is immediately up, or might need ack */
00688       if (p->ackcall > 1)
00689          newstate = AST_STATE_RINGING;
00690       else {
00691          newstate = AST_STATE_UP;
00692          if (recordagentcalls)
00693             agent_start_monitoring(ast, 0);
00694          p->acknowledged = 1;
00695       }
00696       res = 0;
00697    }
00698    CLEANUP(ast, p);
00699    ast_mutex_unlock(&p->lock);
00700    if (newstate)
00701       ast_setstate(ast, newstate);
00702    return res;
00703 }
00704 
00705 /*! \brief store/clear the global variable that stores agentid based on the callerid */
00706 static void set_agentbycallerid(const char *callerid, const char *agent)
00707 {
00708    char buf[AST_MAX_BUF];
00709 
00710    /* if there is no Caller ID, nothing to do */
00711    if (ast_strlen_zero(callerid))
00712       return;
00713 
00714    snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
00715    pbx_builtin_setvar_helper(NULL, buf, agent);
00716 }
00717 
00718 static int agent_hangup(struct ast_channel *ast)
00719 {
00720    struct agent_pvt *p = ast->tech_pvt;
00721    int howlong = 0;
00722    const char *status;
00723    ast_mutex_lock(&p->lock);
00724    p->owner = NULL;
00725    ast->tech_pvt = NULL;
00726    p->app_sleep_cond = 1;
00727    p->acknowledged = 0;
00728 
00729    /* if they really are hung up then set start to 0 so the test
00730     * later if we're called on an already downed channel
00731     * doesn't cause an agent to be logged out like when
00732     * agent_request() is followed immediately by agent_hangup()
00733     * as in apps/app_chanisavail.c:chanavail_exec()
00734     */
00735 
00736    if (option_debug)
00737       ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state));
00738    if (p->start && (ast->_state != AST_STATE_UP)) {
00739       howlong = time(NULL) - p->start;
00740       p->start = 0;
00741    } else if (ast->_state == AST_STATE_RESERVED) 
00742       howlong = 0;
00743    else
00744       p->start = 0; 
00745    if (p->chan) {
00746       p->chan->_bridge = NULL;
00747       /* If they're dead, go ahead and hang up on the agent now */
00748       if (!ast_strlen_zero(p->loginchan)) {
00749          /* Store last disconnect time */
00750          if (p->wrapuptime)
00751             p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00752          else
00753             p->lastdisc = ast_tv(0,0);
00754          if (p->chan) {
00755             status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00756             if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00757                long logintime = time(NULL) - p->loginstart;
00758                p->loginstart = 0;
00759                ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
00760                agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00761             }
00762             /* Recognize the hangup and pass it along immediately */
00763             ast_hangup(p->chan);
00764             p->chan = NULL;
00765          }
00766          if (option_debug)
00767             ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
00768          if (howlong  && p->autologoff && (howlong > p->autologoff)) {
00769             long logintime = time(NULL) - p->loginstart;
00770             p->loginstart = 0;
00771             ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00772             agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
00773          }
00774       } else if (p->dead) {
00775          ast_channel_lock(p->chan);
00776          ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00777          ast_channel_unlock(p->chan);
00778       } else if (p->loginstart) {
00779          ast_channel_lock(p->chan);
00780          ast_indicate_data(p->chan, AST_CONTROL_HOLD, 
00781             S_OR(p->moh, NULL),
00782             !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
00783          ast_channel_unlock(p->chan);
00784       }
00785    }
00786    ast_mutex_unlock(&p->lock);
00787    /* Only register a device state change if the agent is still logged in */
00788    if (p->loginstart)
00789       ast_device_state_changed("Agent/%s", p->agent);
00790 
00791    if (p->p