Wed Jan 8 2020 09:49:43

Asterisk developer's documentation


chan_agent.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2012, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 
20 /*! \file
21  *
22  * \brief Implementation of Agents (proxy channel)
23  *
24  * \author Mark Spencer <markster@digium.com>
25  *
26  * This file is the implementation of Agents modules.
27  * It is a dynamic module that is loaded by Asterisk.
28  * \par See also
29  * \arg \ref Config_agent
30  *
31  * \ingroup channel_drivers
32  */
33 /*** MODULEINFO
34  <depend>chan_local</depend>
35  <depend>res_monitor</depend>
36  <support_level>core</support_level>
37  ***/
38 
39 #include "asterisk.h"
40 
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 380364 $")
42 
43 #include <sys/socket.h>
44 #include <fcntl.h>
45 #include <netdb.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <sys/signal.h>
49 
50 #include "asterisk/lock.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/config.h"
53 #include "asterisk/module.h"
54 #include "asterisk/pbx.h"
55 #include "asterisk/sched.h"
56 #include "asterisk/io.h"
57 #include "asterisk/acl.h"
58 #include "asterisk/callerid.h"
59 #include "asterisk/file.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/app.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/features.h"
65 #include "asterisk/utils.h"
66 #include "asterisk/causes.h"
67 #include "asterisk/astdb.h"
68 #include "asterisk/devicestate.h"
69 #include "asterisk/monitor.h"
70 #include "asterisk/stringfields.h"
71 #include "asterisk/event.h"
72 #include "asterisk/data.h"
73 
74 /*** DOCUMENTATION
75  <application name="AgentLogin" language="en_US">
76  <synopsis>
77  Call agent login.
78  </synopsis>
79  <syntax>
80  <parameter name="AgentNo" />
81  <parameter name="options">
82  <optionlist>
83  <option name="s">
84  <para>silent login - do not announce the login ok segment after
85  agent logged on/off</para>
86  </option>
87  </optionlist>
88  </parameter>
89  </syntax>
90  <description>
91  <para>Asks the agent to login to the system. Always returns <literal>-1</literal>.
92  While logged in, the agent can receive calls and will hear a <literal>beep</literal>
93  when a new call comes in. The agent can dump the call by pressing the star key.</para>
94  </description>
95  <see-also>
96  <ref type="application">Queue</ref>
97  <ref type="application">AddQueueMember</ref>
98  <ref type="application">RemoveQueueMember</ref>
99  <ref type="application">PauseQueueMember</ref>
100  <ref type="application">UnpauseQueueMember</ref>
101  <ref type="function">AGENT</ref>
102  <ref type="filename">agents.conf</ref>
103  <ref type="filename">queues.conf</ref>
104  </see-also>
105  </application>
106  <application name="AgentMonitorOutgoing" language="en_US">
107  <synopsis>
108  Record agent's outgoing call.
109  </synopsis>
110  <syntax>
111  <parameter name="options">
112  <optionlist>
113  <option name="d">
114  <para>make the app return <literal>-1</literal> if there is an error condition.</para>
115  </option>
116  <option name="c">
117  <para>change the CDR so that the source of the call is
118  <literal>Agent/agent_id</literal></para>
119  </option>
120  <option name="n">
121  <para>don't generate the warnings when there is no callerid or the
122  agentid is not known. It's handy if you want to have one context
123  for agent and non-agent calls.</para>
124  </option>
125  </optionlist>
126  </parameter>
127  </syntax>
128  <description>
129  <para>Tries to figure out the id of the agent who is placing outgoing call based on
130  comparison of the callerid of the current interface and the global variable
131  placed by the AgentCallbackLogin application. That's why it should be used only
132  with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent
133  instead of Monitor application. That has to be configured in the
134  <filename>agents.conf</filename> file.</para>
135  <para>Normally the app returns <literal>0</literal> unless the options are passed.</para>
136  </description>
137  <see-also>
138  <ref type="filename">agents.conf</ref>
139  </see-also>
140  </application>
141  <function name="AGENT" language="en_US">
142  <synopsis>
143  Gets information about an Agent
144  </synopsis>
145  <syntax argsep=":">
146  <parameter name="agentid" required="true" />
147  <parameter name="item">
148  <para>The valid items to retrieve are:</para>
149  <enumlist>
150  <enum name="status">
151  <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
152  </enum>
153  <enum name="password">
154  <para>The password of the agent</para>
155  </enum>
156  <enum name="name">
157  <para>The name of the agent</para>
158  </enum>
159  <enum name="mohclass">
160  <para>MusicOnHold class</para>
161  </enum>
162  <enum name="channel">
163  <para>The name of the active channel for the Agent (AgentLogin)</para>
164  </enum>
165  <enum name="fullchannel">
166  <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
167  </enum>
168  </enumlist>
169  </parameter>
170  </syntax>
171  <description></description>
172  </function>
173  <manager name="Agents" language="en_US">
174  <synopsis>
175  Lists agents and their status.
176  </synopsis>
177  <syntax>
178  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
179  </syntax>
180  <description>
181  <para>Will list info about all possible agents.</para>
182  </description>
183  </manager>
184  <manager name="AgentLogoff" language="en_US">
185  <synopsis>
186  Sets an agent as no longer logged in.
187  </synopsis>
188  <syntax>
189  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
190  <parameter name="Agent" required="true">
191  <para>Agent ID of the agent to log off.</para>
192  </parameter>
193  <parameter name="Soft">
194  <para>Set to <literal>true</literal> to not hangup existing calls.</para>
195  </parameter>
196  </syntax>
197  <description>
198  <para>Sets an agent as no longer logged in.</para>
199  </description>
200  </manager>
201  ***/
202 
203 static const char tdesc[] = "Call Agent Proxy Channel";
204 static const char config[] = "agents.conf";
205 
206 static const char app[] = "AgentLogin";
207 static const char app3[] = "AgentMonitorOutgoing";
208 
209 static char moh[80] = "default";
210 
211 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
212 #define AST_MAX_BUF 256
213 #define AST_MAX_FILENAME_LEN 256
214 
215 static const char pa_family[] = "Agents"; /*!< Persistent Agents astdb family */
216 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
217 
218 #define DEFAULT_ACCEPTDTMF '#'
219 #define DEFAULT_ENDDTMF '*'
220 
222 static int autologoff;
223 static int wrapuptime;
224 static int ackcall;
225 static int endcall;
226 static int autologoffunavail = 0;
228 static char enddtmf = DEFAULT_ENDDTMF;
229 
230 static int maxlogintries = 3;
231 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
232 
233 static int recordagentcalls = 0;
234 static char recordformat[AST_MAX_BUF] = "";
235 static char recordformatext[AST_MAX_BUF] = "";
236 static char urlprefix[AST_MAX_BUF] = "";
237 static char savecallsin[AST_MAX_BUF] = "";
238 static int updatecdr = 0;
239 static char beep[AST_MAX_BUF] = "beep";
240 
241 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
242 
243 enum {
244  AGENT_FLAG_ACKCALL = (1 << 0),
248  AGENT_FLAG_ENDDTMF = (1 << 4),
249 };
250 
251 /*! \brief Structure representing an agent. */
252 struct agent_pvt {
253  ast_mutex_t lock; /*!< Channel private lock */
254  int dead; /*!< Poised for destruction? */
255  int pending; /*!< Not a real agent -- just pending a match */
256  int abouttograb; /*!< About to grab */
257  int autologoff; /*!< Auto timeout time */
258  int ackcall; /*!< ackcall */
259  int deferlogoff; /*!< Defer logoff to hangup */
261  char enddtmf;
262  time_t loginstart; /*!< When agent first logged in (0 when logged off) */
263  time_t start; /*!< When call started */
264  struct timeval lastdisc; /*!< When last disconnected */
265  int wrapuptime; /*!< Wrapup time in ms */
266  ast_group_t group; /*!< Group memberships */
267  int acknowledged; /*!< Acknowledged */
268  char moh[80]; /*!< Which music on hold */
269  char agent[AST_MAX_AGENT]; /*!< Agent ID */
270  char password[AST_MAX_AGENT]; /*!< Password for Agent login */
275  int app_sleep_cond; /*!< Non-zero if the login app should sleep. */
276  struct ast_channel *owner; /*!< Agent */
277  struct ast_channel *chan; /*!< Channel we use */
278  unsigned int flags; /*!< Flags show if settings were applied with channel vars */
279  AST_LIST_ENTRY(agent_pvt) list;/*!< Next Agent in the linked list. */
280 };
281 
282 #define DATA_EXPORT_AGENT(MEMBER) \
283  MEMBER(agent_pvt, autologoff, AST_DATA_INTEGER) \
284  MEMBER(agent_pvt, ackcall, AST_DATA_BOOLEAN) \
285  MEMBER(agent_pvt, deferlogoff, AST_DATA_BOOLEAN) \
286  MEMBER(agent_pvt, wrapuptime, AST_DATA_MILLISECONDS) \
287  MEMBER(agent_pvt, acknowledged, AST_DATA_BOOLEAN) \
288  MEMBER(agent_pvt, name, AST_DATA_STRING) \
289  MEMBER(agent_pvt, password, AST_DATA_PASSWORD) \
290  MEMBER(agent_pvt, acceptdtmf, AST_DATA_CHARACTER)
291 
293 
294 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
295 
296 #define CHECK_FORMATS(ast, p) do { \
297  if (p->chan) {\
298  if (ast->nativeformats != p->chan->nativeformats) { \
299  char tmp1[256], tmp2[256]; \
300  ast_debug(1, "Native formats changing from '%s' to '%s'\n", ast_getformatname_multiple(tmp1, sizeof(tmp1), ast->nativeformats), ast_getformatname_multiple(tmp2, sizeof(tmp2), p->chan->nativeformats)); \
301  /* Native formats changed, reset things */ \
302  ast->nativeformats = p->chan->nativeformats; \
303  ast_debug(1, "Resetting read to '%s' and write to '%s'\n", ast_getformatname_multiple(tmp1, sizeof(tmp1), ast->readformat), ast_getformatname_multiple(tmp2, sizeof(tmp2), ast->writeformat));\
304  ast_set_read_format(ast, ast->readformat); \
305  ast_set_write_format(ast, ast->writeformat); \
306  } \
307  if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \
308  ast_set_read_format(p->chan, ast->rawreadformat); \
309  if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
310  ast_set_write_format(p->chan, ast->rawwriteformat); \
311  } \
312 } while(0)
313 
314 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
315  properly for a timingfd XXX This might need more work if agents were logged in as agents or other
316  totally impractical combinations XXX */
317 
318 #define CLEANUP(ast, p) do { \
319  int x; \
320  if (p->chan) { \
321  for (x=0;x<AST_MAX_FDS;x++) {\
322  if (x != AST_TIMING_FD) \
323  ast_channel_set_fd(ast, x, p->chan->fds[x]); \
324  } \
325  ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \
326  } \
327 } while(0)
328 
329 /*--- Forward declarations */
330 static struct ast_channel *agent_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
331 static int agent_devicestate(void *data);
332 static int agent_digit_begin(struct ast_channel *ast, char digit);
333 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
334 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
335 static int agent_hangup(struct ast_channel *ast);
336 static int agent_answer(struct ast_channel *ast);
337 static struct ast_frame *agent_read(struct ast_channel *ast);
338 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
339 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
340 static int agent_sendtext(struct ast_channel *ast, const char *text);
341 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
342 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
343 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
344 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
345 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
346 static int agent_logoff(const char *agent, int soft);
347 
348 /*! \brief Channel interface description for PBX integration */
349 static const struct ast_channel_tech agent_tech = {
350  .type = "Agent",
351  .description = tdesc,
352  .capabilities = -1,
353  .requester = agent_request,
354  .devicestate = agent_devicestate,
355  .send_digit_begin = agent_digit_begin,
356  .send_digit_end = agent_digit_end,
357  .call = agent_call,
358  .hangup = agent_hangup,
359  .answer = agent_answer,
360  .read = agent_read,
361  .write = agent_write,
362  .write_video = agent_write,
363  .send_html = agent_sendhtml,
364  .send_text = agent_sendtext,
365  .exception = agent_read,
366  .indicate = agent_indicate,
367  .fixup = agent_fixup,
368  .bridged_channel = agent_bridgedchannel,
369  .get_base_channel = agent_get_base_channel,
370 };
371 
372 /*!
373  * \brief Locks the owning channel for a LOCKED pvt while obeying locking order. The pvt
374  * must enter this function locked and will be returned locked, but this function will
375  * unlock the pvt for a short time, so it can't be used while expecting the pvt to remain
376  * static. If function returns a non NULL channel, it will need to be unlocked and
377  * unrefed once it is no longer needed.
378  *
379  * \param pvt Pointer to the LOCKED agent_pvt for which the owner is needed
380  * \ret locked channel which owns the pvt at the time of completion. NULL if not available.
381  */
382 static struct ast_channel *agent_lock_owner(struct agent_pvt *pvt)
383 {
384  struct ast_channel *owner;
385 
386  for (;;) {
387  if (!pvt->owner) { /* No owner. Nothing to do. */
388  return NULL;
389  }
390 
391  /* If we don't ref the owner, it could be killed when we unlock the pvt. */
392  owner = ast_channel_ref(pvt->owner);
393 
394  /* Locking order requires us to lock channel, then pvt. */
395  ast_mutex_unlock(&pvt->lock);
396  ast_channel_lock(owner);
397  ast_mutex_lock(&pvt->lock);
398 
399  /* Check if owner changed during pvt unlock period */
400  if (owner != pvt->owner) { /* Channel changed. Unref and do another pass. */
401  ast_channel_unlock(owner);
402  owner = ast_channel_unref(owner);
403  } else { /* Channel stayed the same. Return it. */
404  return owner;
405  }
406  }
407 }
408 
409 /*!
410  * \internal
411  * \brief Destroy an agent pvt struct.
412  *
413  * \param doomed Agent pvt to destroy.
414  *
415  * \return Nothing
416  */
417 static void agent_pvt_destroy(struct agent_pvt *doomed)
418 {
419  ast_mutex_destroy(&doomed->lock);
422  ast_free(doomed);
423 }
424 
425 /*!
426  * Adds an agent to the global list of agents.
427  *
428  * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
429  * \param pending If it is pending or not.
430  * @return The just created agent.
431  * \sa agent_pvt, agents.
432  */
433 static struct agent_pvt *add_agent(const char *agent, int pending)
434 {
435  char *parse;
437  AST_APP_ARG(agt);
439  AST_APP_ARG(name);
440  );
441  char *password = NULL;
442  char *name = NULL;
443  char *agt = NULL;
444  struct agent_pvt *p;
445 
446  parse = ast_strdupa(agent);
447 
448  /* Extract username (agt), password and name from agent (args). */
449  AST_STANDARD_APP_ARGS(args, parse);
450 
451  if(args.argc == 0) {
452  ast_log(LOG_WARNING, "A blank agent line!\n");
453  return NULL;
454  }
455 
456  if(ast_strlen_zero(args.agt) ) {
457  ast_log(LOG_WARNING, "An agent line with no agentid!\n");
458  return NULL;
459  } else
460  agt = args.agt;
461 
462  if(!ast_strlen_zero(args.password)) {
463  password = args.password;
464  while (*password && *password < 33) password++;
465  }
466  if(!ast_strlen_zero(args.name)) {
467  name = args.name;
468  while (*name && *name < 33) name++;
469  }
470 
471  if (!pending) {
472  /* Are we searching for the agent here ? To see if it exists already ? */
473  AST_LIST_TRAVERSE(&agents, p, list) {
474  if (!strcmp(p->agent, agt)) {
475  break;
476  }
477  }
478  } else {
479  p = NULL;
480  }
481  if (!p) {
482  // Build the agent.
483  if (!(p = ast_calloc(1, sizeof(*p))))
484  return NULL;
485  ast_copy_string(p->agent, agt, sizeof(p->agent));
486  ast_mutex_init(&p->lock);
487  ast_cond_init(&p->app_complete_cond, NULL);
488  ast_cond_init(&p->login_wait_cond, NULL);
489  p->app_lock_flag = 0;
490  p->app_sleep_cond = 1;
491  p->group = group;
492  p->pending = pending;
493  AST_LIST_INSERT_TAIL(&agents, p, list);
494  }
495 
496  ast_copy_string(p->password, password ? password : "", sizeof(p->password));
497  ast_copy_string(p->name, name ? name : "", sizeof(p->name));
498  ast_copy_string(p->moh, moh, sizeof(p->moh));
500  p->ackcall = ackcall;
501  }
503  p->autologoff = autologoff;
504  }
506  p->acceptdtmf = acceptdtmf;
507  }
509  p->enddtmf = enddtmf;
510  }
511 
512  /* If someone reduces the wrapuptime and reloads, we want it
513  * to change the wrapuptime immediately on all calls */
514  if (!ast_test_flag(p, AGENT_FLAG_WRAPUPTIME) && p->wrapuptime > wrapuptime) {
515  struct timeval now = ast_tvnow();
516  /* XXX check what is this exactly */
517 
518  /* We won't be pedantic and check the tv_usec val */
519  if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
520  p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
521  p->lastdisc.tv_usec = now.tv_usec;
522  }
523  }
524  p->wrapuptime = wrapuptime;
525 
526  if (pending)
527  p->dead = 1;
528  else
529  p->dead = 0;
530  return p;
531 }
532 
533 /*!
534  * Deletes an agent after doing some clean up.
535  * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
536  *
537  * \warning XXX This function seems to be very unsafe.
538  * Potential for double free and use after free among other
539  * problems.
540  *
541  * \param p Agent to be deleted.
542  * \returns Always 0.
543  */
544 static int agent_cleanup(struct agent_pvt *p)
545 {
546  struct ast_channel *chan;
547 
548  ast_mutex_lock(&p->lock);
549  chan = p->owner;
550  p->owner = NULL;
551  /* Release ownership of the agent to other threads (presumably running the login app). */
552  p->app_sleep_cond = 1;
553  p->app_lock_flag = 0;
555  if (chan) {
556  chan->tech_pvt = NULL;
557  chan = ast_channel_release(chan);
558  }
559  if (p->dead) {
560  ast_mutex_unlock(&p->lock);
562  } else {
563  ast_mutex_unlock(&p->lock);
564  }
565  return 0;
566 }
567 
568 static int agent_answer(struct ast_channel *ast)
569 {
570  ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
571  return -1;
572 }
573 
574 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
575 {
576  char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
577  char filename[AST_MAX_BUF];
578  int res = -1;
579  if (!p)
580  return -1;
581  if (!ast->monitor) {
582  snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
583  /* substitute . for - */
584  if ((pointer = strchr(filename, '.')))
585  *pointer = '-';
586  snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
587  ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT);
588  ast_monitor_setjoinfiles(ast, 1);
589  snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
590 #if 0
591  ast_verbose("name is %s, link is %s\n",tmp, tmp2);
592 #endif
593  if (!ast->cdr)
594  ast->cdr = ast_cdr_alloc();
595  ast_cdr_setuserfield(ast, tmp2);
596  res = 0;
597  } else
598  ast_log(LOG_ERROR, "Recording already started on that call.\n");
599  return res;
600 }
601 
602 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
603 {
604  return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
605 }
606 
607 static struct ast_frame *agent_read(struct ast_channel *ast)
608 {
609  struct agent_pvt *p = ast->tech_pvt;
610  struct ast_frame *f = NULL;
611  static struct ast_frame answer_frame = { AST_FRAME_CONTROL, { AST_CONTROL_ANSWER } };
612  int cur_time = time(NULL);
613  struct ast_channel *owner;
614 
615  ast_mutex_lock(&p->lock);
616  owner = agent_lock_owner(p);
617 
618  CHECK_FORMATS(ast, p);
619  if (!p->start) {
620  p->start = cur_time;
621  }
622  if (p->chan) {
624  p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
625  f = ast_read(p->chan);
626  ast->fdno = -1;
627  } else
628  f = &ast_null_frame;
629  if (f) {
630  /* if acknowledgement is not required, and the channel is up, we may have missed
631  an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
632  if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) {
633  p->acknowledged = 1;
634  }
635 
636  if (!p->acknowledged) {
637  int howlong = cur_time - p->start;
638  if (p->autologoff && (howlong >= p->autologoff)) {
639  ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
640  if (owner || p->chan) {
641  if (owner) {
643  ast_channel_unlock(owner);
644  owner = ast_channel_unref(owner);
645  }
646 
647  while (p->chan && ast_channel_trylock(p->chan)) {
649  }
650  if (p->chan) {
653  }
654  }
655  }
656  }
657  switch (f->frametype) {
658  case AST_FRAME_CONTROL:
659  if (f->subclass.integer == AST_CONTROL_ANSWER) {
660  if (p->ackcall) {
661  ast_verb(3, "%s answered, waiting for '%c' to acknowledge\n", p->chan->name, p->acceptdtmf);
662  /* Don't pass answer along */
663  ast_frfree(f);
664  f = &ast_null_frame;
665  } else {
666  p->acknowledged = 1;
667  /* Use the builtin answer frame for the
668  recording start check below. */
669  ast_frfree(f);
670  f = &answer_frame;
671  }
672  }
673  break;
675  /*ignore DTMF begin's as it can cause issues with queue announce files*/
676  if((!p->acknowledged && f->subclass.integer == p->acceptdtmf) || (f->subclass.integer == p->enddtmf && endcall)){
677  ast_frfree(f);
678  f = &ast_null_frame;
679  }
680  break;
681  case AST_FRAME_DTMF_END:
682  if (!p->acknowledged && (f->subclass.integer == p->acceptdtmf)) {
683  if (p->chan) {
684  ast_verb(3, "%s acknowledged\n", p->chan->name);
685  }
686  p->acknowledged = 1;
687  ast_frfree(f);
688  f = &answer_frame;
689  } else if (f->subclass.integer == p->enddtmf && endcall) {
690  /* terminates call */
691  ast_frfree(f);
692  f = NULL;
693  }
694  break;
695  case AST_FRAME_VOICE:
696  case AST_FRAME_VIDEO:
697  /* don't pass voice or video until the call is acknowledged */
698  if (!p->acknowledged) {
699  ast_frfree(f);
700  f = &ast_null_frame;
701  }
702  default:
703  /* pass everything else on through */
704  break;
705  }
706  }
707 
708  if (owner) {
709  ast_channel_unlock(owner);
710  owner = ast_channel_unref(owner);
711  }
712 
713  CLEANUP(ast,p);
714  if (p->chan && !p->chan->_bridge) {
715  if (strcasecmp(p->chan->tech->type, "Local")) {
716  p->chan->_bridge = ast;
717  ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
718  }
719  }
720  ast_mutex_unlock(&p->lock);
721  if (recordagentcalls && f == &answer_frame)
722  agent_start_monitoring(ast,0);
723  return f;
724 }
725 
726 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
727 {
728  struct agent_pvt *p = ast->tech_pvt;
729  int res = -1;
730  ast_mutex_lock(&p->lock);
731  if (p->chan)
732  res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
733  ast_mutex_unlock(&p->lock);
734  return res;
735 }
736 
737 static int agent_sendtext(struct ast_channel *ast, const char *text)
738 {
739  struct agent_pvt *p = ast->tech_pvt;
740  int res = -1;
741  ast_mutex_lock(&p->lock);
742  if (p->chan)
743  res = ast_sendtext(p->chan, text);
744  ast_mutex_unlock(&p->lock);
745  return res;
746 }
747 
748 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
749 {
750  struct agent_pvt *p = ast->tech_pvt;
751  int res = -1;
752  CHECK_FORMATS(ast, p);
753  ast_mutex_lock(&p->lock);
754  if (!p->chan)
755  res = 0;
756  else {
757  if ((f->frametype != AST_FRAME_VOICE) ||
758  (f->frametype != AST_FRAME_VIDEO) ||
759  (f->subclass.codec == p->chan->writeformat)) {
760  res = ast_write(p->chan, f);
761  } else {
762  ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
763  f->frametype == AST_FRAME_VOICE ? "audio" : "video",
764  ast->name, p->chan->name);
765  res = 0;
766  }
767  }
768  CLEANUP(ast, p);
769  ast_mutex_unlock(&p->lock);
770  return res;
771 }
772 
773 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
774 {
775  struct agent_pvt *p = newchan->tech_pvt;
776  ast_mutex_lock(&p->lock);
777  if (p->owner != oldchan) {
778  ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
779  ast_mutex_unlock(&p->lock);
780  return -1;
781  }
782  p->owner = newchan;
783  ast_mutex_unlock(&p->lock);
784  return 0;
785 }
786 
787 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
788 {
789  struct agent_pvt *p = ast->tech_pvt;
790  int res = -1;
791 
792  ast_mutex_lock(&p->lock);
793  if (p->chan && !ast_check_hangup(p->chan)) {
794  ast_channel_unlock(ast);
796  res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
798  ast_mutex_unlock(&p->lock);
799  ast_channel_lock(ast);
800  } else {
801  ast_mutex_unlock(&p->lock);
802  res = 0;
803  }
804  return res;
805 }
806 
807 static int agent_digit_begin(struct ast_channel *ast, char digit)
808 {
809  struct agent_pvt *p = ast->tech_pvt;
810  ast_mutex_lock(&p->lock);
811  if (p->chan) {
812  ast_senddigit_begin(p->chan, digit);
813  }
814  ast_mutex_unlock(&p->lock);
815  return 0;
816 }
817 
818 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
819 {
820  struct agent_pvt *p = ast->tech_pvt;
821  ast_mutex_lock(&p->lock);
822  if (p->chan) {
823  ast_senddigit_end(p->chan, digit, duration);
824  }
825  ast_mutex_unlock(&p->lock);
826  return 0;
827 }
828 
829 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
830 {
831  struct agent_pvt *p = ast->tech_pvt;
832  int res;
833  int newstate=0;
834 
835  ast_mutex_lock(&p->lock);
836  p->acknowledged = 0;
837 
838  if (p->pending) {
839  ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
840  ast_mutex_unlock(&p->lock);
842  return 0;
843  }
844 
845  ast_assert(p->chan != NULL);
846  ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
847  ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
848 
849  ast_mutex_unlock(&p->lock);
850 
851  res = ast_streamfile(p->chan, beep, p->chan->language);
852  ast_debug(3, "Played beep, result '%d'\n", res);
853  if (!res) {
854  res = ast_waitstream(p->chan, "");
855  ast_debug(3, "Waited for stream, result '%d'\n", res);
856  }
857 
858  ast_mutex_lock(&p->lock);
859 
860  if (!res) {
862  ast_debug(3, "Set read format, result '%d'\n", res);
863  if (res)
864  ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
865  }
866 
867  if (!res) {
869  ast_debug(3, "Set write format, result '%d'\n", res);
870  if (res)
871  ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
872  }
873  if(!res) {
874  /* Call is immediately up, or might need ack */
875  if (p->ackcall) {
876  newstate = AST_STATE_RINGING;
877  } else {
878  newstate = AST_STATE_UP;
879  if (recordagentcalls)
880  agent_start_monitoring(ast, 0);
881  p->acknowledged = 1;
882  }
883  }
884  CLEANUP(ast, p);
885  ast_mutex_unlock(&p->lock);
886  if (newstate)
887  ast_setstate(ast, newstate);
888  return res ? -1 : 0;
889 }
890 
891 /*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
893 {
894  struct agent_pvt *p;
895  struct ast_channel *base = chan;
896 
897  /* chan is locked by the calling function */
898  if (!chan || !chan->tech_pvt) {
899  ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL);
900  return NULL;
901  }
902  p = chan->tech_pvt;
903  if (p->chan)
904  base = p->chan;
905  return base;
906 }
907 
908 static int agent_hangup(struct ast_channel *ast)
909 {
910  struct agent_pvt *p = ast->tech_pvt;
911  struct ast_channel *indicate_chan = NULL;
912  char *tmp_moh; /* moh buffer for indicating after unlocking p */
913 
914  if (p->pending) {
916  AST_LIST_REMOVE(&agents, p, list);
918  }
919 
920  ast_mutex_lock(&p->lock);
921  p->owner = NULL;
922  ast->tech_pvt = NULL;
923  p->acknowledged = 0;
924 
925  /* if they really are hung up then set start to 0 so the test
926  * later if we're called on an already downed channel
927  * doesn't cause an agent to be logged out like when
928  * agent_request() is followed immediately by agent_hangup()
929  * as in apps/app_chanisavail.c:chanavail_exec()
930  */
931 
932  ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
933  p->start = 0;
934  if (p->chan) {
935  p->chan->_bridge = NULL;
936  /* If they're dead, go ahead and hang up on the agent now */
937  if (p->dead) {
939  } else if (p->loginstart) {
940  indicate_chan = ast_channel_ref(p->chan);
941  tmp_moh = ast_strdupa(p->moh);
942  }
943  }
944  ast_mutex_unlock(&p->lock);
945 
946  if (indicate_chan) {
947  ast_indicate_data(indicate_chan, AST_CONTROL_HOLD,
948  S_OR(tmp_moh, NULL),
949  !ast_strlen_zero(tmp_moh) ? strlen(tmp_moh) + 1 : 0);
950  indicate_chan = ast_channel_unref(indicate_chan);
951  }
952 
953  ast_mutex_lock(&p->lock);
954  if (p->abouttograb) {
955  /* Let the "about to grab" thread know this isn't valid anymore, and let it
956  kill it later */
957  p->abouttograb = 0;
958  } else if (p->dead) {
959  ast_mutex_unlock(&p->lock);
961  return 0;
962  } else {
963  /* Store last disconnect time */
965  }
966 
967  /* Release ownership of the agent to other threads (presumably running the login app). */
968  p->app_sleep_cond = 1;
969  p->app_lock_flag = 0;
971 
972  ast_mutex_unlock(&p->lock);
973  return 0;
974 }
975 
976 static int agent_cont_sleep(void *data)
977 {
978  struct agent_pvt *p;
979  int res;
980 
981  p = (struct agent_pvt *) data;
982 
983  ast_mutex_lock(&p->lock);
984  res = p->app_sleep_cond;
985  if (res && p->lastdisc.tv_sec) {
986  if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
987  res = 0;
988  }
989  }
990  ast_mutex_unlock(&p->lock);
991 
992  if (!res) {
993  ast_debug(5, "agent_cont_sleep() returning %d\n", res);
994  }
995 
996  return res;
997 }
998 
999 static int agent_ack_sleep(struct agent_pvt *p)
1000 {
1001  int digit;
1002  int to = 1000;
1003  struct ast_frame *f;
1004  struct timeval start = ast_tvnow();
1005  int ms;
1006 
1007  /* Wait a second and look for something */
1008  while ((ms = ast_remaining_ms(start, to))) {
1009  ms = ast_waitfor(p->chan, ms);
1010  if (ms < 0) {
1011  return -1;
1012  }
1013  if (ms == 0) {
1014  return 0;
1015  }
1016  f = ast_read(p->chan);
1017  if (!f) {
1018  return -1;
1019  }
1020  if (f->frametype == AST_FRAME_DTMF) {
1021  digit = f->subclass.integer;
1022  } else {
1023  digit = 0;
1024  }
1025  ast_frfree(f);
1026  ast_mutex_lock(&p->lock);
1027  if (!p->app_sleep_cond) {
1028  ast_mutex_unlock(&p->lock);
1029  return 0;
1030  }
1031  if (digit == p->acceptdtmf) {
1032  ast_mutex_unlock(&p->lock);
1033  return 1;
1034  }
1035  if (p->lastdisc.tv_sec) {
1036  if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
1037  ast_mutex_unlock(&p->lock);
1038  return 0;
1039  }
1040  }
1041  ast_mutex_unlock(&p->lock);
1042  }
1043  return 0;
1044 }
1045 
1047 {
1048  struct agent_pvt *p = bridge->tech_pvt;
1049  struct ast_channel *ret = NULL;
1050 
1051  if (p) {
1052  if (chan == p->chan)
1053  ret = bridge->_bridge;
1054  else if (chan == bridge->_bridge)
1055  ret = p->chan;
1056  }
1057 
1058  ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
1059  return ret;
1060 }
1061 
1062 /*! \brief Create new agent channel */
1063 static struct ast_channel *agent_new(struct agent_pvt *p, int state, const char *linkedid)
1064 {
1065  struct ast_channel *tmp;
1066 #if 0
1067  if (!p->chan) {
1068  ast_log(LOG_WARNING, "No channel? :(\n");
1069  return NULL;
1070  }
1071 #endif
1072  if (p->pending)
1073  tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff);
1074  else
1075  tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/%s", p->agent);
1076  if (!tmp) {
1077  ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
1078  return NULL;
1079  }
1080 
1081  tmp->tech = &agent_tech;
1082  if (p->chan) {
1083  tmp->nativeformats = p->chan->nativeformats;
1084  tmp->writeformat = p->chan->writeformat;
1085  tmp->rawwriteformat = p->chan->writeformat;
1086  tmp->readformat = p->chan->readformat;
1087  tmp->rawreadformat = p->chan->readformat;
1089  ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
1090  ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
1091  /* XXX Is this really all we copy form the originating channel?? */
1092  } else {
1098  }
1099  /* Safe, agentlock already held */
1100  tmp->tech_pvt = p;
1101  p->owner = tmp;
1102  tmp->priority = 1;
1103  return tmp;
1104 }
1105 
1106 
1107 /*!
1108  * Read configuration data. The file named agents.conf.
1109  *
1110  * \returns Always 0, or so it seems.
1111  */
1112 static int read_agent_config(int reload)
1113 {
1114  struct ast_config *cfg;
1115  struct ast_config *ucfg;
1116  struct ast_variable *v;
1117  struct agent_pvt *p;
1118  const char *catname;
1119  const char *hasagent;
1120  int genhasagent;
1121  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1122 
1123  group = 0;
1124  autologoff = 0;
1125  wrapuptime = 0;
1126  ackcall = 0;
1127  endcall = 1;
1128  cfg = ast_config_load(config, config_flags);
1129  if (!cfg) {
1130  ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1131  return 0;
1132  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1133  return -1;
1134  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
1135  ast_log(LOG_ERROR, "%s contains a parsing error. Aborting\n", config);
1136  return 0;
1137  }
1138  if ((ucfg = ast_config_load("users.conf", config_flags))) {
1139  if (ucfg == CONFIG_STATUS_FILEUNCHANGED) {
1140  ucfg = NULL;
1141  } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
1142  ast_log(LOG_ERROR, "users.conf contains a parsing error. Aborting\n");
1143  return 0;
1144  }
1145  }
1146 
1148  AST_LIST_TRAVERSE(&agents, p, list) {
1149  p->dead = 1;
1150  }
1151  strcpy(moh, "default");
1152  /* set the default recording values */
1153  recordagentcalls = 0;
1154  strcpy(recordformat, "wav");
1155  strcpy(recordformatext, "wav");
1156  urlprefix[0] = '\0';
1157  savecallsin[0] = '\0';
1158 
1159  /* Read in the [agents] section */
1160  v = ast_variable_browse(cfg, "agents");
1161  while(v) {
1162  /* Create the interface list */
1163  if (!strcasecmp(v->name, "agent")) {
1164  add_agent(v->value, 0);
1165  } else if (!strcasecmp(v->name, "group")) {
1166  group = ast_get_group(v->value);
1167  } else if (!strcasecmp(v->name, "autologoff")) {
1168  autologoff = atoi(v->value);
1169  if (autologoff < 0)
1170  autologoff = 0;
1171  } else if (!strcasecmp(v->name, "ackcall")) {
1172  if (ast_true(v->value) || !strcasecmp(v->value, "always")) {
1173  ackcall = 1;
1174  }
1175  } else if (!strcasecmp(v->name, "endcall")) {
1176  endcall = ast_true(v->value);
1177  } else if (!strcasecmp(v->name, "acceptdtmf")) {
1178  acceptdtmf = *(v->value);
1179  ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf);
1180  } else if (!strcasecmp(v->name, "enddtmf")) {
1181  enddtmf = *(v->value);
1182  } else if (!strcasecmp(v->name, "wrapuptime")) {
1183  wrapuptime = atoi(v->value);
1184  if (wrapuptime < 0)
1185  wrapuptime = 0;
1186  } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1187  maxlogintries = atoi(v->value);
1188  if (maxlogintries < 0)
1189  maxlogintries = 0;
1190  } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1191  strcpy(agentgoodbye,v->value);
1192  } else if (!strcasecmp(v->name, "musiconhold")) {
1193  ast_copy_string(moh, v->value, sizeof(moh));
1194  } else if (!strcasecmp(v->name, "updatecdr")) {
1195  if (ast_true(v->value))
1196  updatecdr = 1;
1197  else
1198  updatecdr = 0;
1199  } else if (!strcasecmp(v->name, "autologoffunavail")) {
1200  if (ast_true(v->value))
1201  autologoffunavail = 1;
1202  else
1203  autologoffunavail = 0;
1204  } else if (!strcasecmp(v->name, "recordagentcalls")) {
1205  recordagentcalls = ast_true(v->value);
1206  } else if (!strcasecmp(v->name, "recordformat")) {
1207  ast_copy_string(recordformat, v->value, sizeof(recordformat));
1208  if (!strcasecmp(v->value, "wav49"))
1209  strcpy(recordformatext, "WAV");
1210  else
1211  ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1212  } else if (!strcasecmp(v->name, "urlprefix")) {
1213  ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1214  if (urlprefix[strlen(urlprefix) - 1] != '/')
1215  strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1216  } else if (!strcasecmp(v->name, "savecallsin")) {
1217  if (v->value[0] == '/')
1218  ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1219  else
1220  snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1221  if (savecallsin[strlen(savecallsin) - 1] != '/')
1222  strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1223  } else if (!strcasecmp(v->name, "custom_beep")) {
1224  ast_copy_string(beep, v->value, sizeof(beep));
1225  }
1226  v = v->next;
1227  }
1228  if (ucfg) {
1229  genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1230  catname = ast_category_browse(ucfg, NULL);
1231  while(catname) {
1232  if (strcasecmp(catname, "general")) {
1233  hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1234  if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1235  char tmp[256];
1236  const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1237  const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1238  if (!fullname)
1239  fullname = "";
1240  if (!secret)
1241  secret = "";
1242  snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1243  add_agent(tmp, 0);
1244  }
1245  }
1246  catname = ast_category_browse(ucfg, catname);
1247  }
1248  ast_config_destroy(ucfg);
1249  }
1251  if (p->dead) {
1253  /* Destroy if appropriate */
1254  if (!p->owner) {
1255  if (!p->chan) {
1256  agent_pvt_destroy(p);
1257  } else {
1258  /* Cause them to hang up */
1260  }
1261  }
1262  }
1263  }
1266  ast_config_destroy(cfg);
1267  return 1;
1268 }
1269 
1270 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1271 {
1272  struct ast_channel *chan=NULL, *parent=NULL;
1273  struct agent_pvt *p;
1274  int res;
1275 
1276  ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
1277  if (needlock)
1279  AST_LIST_TRAVERSE(&agents, p, list) {
1280  if (p == newlyavailable) {
1281  continue;
1282  }
1283  ast_mutex_lock(&p->lock);
1284  if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1285  ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1286  /* We found a pending call, time to merge */
1287  chan = agent_new(newlyavailable, AST_STATE_DOWN, p->owner ? p->owner->linkedid : NULL);
1288  parent = p->owner;
1289  p->abouttograb = 1;
1290  ast_mutex_unlock(&p->lock);
1291  break;
1292  }
1293  ast_mutex_unlock(&p->lock);
1294  }
1295  if (needlock)
1297  if (parent && chan) {
1298  if (newlyavailable->ackcall) {
1299  /* Don't do beep here */
1300  res = 0;
1301  } else {
1302  ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1303  res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1304  ast_debug(3, "Played beep, result '%d'\n", res);
1305  if (!res) {
1306  res = ast_waitstream(newlyavailable->chan, "");
1307  ast_debug(1, "Waited for stream, result '%d'\n", res);
1308  }
1309  }
1310  if (!res) {
1311  /* Note -- parent may have disappeared */
1312  if (p->abouttograb) {
1313  newlyavailable->acknowledged = 1;
1314  /* Safe -- agent lock already held */
1315  ast_setstate(parent, AST_STATE_UP);
1316  ast_setstate(chan, AST_STATE_UP);
1317  ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1318  ast_channel_masquerade(parent, chan);
1319  ast_hangup(chan);
1320  p->abouttograb = 0;
1321  } else {
1322  ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
1323  agent_cleanup(newlyavailable);
1324  }
1325  } else {
1326  ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
1327  agent_cleanup(newlyavailable);
1328  }
1329  }
1330  return 0;
1331 }
1332 
1333 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1334 {
1335  struct agent_pvt *p;
1336  int res=0;
1337 
1338  ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
1339  if (needlock)
1341  AST_LIST_TRAVERSE(&agents, p, list) {
1342  if (p == newlyavailable) {
1343  continue;
1344  }
1345  ast_mutex_lock(&p->lock);
1346  if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1347  ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1348  ast_mutex_unlock(&p->lock);
1349  break;
1350  }
1351  ast_mutex_unlock(&p->lock);
1352  }
1353  if (needlock)
1355  if (p) {
1356  ast_mutex_unlock(&newlyavailable->lock);
1357  ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1358  res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1359  ast_debug(1, "Played beep, result '%d'\n", res);
1360  if (!res) {
1361  res = ast_waitstream(newlyavailable->chan, "");
1362  ast_debug(1, "Waited for stream, result '%d'\n", res);
1363  }
1364  ast_mutex_lock(&newlyavailable->lock);
1365  }
1366  return res;
1367 }
1368 
1369 /*! \brief Part of the Asterisk PBX interface */
1370 static struct ast_channel *agent_request(const char *type, format_t format, const struct ast_channel* requestor, void *data, int *cause)
1371 {
1372  struct agent_pvt *p;
1373  struct ast_channel *chan = NULL;
1374  char *s;
1375  ast_group_t groupmatch;
1376  int groupoff;
1377  int waitforagent=0;
1378  int hasagent = 0;
1379  struct timeval now;
1380 
1381  s = data;
1382  if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
1383  groupmatch = (1 << groupoff);
1384  } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
1385  groupmatch = (1 << groupoff);
1386  waitforagent = 1;
1387  } else
1388  groupmatch = 0;
1389 
1390  /* Check actual logged in agents first */
1392  AST_LIST_TRAVERSE(&agents, p, list) {
1393  ast_mutex_lock(&p->lock);
1394  if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1395  if (p->chan) {
1396  hasagent++;
1397  }
1398  now = ast_tvnow();
1399  if (p->loginstart
1400  && (!p->lastdisc.tv_sec || ast_tvdiff_ms(now, p->lastdisc) > 0)) {
1401  p->lastdisc = ast_tv(0, 0);
1402  /* Agent must be registered, but not have any active call, and not be in a waiting state */
1403  if (!p->owner && p->chan) {
1404  /* Fixed agent */
1405  chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
1406  }
1407  if (chan) {
1408  ast_mutex_unlock(&p->lock);
1409  break;
1410  }
1411  }
1412  }
1413  ast_mutex_unlock(&p->lock);
1414  }
1415 
1416  if (!chan && waitforagent) {
1417  /* No agent available -- but we're requesting to wait for one.
1418  Allocate a place holder */
1419  if (hasagent) {
1420  ast_debug(1, "Creating place holder for '%s'\n", s);
1421  p = add_agent(data, 1);
1422  if (p) {
1423  p->group = groupmatch;
1424  chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
1425  if (!chan) {
1426  AST_LIST_REMOVE(&agents, p, list);
1427  agent_pvt_destroy(p);
1428  }
1429  }
1430  } else {
1431  ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
1432  }
1433  }
1434  *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1436 
1437  if (chan) {
1438  ast_mutex_lock(&p->lock);
1439  if (p->pending) {
1440  ast_mutex_unlock(&p->lock);
1441  return chan;
1442  }
1443 
1444  if (!p->chan) {
1445  ast_debug(1, "Agent disconnected before we could connect the call\n");
1446  ast_mutex_unlock(&p->lock);
1447  ast_hangup(chan);
1448  *cause = AST_CAUSE_UNREGISTERED;
1449  return NULL;
1450  }
1451 
1452  /* we need to take control of the channel from the login app
1453  * thread */
1454  p->app_sleep_cond = 0;
1455  p->app_lock_flag = 1;
1458 
1459  if (!p->chan) {
1460  ast_debug(1, "Agent disconnected while we were connecting the call\n");
1461  ast_mutex_unlock(&p->lock);
1462  ast_hangup(chan);
1463  *cause = AST_CAUSE_UNREGISTERED;
1464  return NULL;
1465  }
1466 
1468  ast_mutex_unlock(&p->lock);
1469  }
1470 
1471  return chan;
1472 }
1473 
1474 static force_inline int powerof(unsigned int d)
1475 {
1476  int x = ffs(d);
1477 
1478  if (x)
1479  return x - 1;
1480 
1481  return 0;
1482 }
1483 
1484 /*!
1485  * Lists agents and their status to the Manager API.
1486  * It is registered on load_module() and it gets called by the manager backend.
1487  * This function locks both the pvt and the channel that owns it for a while, but
1488  * does not keep these locks.
1489  * \param s
1490  * \param m
1491  * \returns
1492  * \sa action_agent_logoff(), load_module().
1493  */
1494 static int action_agents(struct mansession *s, const struct message *m)
1495 {
1496  const char *id = astman_get_header(m,"ActionID");
1497  char idText[256] = "";
1498  struct agent_pvt *p;
1499  char *username = NULL;
1500  char *loginChan = NULL;
1501  char *talkingto = NULL;
1502  char *talkingtoChan = NULL;
1503  char *status = NULL;
1504  struct ast_channel *bridge;
1505 
1506  if (!ast_strlen_zero(id))
1507  snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1508  astman_send_ack(s, m, "Agents will follow");
1510  AST_LIST_TRAVERSE(&agents, p, list) {
1511  struct ast_channel *owner;
1512  ast_mutex_lock(&p->lock);
1513  owner = agent_lock_owner(p);
1514 
1515  /* Status Values:
1516  AGENT_LOGGEDOFF - Agent isn't logged in
1517  AGENT_IDLE - Agent is logged in, and waiting for call
1518  AGENT_ONCALL - Agent is logged in, and on a call
1519  AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1520 
1521  username = S_OR(p->name, "None");
1522 
1523  /* Set a default status. It 'should' get changed. */
1524  status = "AGENT_UNKNOWN";
1525 
1526  if (p->chan) {
1527  loginChan = ast_strdupa(p->chan->name);
1528  if (owner && owner->_bridge) {
1529  talkingto = S_COR(p->chan->caller.id.number.valid,
1530  p->chan->caller.id.number.str, "n/a");
1531  if ((bridge = ast_bridged_channel(owner))) {
1532  talkingtoChan = ast_strdupa(bridge->name);
1533  } else {
1534  talkingtoChan = "n/a";
1535  }
1536  status = "AGENT_ONCALL";
1537  } else {
1538  talkingto = "n/a";
1539  talkingtoChan = "n/a";
1540  status = "AGENT_IDLE";
1541  }
1542  } else {
1543  loginChan = "n/a";
1544  talkingto = "n/a";
1545  talkingtoChan = "n/a";
1546  status = "AGENT_LOGGEDOFF";
1547  }
1548 
1549  if (owner) {
1550  ast_channel_unlock(owner);
1551  owner = ast_channel_unref(owner);
1552  }
1553 
1554  astman_append(s, "Event: Agents\r\n"
1555  "Agent: %s\r\n"
1556  "Name: %s\r\n"
1557  "Status: %s\r\n"
1558  "LoggedInChan: %s\r\n"
1559  "LoggedInTime: %d\r\n"
1560  "TalkingTo: %s\r\n"
1561  "TalkingToChan: %s\r\n"
1562  "%s"
1563  "\r\n",
1564  p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
1565  ast_mutex_unlock(&p->lock);
1566  }
1568  astman_append(s, "Event: AgentsComplete\r\n"
1569  "%s"
1570  "\r\n",idText);
1571  return 0;
1572 }
1573 
1574 static int agent_logoff(const char *agent, int soft)
1575 {
1576  struct agent_pvt *p;
1577  int ret = -1; /* Return -1 if no agent if found */
1578 
1580  AST_LIST_TRAVERSE(&agents, p, list) {
1581  if (!strcasecmp(p->agent, agent)) {
1582  ret = 0;
1583  if (p->owner || p->chan) {
1584  if (!soft) {
1585  struct ast_channel *owner;
1586  ast_mutex_lock(&p->lock);
1587  owner = agent_lock_owner(p);
1588 
1589  if (owner) {
1591  ast_channel_unlock(owner);
1592  owner = ast_channel_unref(owner);
1593  }
1594 
1595  while (p->chan && ast_channel_trylock(p->chan)) {
1596  DEADLOCK_AVOIDANCE(&p->lock);
1597  }
1598  if (p->chan) {
1601  }
1602 
1603  ast_mutex_unlock(&p->lock);
1604  } else
1605  p->deferlogoff = 1;
1606  }
1607  break;
1608  }
1609  }
1611 
1612  return ret;
1613 }
1614 
1615 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1616 {
1617  int ret;
1618  const char *agent;
1619 
1620  switch (cmd) {
1621  case CLI_INIT:
1622  e->command = "agent logoff";
1623  e->usage =
1624  "Usage: agent logoff <channel> [soft]\n"
1625  " Sets an agent as no longer logged in.\n"
1626  " If 'soft' is specified, do not hangup existing calls.\n";
1627  return NULL;
1628  case CLI_GENERATE:
1629  return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
1630  }
1631 
1632  if (a->argc < 3 || a->argc > 4)
1633  return CLI_SHOWUSAGE;
1634  if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
1635  return CLI_SHOWUSAGE;
1636 
1637  agent = a->argv[2] + 6;
1638  ret = agent_logoff(agent, a->argc == 4);
1639  if (ret == 0)
1640  ast_cli(a->fd, "Logging out %s\n", agent);
1641 
1642  return CLI_SUCCESS;
1643 }
1644 
1645 /*!
1646  * Sets an agent as no longer logged in in the Manager API.
1647  * It is registered on load_module() and it gets called by the manager backend.
1648  * \param s
1649  * \param m
1650  * \returns
1651  * \sa action_agents(), load_module().
1652  */
1653 static int action_agent_logoff(struct mansession *s, const struct message *m)
1654 {
1655  const char *agent = astman_get_header(m, "Agent");
1656  const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1657  int soft;
1658  int ret; /* return value of agent_logoff */
1659 
1660  if (ast_strlen_zero(agent)) {
1661  astman_send_error(s, m, "No agent specified");
1662  return 0;
1663  }
1664 
1665  soft = ast_true(soft_s) ? 1 : 0;
1666  ret = agent_logoff(agent, soft);
1667  if (ret == 0)
1668  astman_send_ack(s, m, "Agent logged out");
1669  else
1670  astman_send_error(s, m, "No such agent");
1671 
1672  return 0;
1673 }
1674 
1675 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1676 {
1677  char *ret = NULL;
1678 
1679  if (pos == 2) {
1680  struct agent_pvt *p;
1681  char name[AST_MAX_AGENT];
1682  int which = 0, len = strlen(word);
1683 
1685  AST_LIST_TRAVERSE(&agents, p, list) {
1686  snprintf(name, sizeof(name), "Agent/%s", p->agent);
1687  if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
1688  ret = ast_strdup(name);
1689  break;
1690  }
1691  }
1693  } else if (pos == 3 && state == 0)
1694  return ast_strdup("soft");
1695 
1696  return ret;
1697 }
1698 
1699 /*!
1700  * Show agents in cli.
1701  */
1702 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1703 {
1704  struct agent_pvt *p;
1705  char username[AST_MAX_BUF];
1706  char location[AST_MAX_BUF] = "";
1707  char talkingto[AST_MAX_BUF] = "";
1708  char music[AST_MAX_BUF];
1709  int count_agents = 0; /*!< Number of agents configured */
1710  int online_agents = 0; /*!< Number of online agents */
1711  int offline_agents = 0; /*!< Number of offline agents */
1712 
1713  switch (cmd) {
1714  case CLI_INIT:
1715  e->command = "agent show";
1716  e->usage =
1717  "Usage: agent show\n"
1718  " Provides summary information on agents.\n";
1719  return NULL;
1720  case CLI_GENERATE:
1721  return NULL;
1722  }
1723 
1724  if (a->argc != 2)
1725  return CLI_SHOWUSAGE;
1726 
1728  AST_LIST_TRAVERSE(&agents, p, list) {
1729  struct ast_channel *owner;
1730  ast_mutex_lock(&p->lock);
1731  owner = agent_lock_owner(p);
1732  if (p->pending) {
1733  if (p->group)
1734  ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
1735  else
1736  ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
1737  } else {
1738  if (!ast_strlen_zero(p->name))
1739  snprintf(username, sizeof(username), "(%s) ", p->name);
1740  else
1741  username[0] = '\0';
1742  if (p->chan) {
1743  snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1744  if (owner && ast_bridged_channel(owner)) {
1745  snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1746  } else {
1747  strcpy(talkingto, " is idle");
1748  }
1749  online_agents++;
1750  } else {
1751  strcpy(location, "not logged in");
1752  talkingto[0] = '\0';
1753  offline_agents++;
1754  }
1755  if (!ast_strlen_zero(p->moh))
1756  snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
1757  ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
1758  username, location, talkingto, music);
1759  count_agents++;
1760  }
1761 
1762  if (owner) {
1763  ast_channel_unlock(owner);
1764  owner = ast_channel_unref(owner);
1765  }
1766  ast_mutex_unlock(&p->lock);
1767  }
1769  if ( !count_agents )
1770  ast_cli(a->fd, "No Agents are configured in %s\n",config);
1771  else
1772  ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1773  ast_cli(a->fd, "\n");
1774 
1775  return CLI_SUCCESS;
1776 }
1777 
1778 
1779 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1780 {
1781  struct agent_pvt *p;
1782  char username[AST_MAX_BUF];
1783  char location[AST_MAX_BUF] = "";
1784  char talkingto[AST_MAX_BUF] = "";
1785  char music[AST_MAX_BUF];
1786  int count_agents = 0; /* Number of agents configured */
1787  int online_agents = 0; /* Number of online agents */
1788  int agent_status = 0; /* 0 means offline, 1 means online */
1789 
1790  switch (cmd) {
1791  case CLI_INIT:
1792  e->command = "agent show online";
1793  e->usage =
1794  "Usage: agent show online\n"
1795  " Provides a list of all online agents.\n";
1796  return NULL;
1797  case CLI_GENERATE:
1798  return NULL;
1799  }
1800 
1801  if (a->argc != 3)
1802  return CLI_SHOWUSAGE;
1803 
1805  AST_LIST_TRAVERSE(&agents, p, list) {
1806  struct ast_channel *owner;
1807 
1808  agent_status = 0; /* reset it to offline */
1809  ast_mutex_lock(&p->lock);
1810  owner = agent_lock_owner(p);
1811 
1812  if (!ast_strlen_zero(p->name))
1813  snprintf(username, sizeof(username), "(%s) ", p->name);
1814  else
1815  username[0] = '\0';
1816  if (p->chan) {
1817  snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1818  if (owner && ast_bridged_channel(owner)) {
1819  snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(owner)->name);
1820  } else {
1821  strcpy(talkingto, " is idle");
1822  }
1823  agent_status = 1;
1824  online_agents++;
1825  }
1826 
1827  if (owner) {
1828  ast_channel_unlock(owner);
1829  owner = ast_channel_unref(owner);
1830  }
1831 
1832  if (!ast_strlen_zero(p->moh))
1833  snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
1834  if (agent_status)
1835  ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, music);
1836  count_agents++;
1837  ast_mutex_unlock(&p->lock);
1838  }
1840  if (!count_agents)
1841  ast_cli(a->fd, "No Agents are configured in %s\n", config);
1842  else
1843  ast_cli(a->fd, "%d agents online\n", online_agents);
1844  ast_cli(a->fd, "\n");
1845  return CLI_SUCCESS;
1846 }
1847 
1848 static const char agent_logoff_usage[] =
1849 "Usage: agent logoff <channel> [soft]\n"
1850 " Sets an agent as no longer logged in.\n"
1851 " If 'soft' is specified, do not hangup existing calls.\n";
1852 
1853 static struct ast_cli_entry cli_agents[] = {
1854  AST_CLI_DEFINE(agents_show, "Show status of agents"),
1855  AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
1856  AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
1857 };
1858 
1859 /*!
1860  * Called by the AgentLogin application (from the dial plan).
1861  *
1862  * \brief Log in agent application.
1863  *
1864  * \param chan
1865  * \param data
1866  * \returns
1867  * \sa agentmonitoroutgoing_exec(), load_module().
1868  */
1869 static int login_exec(struct ast_channel *chan, const char *data)
1870 {
1871  int res=0;
1872  int tries = 0;
1873  int max_login_tries = maxlogintries;
1874  struct agent_pvt *p;
1875  char user[AST_MAX_AGENT];
1876  char pass[AST_MAX_AGENT];
1877  char xpass[AST_MAX_AGENT];
1878  char *errmsg;
1879  char *parse;
1881  AST_APP_ARG(agent_id);
1882  AST_APP_ARG(options);
1884  );
1885  const char *tmpoptions = NULL;
1886  int play_announcement = 1;
1887  char agent_goodbye[AST_MAX_FILENAME_LEN];
1888  int update_cdr = updatecdr;
1889 
1890  user[0] = '\0';
1891  xpass[0] = '\0';
1892 
1893  parse = ast_strdupa(data);
1894 
1895  AST_STANDARD_APP_ARGS(args, parse);
1896 
1897  ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1898 
1899  ast_channel_lock(chan);
1900  /* Set Channel Specific Login Overrides */
1901  if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1902  max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1903  if (max_login_tries < 0)
1904  max_login_tries = 0;
1905  tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1906  ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
1907  }
1908  if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1909  if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1910  update_cdr = 1;
1911  else
1912  update_cdr = 0;
1913  tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1914  ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1915  }
1916  if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1917  strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1918  tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1919  ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1920  }
1921  ast_channel_unlock(chan);
1922  /* End Channel Specific Login Overrides */
1923 
1924  if (!ast_strlen_zero(args.options)) {
1925  if (strchr(args.options, 's')) {
1926  play_announcement = 0;
1927  }
1928  }
1929 
1930  if (chan->_state != AST_STATE_UP)
1931  res = ast_answer(chan);
1932  if (!res) {
1933  if (!ast_strlen_zero(args.agent_id))
1934  ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1935  else
1936  res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1937  }
1938  while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1939  tries++;
1940  /* Check for password */
1942  AST_LIST_TRAVERSE(&agents, p, list) {
1943  if (!strcmp(p->agent, user) && !p->pending)
1944  ast_copy_string(xpass, p->password, sizeof(xpass));
1945  }
1947  if (!res) {
1948  if (!ast_strlen_zero(xpass))
1949  res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1950  else
1951  pass[0] = '\0';
1952  }
1953  errmsg = "agent-incorrect";
1954 
1955 #if 0
1956  ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1957 #endif
1958 
1959  /* Check again for accuracy */
1961  AST_LIST_TRAVERSE(&agents, p, list) {
1962  int unlock_channel = 1;
1963 
1964  ast_channel_lock(chan);
1965  ast_mutex_lock(&p->lock);
1966  if (!strcmp(p->agent, user) &&
1967  !strcmp(p->password, pass) && !p->pending) {
1968 
1969  /* Set Channel Specific Agent Overrides */
1970  if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1971  if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1972  p->ackcall = 1;
1973  } else {
1974  p->ackcall = 0;
1975  }
1976  tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1977  ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent);
1979  } else {
1980  p->ackcall = ackcall;
1981  }
1982  if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
1983  p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
1984  if (p->autologoff < 0)
1985  p->autologoff = 0;
1986  tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1987  ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent);
1989  } else {
1990  p->autologoff = autologoff;
1991  }
1992  if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
1993  p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
1994  if (p->wrapuptime < 0)
1995  p->wrapuptime = 0;
1996  tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1997  ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent);
1999  } else {
2000  p->wrapuptime = wrapuptime;
2001  }
2002  tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
2003  if (!ast_strlen_zero(tmpoptions)) {
2004  p->acceptdtmf = *tmpoptions;
2005  ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent);
2007  }
2008  tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF");
2009  if (!ast_strlen_zero(tmpoptions)) {
2010  p->enddtmf = *tmpoptions;
2011  ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent);
2013  }
2014  ast_channel_unlock(chan);
2015  unlock_channel = 0;
2016  /* End Channel Specific Agent Overrides */
2017 
2018  if (!p->chan) {
2019  /* Ensure nobody else can be this agent until we're done. */
2020  p->chan = chan;
2021 
2022  p->acknowledged = 0;
2023 
2024  if (!res) {
2025  res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
2026  if (res)
2027  ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats)));
2028  }
2029  if (!res) {
2031  if (res)
2032  ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats)));
2033  }
2034  if (!res && play_announcement == 1) {
2035  ast_mutex_unlock(&p->lock);
2037  res = ast_streamfile(chan, "agent-loginok", chan->language);
2038  if (!res) {
2039  ast_waitstream(chan, "");
2040  }
2042  ast_mutex_lock(&p->lock);
2043  }
2044 
2045  if (!res) {
2046  long logintime;
2047  char agent[AST_MAX_AGENT];
2048 
2049  snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
2050 
2051  /* Login this channel and wait for it to go away */
2053  S_OR(p->moh, NULL),
2054  !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
2055 
2056  /* Must be done after starting HOLD. */
2057  p->lastdisc = ast_tvnow();
2058  time(&p->loginstart);
2059 
2060  manager_event(EVENT_FLAG_AGENT, "Agentlogin",
2061  "Agent: %s\r\n"
2062  "Channel: %s\r\n"
2063  "Uniqueid: %s\r\n",
2064  p->agent, chan->name, chan->uniqueid);
2065  if (update_cdr && chan->cdr)
2066  snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "%s", agent);
2067  ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
2068  ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
2070 
2071  ast_mutex_unlock(&p->lock);
2073 
2074  while (res >= 0) {
2075  ast_mutex_lock(&p->lock);
2076  if (p->deferlogoff) {
2077  p->deferlogoff = 0;
2079  ast_mutex_unlock(&p->lock);
2080  break;
2081  }
2082  ast_mutex_unlock(&p->lock);
2083 
2085  ast_mutex_lock(&p->lock);
2086  if (p->lastdisc.tv_sec) {
2087  if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
2088  ast_debug(1, "Wrapup time for %s expired!\n", agent);
2089  p->lastdisc = ast_tv(0, 0);
2091  if (p->ackcall) {
2092  check_beep(p, 0);
2093  } else {
2094  check_availability(p, 0);
2095  }
2096  }
2097  }
2098  ast_mutex_unlock(&p->lock);
2100 
2101  /* Synchronize channel ownership between call to agent and itself. */
2102  ast_mutex_lock(&p->lock);
2103  if (p->app_lock_flag) {
2106  if (ast_check_hangup(chan)) {
2107  /* Agent hungup */
2108  ast_mutex_unlock(&p->lock);
2109  break;
2110  }
2111  }
2112  ast_mutex_unlock(&p->lock);
2113 
2114  if (p->ackcall) {
2115  res = agent_ack_sleep(p);
2116  if (res == 1) {
2118  ast_mutex_lock(&p->lock);
2119  check_availability(p, 0);
2120  ast_mutex_unlock(&p->lock);
2122  }
2123  } else {
2124  res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2125  }
2126  }
2127  ast_mutex_lock(&p->lock);
2128 
2129  /* Logoff this channel */
2130  p->chan = NULL;
2131  logintime = time(NULL) - p->loginstart;
2132  p->loginstart = 0;
2133 
2134  /* Synchronize channel ownership between call to agent and itself. */
2135  if (p->app_lock_flag) {
2138  }
2139 
2140  if (p->owner) {
2141  ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2142  }
2143 
2144  p->acknowledged = 0;
2145  ast_mutex_unlock(&p->lock);
2146 
2148  manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2149  "Agent: %s\r\n"
2150  "Logintime: %ld\r\n"
2151  "Uniqueid: %s\r\n",
2152  p->agent, logintime, chan->uniqueid);
2153  ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2154  ast_verb(2, "Agent '%s' logged out\n", p->agent);
2155 
2156  /* If there is no owner, go ahead and kill it now */
2157  if (p->dead && !p->owner) {
2158  agent_pvt_destroy(p);
2159  }
2161  } else {
2162  /* Agent hung up before could be logged in. */
2163  p->chan = NULL;
2164 
2165  ast_mutex_unlock(&p->lock);
2166  }
2167  res = -1;
2168  } else {
2169  ast_mutex_unlock(&p->lock);
2170  errmsg = "agent-alreadyon";
2171  }
2172  break;
2173  }
2174  ast_mutex_unlock(&p->lock);
2175  if (unlock_channel) {
2176  ast_channel_unlock(chan);
2177  }
2178  }
2180 
2181  if (!res && (max_login_tries==0 || tries < max_login_tries))
2182  res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2183  }
2184 
2185  if (!res)
2186  res = ast_safe_sleep(chan, 500);
2187 
2188  return -1;
2189 }
2190 
2191 /*!
2192  * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2193  *
2194  * \param chan
2195  * \param data
2196  * \returns
2197  * \sa login_exec(), load_module().
2198  */
2199 static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
2200 {
2201  int exitifnoagentid = 0;
2202  int nowarnings = 0;
2203  int changeoutgoing = 0;
2204  int res = 0;
2205  char agent[AST_MAX_AGENT];
2206 
2207  if (data) {
2208  if (strchr(data, 'd'))
2209  exitifnoagentid = 1;
2210  if (strchr(data, 'n'))
2211  nowarnings = 1;
2212  if (strchr(data, 'c'))
2213  changeoutgoing = 1;
2214  }
2215  if (chan->caller.id.number.valid
2216  && !ast_strlen_zero(chan->caller.id.number.str)) {
2217  const char *tmp;
2218  char agentvar[AST_MAX_BUF];
2219  snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID,
2220  chan->caller.id.number.str);
2221  if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2222  struct agent_pvt *p;
2223  ast_copy_string(agent, tmp, sizeof(agent));
2225  AST_LIST_TRAVERSE(&agents, p, list) {
2226  if (!strcasecmp(p->agent, tmp)) {
2227  if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2228  __agent_start_monitoring(chan, p, 1);
2229  break;
2230  }
2231  }
2233 
2234  } else {
2235  res = -1;
2236  if (!nowarnings)
2237  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);
2238  }
2239  } else {
2240  res = -1;
2241  if (!nowarnings)
2242  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");
2243  }
2244  if (res) {
2245  if (exitifnoagentid)
2246  return res;
2247  }
2248  return 0;
2249 }
2250 
2251 /*! \brief Part of PBX channel interface */
2252 static int agent_devicestate(void *data)
2253 {
2254  struct agent_pvt *p;
2255  const char *device = data;
2256  int res = AST_DEVICE_INVALID;
2257 
2258  if (device[0] == '@' || device[0] == ':') {
2259  /* Device state of groups not supported. */
2260  return AST_DEVICE_INVALID;
2261  }
2262 
2263  /* Want device state of a specific agent. */
2265  AST_LIST_TRAVERSE(&agents, p, list) {
2266  ast_mutex_lock(&p->lock);
2267  if (!p->pending && !strcmp(device, p->agent)) {
2268  if (p->owner) {
2269  res = AST_DEVICE_BUSY;
2270  } else if (p->chan) {
2271  if (p->lastdisc.tv_sec || p->deferlogoff) {
2272  /* Agent is in wrapup time so unavailable for another call. */
2273  res = AST_DEVICE_INUSE;
2274  } else {
2275  res = AST_DEVICE_NOT_INUSE;
2276  }
2277  } else {
2278  res = AST_DEVICE_UNAVAILABLE;
2279  }
2280  ast_mutex_unlock(&p->lock);
2281  break;
2282  }
2283  ast_mutex_unlock(&p->lock);
2284  }
2286  return res;
2287 }
2288 
2289 /*!
2290  * \note This function expects the agent list to be locked
2291  */
2292 static struct agent_pvt *find_agent(char *agentid)
2293 {
2294  struct agent_pvt *cur;
2295 
2296  AST_LIST_TRAVERSE(&agents, cur, list) {
2297  if (!strcmp(cur->agent, agentid))
2298  break;
2299  }
2300 
2301  return cur;
2302 }
2303 
2304 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2305 {
2306  char *parse;
2308  AST_APP_ARG(agentid);
2309  AST_APP_ARG(item);
2310  );
2311  char *tmp;
2312  struct agent_pvt *agent;
2313 
2314  buf[0] = '\0';
2315 
2316  if (ast_strlen_zero(data)) {
2317  ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2318  return -1;
2319  }
2320 
2321  parse = ast_strdupa(data);
2322 
2323  AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2324  if (!args.item)
2325  args.item = "status";
2326 
2328 
2329  if (!(agent = find_agent(args.agentid))) {
2331  ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2332  return -1;
2333  }
2334 
2335  if (!strcasecmp(args.item, "status")) {
2336  char *status = "LOGGEDOUT";
2337  if (agent->chan) {
2338  status = "LOGGEDIN";
2339  }
2340  ast_copy_string(buf, status, len);
2341  } else if (!strcasecmp(args.item, "password"))
2342  ast_copy_string(buf, agent->password, len);
2343  else if (!strcasecmp(args.item, "name"))
2344  ast_copy_string(buf, agent->name, len);
2345  else if (!strcasecmp(args.item, "mohclass"))
2346  ast_copy_string(buf, agent->moh, len);
2347  else if (!strcasecmp(args.item, "channel")) {
2348  if (agent->chan) {
2349  ast_channel_lock(agent->chan);
2350  ast_copy_string(buf, agent->chan->name, len);
2351  ast_channel_unlock(agent->chan);
2352  tmp = strrchr(buf, '-');
2353  if (tmp)
2354  *tmp = '\0';
2355  }
2356  } else if (!strcasecmp(args.item, "fullchannel")) {
2357  if (agent->chan) {
2358  ast_channel_lock(agent->chan);
2359  ast_copy_string(buf, agent->chan->name, len);
2360  ast_channel_unlock(agent->chan);
2361  }
2362  } else if (!strcasecmp(args.item, "exten")) {
2363  buf[0] = '\0';
2364  }
2365 
2367 
2368  return 0;
2369 }
2370 
2372  .name = "AGENT",
2373  .read = function_agent,
2374 };
2375 
2376 /*!
2377  * \internal
2378  * \brief Callback used to generate the agents tree.
2379  * \param[in] search The search pattern tree.
2380  * \retval NULL on error.
2381  * \retval non-NULL The generated tree.
2382  */
2383 static int agents_data_provider_get(const struct ast_data_search *search,
2384  struct ast_data *data_root)
2385 {
2386  struct agent_pvt *p;
2387  struct ast_data *data_agent, *data_channel, *data_talkingto;
2388 
2390  AST_LIST_TRAVERSE(&agents, p, list) {
2391  struct ast_channel *owner;
2392 
2393  data_agent = ast_data_add_node(data_root, "agent");
2394  if (!data_agent) {
2395  continue;
2396  }
2397 
2398  ast_mutex_lock(&p->lock);
2399  owner = agent_lock_owner(p);
2400 
2401  if (!(p->pending)) {
2402  ast_data_add_str(data_agent, "id", p->agent);
2403  ast_data_add_structure(agent_pvt, data_agent, p);
2404 
2405  ast_data_add_bool(data_agent, "logged", p->chan ? 1 : 0);
2406  if (p->chan) {
2407  data_channel = ast_data_add_node(data_agent, "loggedon");
2408  if (!data_channel) {
2409  ast_mutex_unlock(&p->lock);
2410  ast_data_remove_node(data_root, data_agent);
2411  if (owner) {
2412  ast_channel_unlock(owner);
2413  owner = ast_channel_unref(owner);
2414  }
2415  continue;
2416  }
2417  ast_channel_data_add_structure(data_channel, p->chan, 0);
2418  if (owner && ast_bridged_channel(owner)) {
2419  data_talkingto = ast_data_add_node(data_agent, "talkingto");
2420  if (!data_talkingto) {
2421  ast_mutex_unlock(&p->lock);
2422  ast_data_remove_node(data_root, data_agent);
2423  if (owner) {
2424  ast_channel_unlock(owner);
2425  owner = ast_channel_unref(owner);
2426  }
2427  continue;
2428  }
2429  ast_channel_data_add_structure(data_talkingto, ast_bridged_channel(owner), 0);
2430  }
2431  } else {
2432  ast_data_add_node(data_agent, "talkingto");
2433  ast_data_add_node(data_agent, "loggedon");
2434  }
2435  ast_data_add_str(data_agent, "musiconhold", p->moh);
2436  }
2437 
2438  if (owner) {
2439  ast_channel_unlock(owner);
2440  owner = ast_channel_unref(owner);
2441  }
2442 
2443  ast_mutex_unlock(&p->lock);
2444 
2445  /* if this agent doesn't match remove the added agent. */
2446  if (!ast_data_search_match(search, data_agent)) {
2447  ast_data_remove_node(data_root, data_agent);
2448  }
2449  }
2451 
2452  return 0;
2453 }
2454 
2458 };
2459 
2460 static const struct ast_data_entry agents_data_providers[] = {
2461  AST_DATA_ENTRY("asterisk/channel/agent/list", &agents_data_provider),
2462 };
2463 
2464 /*!
2465  * \brief Initialize the Agents module.
2466  * This function is being called by Asterisk when loading the module.
2467  * Among other things it registers applications, cli commands and reads the cofiguration file.
2468  *
2469  * \returns int Always 0.
2470  */
2471 static int load_module(void)
2472 {
2473  /* Make sure we can register our agent channel type */
2475  ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2476  return AST_MODULE_LOAD_FAILURE;
2477  }
2478  /* Read in the config */
2479  if (!read_agent_config(0))
2480  return AST_MODULE_LOAD_DECLINE;
2481  /* Dialplan applications */
2484 
2485  /* data tree */
2487 
2488  /* Manager commands */
2491 
2492  /* CLI Commands */
2494 
2495  /* Dialplan Functions */
2497 
2498  return AST_MODULE_LOAD_SUCCESS;
2499 }
2500 
2501 static int reload(void)
2502 {
2503  return read_agent_config(1);
2504 }
2505 
2506 static int unload_module(void)
2507 {
2508  struct agent_pvt *p;
2509  /* First, take us out of the channel loop */
2511  /* Unregister dialplan functions */
2513  /* Unregister CLI commands */
2515  /* Unregister dialplan applications */
2518  /* Unregister manager command */
2519  ast_manager_unregister("Agents");
2520  ast_manager_unregister("AgentLogoff");
2521  /* Unregister the data tree */
2522  ast_data_unregister(NULL);
2523  /* Unregister channel */
2525  /* Hangup all interfaces if they have an owner */
2526  while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2527  if (p->owner)
2529  ast_free(p);
2530  }
2532  return 0;
2533 }
2534 
2536  .load = load_module,
2537  .unload = unload_module,
2538  .reload = reload,
2539  .load_pri = AST_MODPRI_CHANNEL_DRIVER,
2540  .nonoptreq = "res_monitor,chan_local",
2541  );
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1916
unsigned long long ast_group_t
Definition: channel.h:175
static char pass[512]
union ast_frame_subclass subclass
Definition: frame.h:146
int ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2804
static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
Called by the AgentMonitorOutgoing application (from the dial plan).
Definition: chan_agent.c:2199
#define ast_channel_lock(chan)
Definition: channel.h:2466
#define X_REC_IN
Definition: monitor.h:35
Main Channel structure associated with a channel.
Definition: channel.h:742
char password[AST_MAX_AGENT]
Definition: chan_agent.c:270
Music on hold handling.
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
char acceptdtmf
Definition: chan_agent.c:260
static int read_agent_config(int reload)
Definition: chan_agent.c:1112
char * str
Subscriber phone number (Malloced)
Definition: channel.h:241
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:946
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
const char *const type
Definition: channel.h:508
ast_mutex_t lock
Definition: chan_agent.c:253
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:2068
static int check_beep(struct agent_pvt *newlyavailable, int needlock)
Definition: chan_agent.c:1333
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
The data tree to be returned by the callbacks and managed by functions local to this file...
Definition: data.c:85
int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: app.c:178
struct ast_frame ast_null_frame
Definition: frame.c:131
struct ast_party_caller caller
Channel Caller ID information.
Definition: channel.h:804
static int agents_data_provider_get(const struct ast_data_search *search, struct ast_data *data_root)
Definition: chan_agent.c:2383
#define AST_AGENT_FD
Definition: channel.h:165
int priority
Definition: channel.h:841
const ast_string_field uniqueid
Definition: channel.h:787
#define ast_strdup(a)
Definition: astmm.h:109
int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
Weird function made for call transfers.
Definition: channel.c:6110
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
time_t start
Definition: chan_agent.c:263
format_t writeformat
Definition: channel.h:854
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
#define ast_test_flag(p, flag)
Definition: utils.h:63
Device state management.
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4393
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
Definition: channel.c:938
void * tech_pvt
Definition: channel.h:744
#define force_inline
Definition: compiler.h:29
char context[AST_MAX_CONTEXT]
Definition: channel.h:868
static char * agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: chan_agent.c:1615
struct ast_channel * ast_channel_release(struct ast_channel *chan)
Unlink and release reference to a channel.
Definition: channel.c:1921
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define DEADLOCK_AVOIDANCE(lock)
Definition: lock.h:485
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
static struct ast_channel * agent_get_base_channel(struct ast_channel *chan)
return the channel or base channel if one exists. This function assumes the channel it is called on i...
Definition: chan_agent.c:892
#define LOG_WARNING
Definition: logger.h:144
static int ackcall
Definition: chan_agent.c:224
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
int acknowledged
Definition: chan_agent.c:267
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
int wrapuptime
Definition: chan_agent.c:265
#define DEFAULT_ACCEPTDTMF
Definition: chan_agent.c:218
void ast_verbose(const char *fmt,...)
Definition: logger.c:1568
#define AST_FRAME_DTMF
Definition: frame.h:128
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: chan_agent.c:2304
This entries are for multiple registers.
Definition: data.h:253
static int wrapuptime
Definition: chan_agent.c:223
Data retrieval API.
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
static struct ast_custom_function agent_function
Definition: chan_agent.c:2371
void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt,...)
Definition: logger.c:479
format_t rawwriteformat
Definition: channel.h:856
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4383
static char recordformatext[AST_MAX_BUF]
Definition: chan_agent.c:235
Definition: cli.h:146
static int agent_digit_begin(struct ast_channel *ast, char digit)
Definition: chan_agent.c:807
#define DATA_EXPORT_AGENT(MEMBER)
Definition: chan_agent.c:282
Configuration File Parser.
static char recordformat[AST_MAX_BUF]
Definition: chan_agent.c:234
int pending
Definition: chan_agent.c:255
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
Indicates condition of channel, with payload.
Definition: channel.c:4447
static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
Definition: chan_agent.c:818
#define ast_cond_wait(cond, mutex)
Definition: lock.h:171
#define ast_cond_init(cond, attr)
Definition: lock.h:167
format_t ast_best_codec(format_t fmts)
Pick the best audio codec.
Definition: channel.c:1062
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:142
int abouttograb
Definition: chan_agent.c:256
struct ast_data * ast_data_add_bool(struct ast_data *root, const char *childname, unsigned int boolean)
Add a boolean node type.
Definition: data.c:2344
char moh[80]
Definition: chan_agent.c:268
static struct agent_pvt * add_agent(const char *agent, int pending)
Definition: chan_agent.c:433
#define ast_assert(a)
Definition: utils.h:738
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2135
#define ast_mutex_lock(a)
Definition: lock.h:155
struct ast_cdr * cdr
Definition: channel.h:766
int ast_channel_register(const struct ast_channel_tech *tech)
Register a channel technology (a new channel driver) Called by a channel module to register the kind ...
Definition: channel.c:907
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
char * text
Definition: app_queue.c:1091
format_t nativeformats
Definition: channel.h:852
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:90
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
const char * ast_state2str(enum ast_channel_state)
Gives the string form of a given channel state.
Definition: channel.c:1007
format_t codec
Definition: frame.h:137
int ast_senddigit_begin(struct ast_channel *chan, char digit)
Send a DTMF digit to a channel.
Definition: channel.c:4705
const char * data
Definition: channel.h:755
I/O Management (derived from Cheops-NG)
ast_cond_t login_wait_cond
Definition: chan_agent.c:274
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:841
void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
Definition: res_monitor.c:867
const ast_string_field linkedid
Definition: channel.h:787
#define LOG_DEBUG
Definition: logger.h:122
#define AST_DATA_ENTRY(__path, __handler)
Definition: data.h:260
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
#define GETAGENTBYCALLERID
Definition: chan_agent.c:241
format_t rawreadformat
Definition: channel.h:855
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
#define ast_cond_signal(cond)
Definition: lock.h:169
static int recordagentcalls
Definition: chan_agent.c:233
#define ast_verb(level,...)
Definition: logger.h:243
Definition: ael.tab.c:203
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
static char urlprefix[AST_MAX_BUF]
Definition: chan_agent.c:236
#define AST_MAX_BUF
Definition: chan_agent.c:212
const char * line
Definition: cli.h:156
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
struct ast_channel * ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const char *linkedid, const int amaflag, const char *name_fmt,...)
Definition: channel.c:9825
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Definition: pbx.c:10475
static struct ast_channel * agent_lock_owner(struct agent_pvt *pvt)
Locks the owning channel for a LOCKED pvt while obeying locking order. The pvt must enter this functi...
Definition: chan_agent.c:382
static int agent_hangup(struct ast_channel *ast)
Definition: chan_agent.c:908
#define X_REC_OUT
Definition: monitor.h:36
static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
Definition: chan_agent.c:574
char name[AST_MAX_AGENT]
Definition: chan_agent.c:271
int ackcall
Definition: chan_agent.c:258
String fields in structures.
Utility functions.
#define AST_MAX_FILENAME_LEN
Definition: chan_agent.c:213
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:1860
pthread_cond_t ast_cond_t
Definition: lock.h:144
struct ast_channel * chan
Definition: chan_agent.c:277
int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
Set CDR user field for channel (stored in CDR)
Definition: cdr.c:1057
static char beep[AST_MAX_BUF]
Definition: chan_agent.c:239
struct ast_party_id id
Caller party ID.
Definition: channel.h:370
int ast_set_write_format(struct ast_channel *chan, format_t format)
Sets write format on channel chan Set write format for channel to whichever component of &quot;format&quot; is ...
Definition: channel.c:5307
int ast_set_read_format(struct ast_channel *chan, format_t format)
Sets read format on channel chan Set read format for channel to whichever component of &quot;format&quot; is be...
Definition: channel.c:5301
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
int ast_channel_data_add_structure(struct ast_data *tree, struct ast_channel *chan, int add_bridged)
Insert into an astdata tree, the channel structure.
Definition: channel.c:357
#define AST_DATA_HANDLER_VERSION
The Data API structures version.
Definition: data.h:204
const char * value
Definition: config.h:79
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:516
static int load_module(void)
Initialize the Agents module. This function is being called by Asterisk when loading the module...
Definition: chan_agent.c:2471
#define CHECK_FORMATS(ast, p)
Definition: chan_agent.c:296
General Asterisk PBX channel definitions.
static int updatecdr
Definition: chan_agent.c:238
const int fd
Definition: cli.h:153
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
#define ast_manager_register_xml(a, b, c)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:172
int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration)
Send a DTMF digit to a channel.
Definition: channel.c:4755
int deferlogoff
Definition: chan_agent.c:259
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
const int n
Definition: cli.h:159
static struct ast_data_entry agents_data_providers[]
Definition: chan_agent.c:2460
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
Access Control of various sorts.
static int agent_sendtext(struct ast_channel *ast, const char *text)
Definition: chan_agent.c:737
struct ast_data * ast_data_add_node(struct ast_data *root, const char *childname)
Add a container child.
Definition: data.c:2317
int datalen
Definition: frame.h:148
Scheduler Routines (derived from cheops)
#define ast_data_unregister(path)
Definition: data.h:394
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
Definition: config.c:810
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:83
int ast_softhangup(struct ast_channel *chan, int reason)
Softly hangup up a channel.
Definition: channel.c:2746
long int ast_random(void)
Definition: utils.c:1640
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:191
static struct ast_channel * agent_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
Part of the Asterisk PBX interface.
Definition: chan_agent.c:1370
static int login_exec(struct ast_channel *chan, const char *data)
Log in agent application.
Definition: chan_agent.c:1869
static char enddtmf
Definition: chan_agent.c:228
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:108
const char * name
Definition: config.h:77
int ast_monitor_start(struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock, int stream_action)
Start monitoring a channel.
Definition: res_monitor.c:290
time_t loginstart
Definition: chan_agent.c:262
char channel[AST_MAX_EXTENSION]
Definition: cdr.h:92
struct ast_channel * _bridge
Definition: channel.h:748
ast_group_t ast_get_group(const char *s)
Definition: channel.c:7987
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
static const char config[]
Definition: chan_agent.c:204
#define ast_data_register_multiple(data_entries, entries)
Definition: data.h:377
ast_cond_t app_complete_cond
Definition: chan_agent.c:273
Structure to describe a channel &quot;technology&quot;, ie a channel driver See for examples: ...
Definition: channel.h:507
static char * complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
Definition: chan_agent.c:1675
Core PBX routines and definitions.
static char agentgoodbye[AST_MAX_FILENAME_LEN]
Definition: chan_agent.c:231
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f)
Queue one or more frames to a channel&#39;s frame queue.
Definition: channel.c:1558
ast_group_t group
Definition: chan_agent.c:266
struct ast_channel * owner
Definition: chan_agent.c:276
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:806
static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
Definition: chan_agent.c:726
#define ast_data_add_structure(structure_name, root, structure)
Definition: data.h:620
The list of nodes with their search requirement.
Definition: data.c:122
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
const char *const * argv
Definition: cli.h:155
static int agent_logoff(const char *agent, int soft)
Definition: chan_agent.c:1574
static const char pa_family[]
Definition: chan_agent.c:215
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static char moh[80]
Definition: chan_agent.c:209
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
static int check_availability(struct agent_pvt *newlyavailable, int needlock)
Definition: chan_agent.c:1270
int app_sleep_cond
Definition: chan_agent.c:275
static int action_agents(struct mansession *s, const struct message *m)
Definition: chan_agent.c:1494
char * ast_getformatname(format_t format)
Get the name of a format.
Definition: frame.c:578
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
int64_t format_t
Definition: frame_defs.h:32
#define CLEANUP(ast, p)
Cleanup moves all the relevant FD&#39;s from the 2nd to the first, but retains things properly for a timi...
Definition: chan_agent.c:318
static int agent_devicestate(void *data)
Part of PBX channel interface.
Definition: chan_agent.c:2252
Structure representing an agent.
Definition: chan_agent.c:252
static struct @350 args
#define CLI_SHOWUSAGE
Definition: cli.h:44
void ast_data_remove_node(struct ast_data *root, struct ast_data *child)
Remove a node that was added using ast_data_add_.
Definition: data.c:2486
char agent[AST_MAX_AGENT]
Definition: chan_agent.c:269
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:1615
static const char app3[]
Definition: chan_agent.c:207
struct agent_pvt::@78 list
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define AST_TIMING_FD
Definition: channel.h:164
enum ast_channel_state _state
Definition: channel.h:839
struct ast_channel * ast_bridged_channel(struct ast_channel *chan)
Find bridged channel.
Definition: channel.c:7160
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: utils.c:1587
static char * agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: chan_agent.c:1779
const ast_string_field name
Definition: channel.h:787
static int agent_start_monitoring(struct ast_channel *ast, int needlock)
Definition: chan_agent.c:602
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define EVENT_FLAG_AGENT
Definition: manager.h:76
static char savecallsin[AST_MAX_BUF]
Definition: chan_agent.c:237
#define ast_cond_destroy(cond)
Definition: lock.h:168
#define LOG_NOTICE
Definition: logger.h:133
static struct ast_cli_entry cli_agents[]
Definition: chan_agent.c:1853
int ast_channel_sendhtml(struct ast_channel *channel, int subclass, const char *data, int datalen)
Sends HTML on given channel Send HTML or URL on link.
Definition: channel.c:5907
struct ast_bridge * bridge
Definition: channel.h:865
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static int autologoffunavail
Definition: chan_agent.c:226
int fdno
Definition: channel.h:834
#define ast_channel_unlock(chan)
Definition: channel.h:2467
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
static int autologoff
Definition: chan_agent.c:222
#define AST_CAUSE_UNREGISTERED
Definition: causes.h:153
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
static char * agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: chan_agent.c:1702
static int agent_call(struct ast_channel *ast, char *dest, int timeout)
Definition: chan_agent.c:829
static struct ast_format f[]
Definition: format_g726.c:181
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition: channel.c:4916
const char * word
Definition: cli.h:157
#define DEFAULT_ENDDTMF
Definition: chan_agent.c:219
static char acceptdtmf
Definition: chan_agent.c:227
static int agent_answer(struct ast_channel *ast)
Definition: chan_agent.c:568
static int maxlogintries
Definition: chan_agent.c:230
static const char type[]
Definition: chan_nbs.c:57
unsigned int flags
Definition: chan_agent.c:278
structure to hold users read from users.conf
Structure used to handle boolean flags.
Definition: utils.h:200
static int reload(void)
Definition: chan_agent.c:2501
const char * usage
Definition: cli.h:171
static int agent_cleanup(struct agent_pvt *p)
Definition: chan_agent.c:544
struct timeval lastdisc
Definition: chan_agent.c:264
static int agent_cont_sleep(void *data)
Definition: chan_agent.c:976
static int agent_ack_sleep(struct agent_pvt *p)
Definition: chan_agent.c:999
#define CLI_SUCCESS
Definition: cli.h:43
int app_lock_flag
Definition: chan_agent.c:272
static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
Definition: chan_agent.c:787
static char secret[50]
Definition: chan_h323.c:148
#define AST_FORMAT_SLINEAR
Definition: frame.h:254
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2491
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3539
Standard Command Line Interface.
format_t readformat
Definition: channel.h:853
static int agent_write(struct ast_channel *ast, struct ast_frame *f)
Definition: chan_agent.c:748
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
Definition: chan_agent.c:773
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:179
char enddtmf
Definition: chan_agent.c:261
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
const int pos
Definition: cli.h:158
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1343
Channel monitoring.
static const char app[]
Definition: chan_agent.c:206
int ast_setstate(struct ast_channel *chan, enum ast_channel_state)
Change the state of a channel.
Definition: channel.c:7119
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:3086
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define AST_CAUSE_BUSY
Definition: causes.h:148
static const char agent_logoff_usage[]
Definition: chan_agent.c:1848
Data structure associated with a single frame of data.
Definition: frame.h:142
#define AST_DATA_STRUCTURE(__struct, __name)
Definition: data.h:300
Internal Asterisk hangup causes.
static struct ast_channel * agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
Definition: chan_agent.c:1046
const char * name
Definition: pbx.h:96
static long count_agents(const char *const caller, const int core_id_exception)
Definition: ccss.c:2243
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
enum ast_frame_type frametype
Definition: frame.h:144
struct ast_variable * next
Definition: config.h:82
int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int(*cond)(void *), void *data)
Wait for a specified amount of time, looking for hangups and a condition argument.
Definition: channel.c:1845
#define ast_mutex_init(pmutex)
Definition: lock.h:152
#define ast_channel_trylock(chan)
Definition: channel.h:2468
#define ast_frfree(fr)
Definition: frame.h:583
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
static struct ast_channel * agent_new(struct agent_pvt *p, int state, const char *linkedid)
Create new agent channel.
Definition: chan_agent.c:1063
Call Parking and Pickup API Includes code and algorithms from the Zapata library. ...
#define AST_MAX_AGENT
Definition: chan_agent.c:211
struct ast_channel_monitor * monitor
Definition: channel.h:769
#define ast_mutex_destroy(a)
Definition: lock.h:154
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
Definition: app.h:619
static force_inline int powerof(unsigned int d)
Definition: chan_agent.c:1474
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
static void agent_pvt_destroy(struct agent_pvt *doomed)
Definition: chan_agent.c:417
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:219
struct ast_data * ast_data_add_str(struct ast_data *root, const char *childname, const char *string)
Add a string node type.
Definition: data.c:2401
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:2130
Asterisk module definitions.
static struct agent_pvt * find_agent(char *agentid)
Definition: chan_agent.c:2292
static snd_pcm_format_t format
Definition: chan_alsa.c:93
union ast_frame::@172 data
static struct ast_frame * agent_read(struct ast_channel *ast)
Definition: chan_agent.c:607
struct ast_channel_tech * tech
Definition: channel.h:743
Persistant data storage (akin to *doze registry)
static struct ast_data_handler agents_data_provider
Definition: chan_agent.c:2455
int(*const indicate)(struct ast_channel *c, int condition, const void *data, size_t datalen)
Indicate a particular condition (e.g. AST_CONTROL_BUSY or AST_CONTROL_RINGING or AST_CONTROL_CONGESTI...
Definition: channel.h:576
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:247
int ast_data_search_match(const struct ast_data_search *search, struct ast_data *data)
Check the current generated node to know if it matches the search condition.
Definition: data.c:1458
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
static int endcall
Definition: chan_agent.c:225
const ast_string_field language
Definition: channel.h:787
uint32_t version
Structure version.
Definition: data.h:247
struct ast_cdr * ast_cdr_alloc(void)
Allocate a CDR record.
Definition: cdr.c:499
char exten[AST_MAX_EXTENSION]
Definition: channel.h:869
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
static int action_agent_logoff(struct mansession *s, const struct message *m)
Definition: chan_agent.c:1653
Structure for mutex and tracking information.
Definition: lock.h:121
static int unload_module(void)
Definition: chan_agent.c:2506
The structure of the node handler.
Definition: data.h:245
static const char tdesc[]
Definition: chan_agent.c:203
int autologoff
Definition: chan_agent.c:257
int ast_manager_unregister(char *action)
Unregister a registered manager command.
Definition: manager.c:5355
jack_status_t status
Definition: app_jack.c:143
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
static int update_cdr
queues.conf [general] option
Definition: app_queue.c:1076
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
#define ast_mutex_unlock(a)
Definition: lock.h:156
int ast_sendtext(struct ast_channel *chan, const char *text)
Sends text to a channel.
Definition: channel.c:4687
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
static ast_group_t group
Definition: chan_agent.c:221
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:292
static struct ast_channel_tech agent_tech
Channel interface description for PBX integration.
Definition: chan_agent.c:349