Sat Mar 24 23:26:00 2007

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

Generated on Sat Mar 24 23:26:00 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.6