Wed Jan 8 2020 09:49:39

Asterisk developer's documentation


app_followme.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * A full-featured Find-Me/Follow-Me Application
5  *
6  * Copyright (C) 2005-2006, BJ Weschke All Rights Reserved.
7  *
8  * BJ Weschke <bweschke@btwtech.com>
9  *
10  * This code is released by the author with no restrictions on usage.
11  *
12  * See http://www.asterisk.org for more information about
13  * the Asterisk project. Please do not directly contact
14  * any of the maintainers of this project for assistance;
15  * the project provides a web site, mailing lists and IRC
16  * channels for your use.
17  *
18  */
19 
20 /*! \file
21  *
22  * \brief Find-Me Follow-Me application
23  *
24  * \author BJ Weschke <bweschke@btwtech.com>
25  *
26  * \arg See \ref Config_followme
27  *
28  * \ingroup applications
29  */
30 
31 /*** MODULEINFO
32  <depend>chan_local</depend>
33  <support_level>core</support_level>
34  ***/
35 
36 #include "asterisk.h"
37 
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 372390 $")
39 
40 #include <signal.h>
41 
42 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
43 #include "asterisk/lock.h"
44 #include "asterisk/file.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/module.h"
48 #include "asterisk/translate.h"
49 #include "asterisk/say.h"
50 #include "asterisk/features.h"
51 #include "asterisk/musiconhold.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/manager.h"
54 #include "asterisk/config.h"
55 #include "asterisk/utils.h"
56 #include "asterisk/causes.h"
57 #include "asterisk/astdb.h"
58 #include "asterisk/dsp.h"
59 #include "asterisk/app.h"
60 
61 /*** DOCUMENTATION
62  <application name="FollowMe" language="en_US">
63  <synopsis>
64  Find-Me/Follow-Me application.
65  </synopsis>
66  <syntax>
67  <parameter name="followmeid" required="true" />
68  <parameter name="options">
69  <optionlist>
70  <option name="a">
71  <para>Record the caller's name so it can be announced to the
72  callee on each step.</para>
73  </option>
74  <option name="d">
75  <para>Disable the 'Please hold while we try to connect your call' announcement.</para>
76  </option>
77  <option name="I">
78  <para>Asterisk will ignore any connected line update requests
79  it may receive on this dial attempt.</para>
80  </option>
81  <option name="n">
82  <para>Playback the unreachable status message if we've run out
83  of steps or the callee has elected not to be reachable.</para>
84  </option>
85  <option name="s">
86  <para>Playback the incoming status message prior to starting
87  the follow-me step(s)</para>
88  </option>
89  </optionlist>
90  </parameter>
91  </syntax>
92  <description>
93  <para>This application performs Find-Me/Follow-Me functionality for the caller
94  as defined in the profile matching the <replaceable>followmeid</replaceable> parameter in
95  <filename>followme.conf</filename>. If the specified <replaceable>followmeid</replaceable>
96  profile doesn't exist in <filename>followme.conf</filename>, execution will be returned
97  to the dialplan and call execution will continue at the next priority.</para>
98  <para>Returns -1 on hangup.</para>
99  </description>
100  </application>
101  ***/
102 
103 static char *app = "FollowMe";
104 
105 /*! Maximum accept/decline DTMF string plus terminator. */
106 #define MAX_YN_STRING 20
107 
108 /*! \brief Number structure */
109 struct number {
110  char number[512]; /*!< Phone Number(s) and/or Extension(s) */
111  long timeout; /*!< Dial Timeout, if used. */
112  int order; /*!< The order to dial in */
113  AST_LIST_ENTRY(number) entry; /*!< Next Number record */
114 };
115 
116 /*! \brief Data structure for followme scripts */
119  char name[AST_MAX_EXTENSION]; /*!< Name - FollowMeID */
120  char moh[MAX_MUSICCLASS]; /*!< Music On Hold Class to be used */
121  char context[AST_MAX_CONTEXT]; /*!< Context to dial from */
122  unsigned int active; /*!< Profile is active (1), or disabled (0). */
123  int realtime; /*!< Cached from realtime */
124  char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
125  char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
126  char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
127  char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
128  char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
129  char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
130  char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
131  char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
132 
133  AST_LIST_HEAD_NOLOCK(numbers, number) numbers; /*!< Head of the list of follow-me numbers */
134  AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
135  AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
136  AST_LIST_ENTRY(call_followme) entry; /*!< Next Follow-Me record */
137 };
138 
139 struct fm_args {
140  /*! Inbound (caller) channel */
141  struct ast_channel *chan;
142  char *mohclass;
144  /*! Winning outbound (callee) channel */
145  struct ast_channel *outbound;
146  /*! Accumulated connected line information from inbound call. */
147  struct ast_party_connected_line connected_in;
148  /*! Accumulated connected line information from outbound call. */
149  struct ast_party_connected_line connected_out;
150  /*! TRUE if connected line information from inbound call changed. */
151  unsigned int pending_in_connected_update:1;
152  /*! TRUE if connected line information from outbound call is available. */
153  unsigned int pending_out_connected_update:1;
154  int status;
155  char context[AST_MAX_CONTEXT];
156  char namerecloc[PATH_MAX];
157  char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
158  char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
159  char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
160  char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
161  char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
162  char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
163  char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
164  char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
165  struct ast_flags followmeflags;
166 };
167 
168 struct findme_user {
170  /*! Accumulated connected line information from outgoing call. */
172  long digts;
173  int ynidx;
174  int state;
175  char dialarg[256];
176  /*! Collected digits to accept/decline the call. */
177  char yn[MAX_YN_STRING];
178  /*! TRUE if call cleared. */
179  unsigned int cleared:1;
180  /*! TRUE if connected line information is available. */
181  unsigned int pending_connected_update:1;
182  AST_LIST_ENTRY(findme_user) entry;
183 };
184 
185 enum {
191 };
192 
199 });
200 
201 static const char *featuredigittostr;
202 static int featuredigittimeout = 5000; /*!< Feature Digit Timeout */
203 static const char *defaultmoh = "default"; /*!< Default Music-On-Hold Class */
204 
205 static char takecall[MAX_YN_STRING] = "1";
206 static char nextindp[MAX_YN_STRING] = "2";
207 static char callfromprompt[PATH_MAX] = "followme/call-from";
208 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
209 static char optionsprompt[PATH_MAX] = "followme/options";
210 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
211 static char statusprompt[PATH_MAX] = "followme/status";
212 static char sorryprompt[PATH_MAX] = "followme/sorry";
213 
214 
215 static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
217 
218 static void free_numbers(struct call_followme *f)
219 {
220  /* Free numbers attached to the profile */
221  struct number *prev;
222 
223  while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
224  /* Free the number */
225  ast_free(prev);
227 
228  while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
229  /* Free the blacklisted number */
230  ast_free(prev);
232 
233  while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
234  /* Free the whitelisted number */
235  ast_free(prev);
237 }
238 
239 
240 /*! \brief Allocate and initialize followme profile */
241 static struct call_followme *alloc_profile(const char *fmname)
242 {
243  struct call_followme *f;
244 
245  if (!(f = ast_calloc(1, sizeof(*f))))
246  return NULL;
247 
248  ast_mutex_init(&f->lock);
249  ast_copy_string(f->name, fmname, sizeof(f->name));
250  f->moh[0] = '\0';
251  f->context[0] = '\0';
252  ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
253  ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
254  ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
255  ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
256  ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
257  ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
258  ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
259  ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
263  return f;
264 }
265 
266 static void init_profile(struct call_followme *f)
267 {
268  f->active = 1;
269  ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
270 }
271 
272 
273 
274 /*! \brief Set parameter in profile from configuration file */
275 static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
276 {
277 
278  if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
279  ast_copy_string(f->moh, val, sizeof(f->moh));
280  else if (!strcasecmp(param, "context"))
281  ast_copy_string(f->context, val, sizeof(f->context));
282  else if (!strcasecmp(param, "takecall"))
283  ast_copy_string(f->takecall, val, sizeof(f->takecall));
284  else if (!strcasecmp(param, "declinecall"))
285  ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
286  else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
287  ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
288  else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))
290  else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))
291  ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
292  else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
293  ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
294  else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))
295  ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
296  else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))
297  ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
298  else if (failunknown) {
299  if (linenum >= 0)
300  ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
301  else
302  ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
303  }
304 }
305 
306 /*! \brief Add a new number */
307 static struct number *create_followme_number(const char *number, int timeout, int numorder)
308 {
309  struct number *cur;
310  char *buf = ast_strdupa(number);
311  char *tmp;
312 
313  if (!(cur = ast_calloc(1, sizeof(*cur))))
314  return NULL;
315 
316  cur->timeout = timeout;
317  if ((tmp = strchr(buf, ',')))
318  *tmp = '\0';
319  ast_copy_string(cur->number, buf, sizeof(cur->number));
320  cur->order = numorder;
321  ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
322 
323  return cur;
324 }
325 
326 /*! \brief Reload followme application module */
327 static int reload_followme(int reload)
328 {
329  struct call_followme *f;
330  struct ast_config *cfg;
331  char *cat = NULL, *tmp;
332  struct ast_variable *var;
333  struct number *cur, *nm;
334  char *numberstr;
335  int timeout;
336  int numorder;
337  const char *takecallstr;
338  const char *declinecallstr;
339  const char *tmpstr;
340  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
341 
342  if (!(cfg = ast_config_load("followme.conf", config_flags))) {
343  ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
344  return 0;
345  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
346  return 0;
347  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
348  ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format. Aborting.\n");
349  return 0;
350  }
351 
353 
354  /* Reset Global Var Values */
355  featuredigittimeout = 5000;
356 
357  /* Mark all profiles as inactive for the moment */
358  AST_RWLIST_TRAVERSE(&followmes, f, entry) {
359  f->active = 0;
360  }
361 
362  featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
363 
364  if (!ast_strlen_zero(featuredigittostr)) {
365  if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
366  featuredigittimeout = 5000;
367  }
368 
369  if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
370  ast_copy_string(takecall, takecallstr, sizeof(takecall));
371  }
372 
373  if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
374  ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
375  }
376 
377  if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
378  ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
379  } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
380  ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
381  }
382 
383  if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
384  ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
385  } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
386  ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
387  }
388 
389 
390  if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
391  ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
392  } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
393  ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
394  }
395 
396  if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
397  ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
398  } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
399  ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
400  }
401 
402  if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
403  ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
404  } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
405  ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
406  }
407 
408  if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
409  ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
410  } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
411  ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
412  }
413 
414  /* Chug through config file */
415  while ((cat = ast_category_browse(cfg, cat))) {
416  int new = 0;
417 
418  if (!strcasecmp(cat, "general"))
419  continue;
420 
421  /* Look for an existing one */
422  AST_LIST_TRAVERSE(&followmes, f, entry) {
423  if (!strcasecmp(f->name, cat))
424  break;
425  }
426 
427  ast_debug(1, "New profile %s.\n", cat);
428 
429  if (!f) {
430  /* Make one then */
431  f = alloc_profile(cat);
432  new = 1;
433  }
434 
435  /* Totally fail if we fail to find/create an entry */
436  if (!f)
437  continue;
438 
439  if (!new)
440  ast_mutex_lock(&f->lock);
441  /* Re-initialize the profile */
442  init_profile(f);
443  free_numbers(f);
444  var = ast_variable_browse(cfg, cat);
445  while (var) {
446  if (!strcasecmp(var->name, "number")) {
447  int idx = 0;
448 
449  /* Add a new number */
450  numberstr = ast_strdupa(var->value);
451  if ((tmp = strchr(numberstr, ','))) {
452  *tmp++ = '\0';
453  timeout = atoi(tmp);
454  if (timeout < 0) {
455  timeout = 25;
456  }
457  if ((tmp = strchr(tmp, ','))) {
458  *tmp++ = '\0';
459  numorder = atoi(tmp);
460  if (numorder < 0)
461  numorder = 0;
462  } else
463  numorder = 0;
464  } else {
465  timeout = 25;
466  numorder = 0;
467  }
468 
469  if (!numorder) {
470  idx = 1;
471  AST_LIST_TRAVERSE(&f->numbers, nm, entry)
472  idx++;
473  numorder = idx;
474  }
475  cur = create_followme_number(numberstr, timeout, numorder);
476  if (cur) {
477  AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
478  }
479  } else {
480  profile_set_param(f, var->name, var->value, var->lineno, 1);
481  ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
482  }
483  var = var->next;
484  } /* End while(var) loop */
485 
486  if (!new)
487  ast_mutex_unlock(&f->lock);
488  else
489  AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
490  }
491 
492  ast_config_destroy(cfg);
493 
495 
496  return 1;
497 }
498 
499 static void clear_caller(struct findme_user *tmpuser)
500 {
501  struct ast_channel *outbound;
502 
503  if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
504  outbound = tmpuser->ochan;
505  ast_channel_lock(outbound);
506  if (!outbound->cdr) {
507  outbound->cdr = ast_cdr_alloc();
508  if (outbound->cdr) {
509  ast_cdr_init(outbound->cdr, outbound);
510  }
511  }
512  if (outbound->cdr) {
513  char tmp[256];
514 
515  snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
516  ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
517  ast_cdr_update(outbound);
518  ast_cdr_start(outbound->cdr);
519  ast_cdr_end(outbound->cdr);
520  /* If the cause wasn't handled properly */
521  if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
522  ast_cdr_failed(outbound->cdr);
523  }
524  } else {
525  ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
526  }
527  ast_channel_unlock(outbound);
528  ast_hangup(outbound);
529  tmpuser->ochan = NULL;
530  }
531 }
532 
533 static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
534 {
535  struct findme_user *tmpuser;
536 
537  AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
538  clear_caller(tmpuser);
539  tmpuser->cleared = 1;
540  }
541 }
542 
543 static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
544 {
545  struct findme_user *fmuser;
546 
547  while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
548  if (!fmuser->cleared) {
549  clear_caller(fmuser);
550  }
552  ast_free(fmuser);
553  }
554  ast_free(findme_user_list);
555 }
556 
557 static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs)
558 {
559  struct ast_party_connected_line connected;
560  struct ast_channel *watchers[256];
561  int pos;
562  struct ast_channel *winner;
563  struct ast_frame *f;
564  int ctstatus = 0;
565  int dg;
566  struct findme_user *tmpuser;
567  int to = 0;
568  int livechannels = 0;
569  int tmpto;
570  long totalwait = 0, wtd = 0, towas = 0;
571  char *callfromname;
572  char *pressbuttonname;
573 
574  /* ------------ wait_for_winner_channel start --------------- */
575 
576  callfromname = ast_strdupa(tpargs->callfromprompt);
577  pressbuttonname = ast_strdupa(tpargs->optionsprompt);
578 
579  if (AST_LIST_EMPTY(findme_user_list)) {
580  ast_verb(3, "couldn't reach at this number.\n");
581  return NULL;
582  }
583 
584  if (!caller) {
585  ast_verb(3, "Original caller hungup. Cleanup.\n");
586  clear_calling_tree(findme_user_list);
587  return NULL;
588  }
589 
590  totalwait = nm->timeout * 1000;
591 
592  while (!ctstatus) {
593  to = 1000;
594  pos = 1;
595  livechannels = 0;
596  watchers[0] = caller;
597 
598  dg = 0;
599  winner = NULL;
600  AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
601  if (tmpuser->state >= 0 && tmpuser->ochan) {
602  if (tmpuser->state == 3)
603  tmpuser->digts += (towas - wtd);
604  if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
605  ast_verb(3, "We've been waiting for digits longer than we should have.\n");
606  if (!ast_strlen_zero(namerecloc)) {
607  tmpuser->state = 1;
608  tmpuser->digts = 0;
609  if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
610  ast_sched_runq(tmpuser->ochan->sched);
611  } else {
612  ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
613  return NULL;
614  }
615  } else {
616  tmpuser->state = 2;
617  tmpuser->digts = 0;
618  if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
619  ast_sched_runq(tmpuser->ochan->sched);
620  else {
621  ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
622  return NULL;
623  }
624  }
625  }
626  if (tmpuser->ochan->stream) {
627  ast_sched_runq(tmpuser->ochan->sched);
628  tmpto = ast_sched_wait(tmpuser->ochan->sched);
629  if (tmpto > 0 && tmpto < to)
630  to = tmpto;
631  else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
632  ast_stopstream(tmpuser->ochan);
633  if (tmpuser->state == 1) {
634  ast_verb(3, "Playback of the call-from file appears to be done.\n");
635  if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
636  tmpuser->state = 2;
637  } else {
638  ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
639  memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
640  tmpuser->ynidx = 0;
641  if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
642  tmpuser->state = 3;
643  else {
644  ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
645  return NULL;
646  }
647  }
648  } else if (tmpuser->state == 2) {
649  ast_verb(3, "Playback of name file appears to be done.\n");
650  memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
651  tmpuser->ynidx = 0;
652  if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
653  tmpuser->state = 3;
654  } else {
655  return NULL;
656  }
657  } else if (tmpuser->state == 3) {
658  ast_verb(3, "Playback of the next step file appears to be done.\n");
659  tmpuser->digts = 0;
660  }
661  }
662  }
663  watchers[pos++] = tmpuser->ochan;
664  livechannels++;
665  }
666  }
667 
668  tmpto = to;
669  if (to < 0) {
670  to = 1000;
671  tmpto = 1000;
672  }
673  towas = to;
674  winner = ast_waitfor_n(watchers, pos, &to);
675  tmpto -= to;
676  totalwait -= tmpto;
677  wtd = to;
678  if (totalwait <= 0) {
679  ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
680  clear_calling_tree(findme_user_list);
681  return NULL;
682  }
683  if (winner) {
684  /* Need to find out which channel this is */
685  for (dg = 0; dg < ARRAY_LEN(watchers); ++dg) {
686  if (winner == watchers[dg]) {
687  break;
688  }
689  }
690  if (dg) {
691  /* The winner is an outgoing channel. */
692  AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
693  if (tmpuser->ochan == winner) {
694  break;
695  }
696  }
697  } else {
698  tmpuser = NULL;
699  }
700  f = ast_read(winner);
701  if (f) {
702  if (f->frametype == AST_FRAME_CONTROL) {
703  switch (f->subclass.integer) {
704  case AST_CONTROL_HANGUP:
705  ast_verb(3, "%s received a hangup frame.\n", winner->name);
706  if (f->data.uint32) {
707  winner->hangupcause = f->data.uint32;
708  }
709  if (dg == 0) {
710  ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
711  clear_calling_tree(findme_user_list);
712  ctstatus = -1;
713  }
714  break;
715  case AST_CONTROL_ANSWER:
716  ast_verb(3, "%s answered %s\n", winner->name, caller->name);
717  /* If call has been answered, then the eventual hangup is likely to be normal hangup */
720  ast_verb(3, "Starting playback of %s\n", callfromname);
721  if (dg > 0) {
722  if (!ast_strlen_zero(namerecloc)) {
723  if (!ast_streamfile(winner, callfromname, winner->language)) {
724  ast_sched_runq(winner->sched);
725  tmpuser->state = 1;
726  } else {
727  ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
728  ast_frfree(f);
729  return NULL;
730  }
731  } else {
732  tmpuser->state = 2;
733  if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
734  ast_sched_runq(tmpuser->ochan->sched);
735  else {
736  ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
737  ast_frfree(f);
738  return NULL;
739  }
740  }
741  }
742  break;
743  case AST_CONTROL_BUSY:
744  ast_verb(3, "%s is busy\n", winner->name);
745  break;
747  ast_verb(3, "%s is circuit-busy\n", winner->name);
748  break;
749  case AST_CONTROL_RINGING:
750  ast_verb(3, "%s is ringing\n", winner->name);
751  break;
753  ast_verb(3, "%s is making progress\n", winner->name);
754  break;
756  ast_verb(3, "%s requested a video update\n", winner->name);
757  break;
759  ast_verb(3, "%s requested a source update\n", winner->name);
760  break;
762  ast_verb(3, "%s is proceeding\n", winner->name);
763  break;
764  case AST_CONTROL_HOLD:
765  ast_verb(3, "%s placed call on hold\n", winner->name);
766  break;
767  case AST_CONTROL_UNHOLD:
768  ast_verb(3, "%s removed call from hold\n", winner->name);
769  break;
770  case AST_CONTROL_OFFHOOK:
771  case AST_CONTROL_FLASH:
772  /* Ignore going off hook and flash */
773  break;
775  if (!tmpuser) {
776  /*
777  * Hold connected line update from caller until we have a
778  * winner.
779  */
780  ast_verb(3,
781  "%s connected line has changed. Saving it until we have a winner.\n",
782  winner->name);
783  ast_party_connected_line_set_init(&connected, &tpargs->connected_in);
784  if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
786  &connected, NULL);
787  tpargs->pending_in_connected_update = 1;
788  }
789  ast_party_connected_line_free(&connected);
790  break;
791  }
793  ast_verb(3, "Connected line update from %s prevented.\n",
794  winner->name);
795  } else {
796  ast_verb(3,
797  "%s connected line has changed. Saving it until answer.\n",
798  winner->name);
799  ast_party_connected_line_set_init(&connected, &tmpuser->connected);
800  if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
802  &connected, NULL);
803  tmpuser->pending_connected_update = 1;
804  }
805  ast_party_connected_line_free(&connected);
806  }
807  break;
809  /*
810  * Always ignore because the caller is already answered
811  * and is likely listening to MOH.
812  */
813  break;
814  case -1:
815  ast_verb(3, "%s stopped sounds\n", winner->name);
816  break;
817  default:
818  ast_debug(1, "Dunno what to do with control type %d from %s\n",
819  f->subclass.integer, winner->name);
820  break;
821  }
822  }
823  if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
824  if (winner->stream)
825  ast_stopstream(winner);
826  tmpuser->digts = 0;
827  ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
828  if (tmpuser->ynidx < ARRAY_LEN(tmpuser->yn) - 1) {
829  tmpuser->yn[tmpuser->ynidx++] = (char) f->subclass.integer;
830  }
831  ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
832  if (!strcmp(tmpuser->yn, tpargs->takecall)) {
833  ast_debug(1, "Match to take the call!\n");
834  ast_frfree(f);
835  return tmpuser->ochan;
836  }
837  if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
838  ast_debug(1, "Next in dial plan step requested.\n");
839  ast_frfree(f);
840  return NULL;
841  }
842  }
843 
844  ast_frfree(f);
845  } else {
846  ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n", dg);
847  if (!dg) {
848  /* Caller hung up. */
849  clear_calling_tree(findme_user_list);
850  return NULL;
851  } else {
852  /* Outgoing channel hung up. */
853  tmpuser->state = -1;
854  tmpuser->ochan = NULL;
855  ast_hangup(winner);
856  --livechannels;
857  ast_debug(1, "live channels left %d\n", livechannels);
858  if (!livechannels) {
859  ast_verb(3, "no live channels left. exiting.\n");
860  return NULL;
861  }
862  }
863  }
864  } else {
865  ast_debug(1, "timed out waiting for action\n");
866  }
867  }
868 
869  /* --- WAIT FOR WINNER NUMBER END! -----------*/
870  return NULL;
871 }
872 
873 static void findmeexec(struct fm_args *tpargs)
874 {
875  struct number *nm;
876  struct ast_channel *outbound;
877  struct ast_channel *caller;
878  struct ast_channel *winner = NULL;
879  char dialarg[512];
880  char num[512];
881  int dg, idx;
882  char *rest, *number;
883  struct findme_user *tmpuser;
884  struct findme_user *fmuser;
885  struct findme_user_listptr *findme_user_list;
886 
887  findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
888  if (!findme_user_list) {
889  ast_log(LOG_WARNING, "Failed to allocate memory for findme_user_list\n");
890  return;
891  }
892  AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
893 
894  caller = tpargs->chan;
895  for (idx = 1; !ast_check_hangup(caller); ++idx) {
896  /* Find next followme numbers to dial. */
897  AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
898  if (nm->order == idx) {
899  break;
900  }
901  }
902  if (!nm) {
903  break;
904  }
905 
906  ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
907 
908  ast_copy_string(num, nm->number, sizeof(num));
909  for (number = num; number; number = rest) {
910  rest = strchr(number, '&');
911  if (rest) {
912  *rest++ = 0;
913  }
914 
915  /* We check if the extension exists, before creating the ast_channel struct */
916  if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) {
917  ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
918  continue;
919  }
920 
921  if (!strcmp(tpargs->context, "")) {
922  snprintf(dialarg, sizeof(dialarg), "%s", number);
923  } else {
924  snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
925  }
926 
927  tmpuser = ast_calloc(1, sizeof(*tmpuser));
928  if (!tmpuser) {
929  continue;
930  }
931 
932  outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg);
933  if (outbound) {
934  ast_channel_lock_both(caller, outbound);
936  ast_channel_inherit_variables(caller, outbound);
937  ast_channel_datastore_inherit(caller, outbound);
938  ast_string_field_set(outbound, language, caller->language);
939  ast_string_field_set(outbound, accountcode, caller->accountcode);
940  ast_string_field_set(outbound, musicclass, caller->musicclass);
941  ast_channel_unlock(outbound);
942  ast_channel_unlock(caller);
943  ast_verb(3, "calling Local/%s\n", dialarg);
944  if (!ast_call(outbound, dialarg, 0)) {
945  tmpuser->ochan = outbound;
946  tmpuser->state = 0;
947  tmpuser->cleared = 0;
948  ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
949  AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
950  } else {
951  ast_verb(3, "couldn't reach at this number.\n");
952  ast_channel_lock(outbound);
953  if (!outbound->cdr) {
954  outbound->cdr = ast_cdr_alloc();
955  }
956  if (outbound->cdr) {
957  char tmp[256];
958 
959  ast_cdr_init(outbound->cdr, outbound);
960  snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
961  ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
962  ast_cdr_update(outbound);
963  ast_cdr_start(outbound->cdr);
964  ast_cdr_end(outbound->cdr);
965  /* If the cause wasn't handled properly */
966  if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
967  ast_cdr_failed(outbound->cdr);
968  }
969  } else {
970  ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
971  }
972  ast_channel_unlock(outbound);
973  ast_hangup(outbound);
974  ast_free(tmpuser);
975  }
976  } else {
977  ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
978  ast_free(tmpuser);
979  }
980  }
981 
982  if (AST_LIST_EMPTY(findme_user_list)) {
983  continue;
984  }
985 
986  winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, tpargs);
987  if (!winner) {
988  continue;
989  }
990 
991  /* Destroy losing calls up to the winner. The rest will be destroyed later. */
992  while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
993  if (fmuser->ochan == winner) {
994  /* Pass any connected line info up. */
995  tpargs->connected_out = fmuser->connected;
997  ast_free(fmuser);
998  break;
999  } else {
1000  /* Destroy losing call. */
1001  if (!fmuser->cleared) {
1002  clear_caller(fmuser);
1003  }
1005  ast_free(fmuser);
1006  }
1007  }
1008  break;
1009  }
1010  destroy_calling_tree(findme_user_list);
1011  if (!winner) {
1012  tpargs->status = 1;
1013  } else {
1014  tpargs->status = 100;
1015  tpargs->outbound = winner;
1016  }
1017 }
1018 
1019 static struct call_followme *find_realtime(const char *name)
1020 {
1021  struct ast_variable *var;
1022  struct ast_variable *v;
1023  struct ast_config *cfg;
1024  const char *catg;
1025  struct call_followme *new_follower;
1026  struct ast_str *str;
1027 
1028  str = ast_str_create(16);
1029  if (!str) {
1030  return NULL;
1031  }
1032 
1033  var = ast_load_realtime("followme", "name", name, SENTINEL);
1034  if (!var) {
1035  ast_free(str);
1036  return NULL;
1037  }
1038 
1039  if (!(new_follower = alloc_profile(name))) {
1040  ast_variables_destroy(var);
1041  ast_free(str);
1042  return NULL;
1043  }
1044 
1045  for (v = var; v; v = v->next) {
1046  if (!strcasecmp(v->name, "active")) {
1047  if (ast_false(v->value)) {
1048  ast_mutex_destroy(&new_follower->lock);
1049  ast_free(new_follower);
1050  ast_variables_destroy(var);
1051  ast_free(str);
1052  return NULL;
1053  }
1054  } else {
1055  profile_set_param(new_follower, v->name, v->value, 0, 0);
1056  }
1057  }
1058 
1059  ast_variables_destroy(var);
1060  new_follower->realtime = 1;
1061 
1062  /* Load numbers */
1063  cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
1064  name, SENTINEL);
1065  if (!cfg) {
1066  ast_mutex_destroy(&new_follower->lock);
1067  ast_free(new_follower);
1068  ast_free(str);
1069  return NULL;
1070  }
1071 
1072  for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
1073  const char *numstr;
1074  const char *timeoutstr;
1075  const char *ordstr;
1076  int timeout;
1077  struct number *cur;
1078 
1079  if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
1080  continue;
1081  }
1082  if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
1083  || sscanf(timeoutstr, "%30d", &timeout) != 1
1084  || timeout < 1) {
1085  timeout = 25;
1086  }
1087  /* This one has to exist; it was part of the query */
1088  ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
1089  ast_str_set(&str, 0, "%s", numstr);
1090  if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
1091  AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
1092  }
1093  }
1094  ast_config_destroy(cfg);
1095 
1096  ast_free(str);
1097  return new_follower;
1098 }
1099 
1100 static void end_bridge_callback(void *data)
1101 {
1102  char buf[80];
1103  time_t end;
1104  struct ast_channel *chan = data;
1105 
1106  time(&end);
1107 
1108  ast_channel_lock(chan);
1109  if (chan->cdr->answer.tv_sec) {
1110  snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
1111  pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
1112  }
1113 
1114  if (chan->cdr->start.tv_sec) {
1115  snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
1116  pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
1117  }
1118  ast_channel_unlock(chan);
1119 }
1120 
1121 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
1122 {
1123  bconfig->end_bridge_callback_data = originator;
1124 }
1125 
1126 static int app_exec(struct ast_channel *chan, const char *data)
1127 {
1128  struct fm_args targs = { 0, };
1129  struct ast_bridge_config config;
1130  struct call_followme *f;
1131  struct number *nm, *newnm;
1132  int res = 0;
1133  char *argstr;
1134  struct ast_channel *caller;
1135  struct ast_channel *outbound;
1137  AST_APP_ARG(followmeid);
1138  AST_APP_ARG(options);
1139  );
1140 
1141  if (ast_strlen_zero(data)) {
1142  ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1143  return -1;
1144  }
1145 
1146  argstr = ast_strdupa((char *) data);
1147 
1148  AST_STANDARD_APP_ARGS(args, argstr);
1149 
1150  if (ast_strlen_zero(args.followmeid)) {
1151  ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1152  return -1;
1153  }
1154 
1156  AST_RWLIST_TRAVERSE(&followmes, f, entry) {
1157  if (!strcasecmp(f->name, args.followmeid) && (f->active))
1158  break;
1159  }
1161 
1162  ast_debug(1, "New profile %s.\n", args.followmeid);
1163 
1164  if (!f) {
1165  f = find_realtime(args.followmeid);
1166  }
1167 
1168  if (!f) {
1169  ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
1170  return 0;
1171  }
1172 
1173  /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
1174  if (args.options)
1175  ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
1176 
1177  /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
1178  ast_mutex_lock(&f->lock);
1179  targs.mohclass = ast_strdupa(f->moh);
1180  ast_copy_string(targs.context, f->context, sizeof(targs.context));
1181  ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
1182  ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
1183  ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
1185  ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
1186  ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
1187  ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
1188  ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
1189  /* Copy the numbers we're going to use into another list in case the master list should get modified
1190  (and locked) while we're trying to do a follow-me */
1192  AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
1193  newnm = create_followme_number(nm->number, nm->timeout, nm->order);
1194  if (newnm) {
1195  AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
1196  }
1197  }
1198  ast_mutex_unlock(&f->lock);
1199 
1200  /* Answer the call */
1201  if (chan->_state != AST_STATE_UP) {
1202  ast_answer(chan);
1203  }
1204 
1206  ast_stream_and_wait(chan, targs.statusprompt, "");
1207 
1209  int duration = 5;
1210 
1211  snprintf(targs.namerecloc, sizeof(targs.namerecloc), "%s/followme.%s",
1213 
1214  if (ast_play_and_record(chan, "vm-rec-name", targs.namerecloc, 5, "sln", &duration,
1216  goto outrun;
1217  }
1218 
1219  if (!ast_fileexists(targs.namerecloc, NULL, chan->language)) {
1220  targs.namerecloc[0] = '\0';
1221  }
1222  }
1223 
1225  if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
1226  goto outrun;
1227  if (ast_waitstream(chan, "") < 0)
1228  goto outrun;
1229  }
1230  ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
1231 
1232  targs.status = 0;
1233  targs.chan = chan;
1234  ast_channel_lock(chan);
1236  ast_channel_unlock(chan);
1237 
1238  findmeexec(&targs);
1239  if (targs.status != 100) {
1240  ast_moh_stop(chan);
1242  ast_stream_and_wait(chan, targs.sorryprompt, "");
1243  res = 0;
1244  } else {
1245  caller = chan;
1246  outbound = targs.outbound;
1247  /* Bridge the two channels. */
1248 
1249  memset(&config, 0, sizeof(config));
1254  config.end_bridge_callback_data = chan;
1256 
1257  ast_moh_stop(caller);
1258  /* Be sure no generators are left on it */
1259  ast_deactivate_generator(caller);
1260  /* Make sure channels are compatible */
1261  res = ast_channel_make_compatible(caller, outbound);
1262  if (res < 0) {
1263  ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
1264  ast_hangup(outbound);
1265  goto outrun;
1266  }
1267 
1268  /* Update connected line to caller if available. */
1269  if (targs.pending_out_connected_update) {
1270  if (ast_channel_connected_line_macro(outbound, caller, &targs.connected_out, 1, 0)) {
1271  ast_channel_update_connected_line(caller, &targs.connected_out, NULL);
1272  }
1273  }
1274 
1275  /* Update connected line to winner if changed. */
1276  if (targs.pending_in_connected_update) {
1277  if (ast_channel_connected_line_macro(caller, outbound, &targs.connected_in, 0, 0)) {
1278  ast_channel_update_connected_line(outbound, &targs.connected_in, NULL);
1279  }
1280  }
1281 
1282  res = ast_bridge_call(caller, outbound, &config);
1283  ast_hangup(outbound);
1284  }
1285 
1286 outrun:
1287  while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry))) {
1288  ast_free(nm);
1289  }
1290  if (!ast_strlen_zero(targs.namerecloc)) {
1291  unlink(targs.namerecloc);
1292  }
1295 
1296  if (f->realtime) {
1297  /* Not in list */
1298  free_numbers(f);
1299  ast_free(f);
1300  }
1301 
1302  return res;
1303 }
1304 
1305 static int unload_module(void)
1306 {
1307  struct call_followme *f;
1308 
1310 
1311  /* Free Memory. Yeah! I'm free! */
1313  while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
1314  free_numbers(f);
1315  ast_free(f);
1316  }
1317 
1319 
1320  return 0;
1321 }
1322 
1323 static int load_module(void)
1324 {
1325  if(!reload_followme(0))
1326  return AST_MODULE_LOAD_DECLINE;
1327 
1329 }
1330 
1331 static int reload(void)
1332 {
1333  reload_followme(1);
1334 
1335  return 0;
1336 }
1337 
1338 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
1339  .load = load_module,
1340  .unload = unload_module,
1341  .reload = reload,
1342  );
static void free_numbers(struct call_followme *f)
Definition: app_followme.c:218
int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
Save the result of the call based on the AST_CAUSE_*.
Definition: cdr.c:790
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path)
Record a file based on input from a channel. Use default accept and cancel DTMF. This function will p...
Definition: app.c:1183
static char musicclass[MAX_MUSICCLASS]
Definition: chan_mgcp.c:155
struct ast_channel * ast_waitfor_n(struct ast_channel **chan, int n, int *ms)
Waits for input on a group of channels Wait for input on an array of channels for a given # of millis...
Definition: channel.c:3534
union ast_frame_subclass subclass
Definition: frame.h:146
#define MAX_YN_STRING
Definition: app_followme.c:106
int ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2804
static char accountcode[AST_MAX_ACCOUNT_CODE]
Definition: chan_iax2.c:383
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
Music on hold handling.
void ast_cdr_failed(struct ast_cdr *cdr)
Fail a call.
Definition: cdr.c:764
char norecordingprompt[PATH_MAX]
Definition: app_followme.c:160
char * str
Subscriber phone number (Malloced)
Definition: channel.h:241
struct ast_party_connected_line connected
Channel Connected Line ID information.
Definition: channel.h:811
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:946
Asterisk locking-related definitions:
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
void(* end_bridge_callback_data_fixup)(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
Definition: channel.h:993
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Definition: app.h:712
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
struct ast_party_caller caller
Channel Caller ID information.
Definition: channel.h:804
unsigned int pending_out_connected_update
Definition: app_followme.c:153
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:332
static void init_profile(struct call_followme *f)
Definition: app_followme.c:266
const ast_string_field uniqueid
Definition: channel.h:787
struct ast_flags features_callee
Definition: channel.h:976
Definition: ast_expr2.c:325
void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide)
Initialize the given connected line structure using the given guide for a set update operation...
Definition: channel.c:2329
#define ast_test_flag(p, flag)
Definition: utils.h:63
Support for translation of data formats. translate.c.
struct ast_channel * outbound
Definition: app_followme.c:145
void ast_cdr_end(struct ast_cdr *cdr)
End a call.
Definition: cdr.c:933
#define MAX_MUSICCLASS
Definition: channel.h:139
char norecordingprompt[PATH_MAX]
Definition: app_followme.c:127
void * ptr
Definition: frame.h:160
Convenient Signal Processing routines.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:144
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected, const struct ast_set_party_connected_line *update)
Indicate that the connected line information has changed.
Definition: channel.c:9085
static struct number * create_followme_number(const char *number, int timeout, int numorder)
Add a new number.
Definition: app_followme.c:307
int lineno
Definition: config.h:87
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: app.c:2101
struct fm_args::cnumbers cnumbers
#define AST_FRAME_DTMF
Definition: frame.h:128
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
static char * app
Definition: app_followme.c:103
#define var
Definition: ast_expr2f.c:606
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4383
char callfromprompt[PATH_MAX]
Definition: app_followme.c:159
struct ast_party_connected_line connected_out
Definition: app_followme.c:149
Configuration File Parser.
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: config.c:2548
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
Bridge a call, optionally allowing redirection.
Definition: features.c:3960
ast_mutex_t lock
Definition: app_followme.c:118
format_t ast_best_codec(format_t fmts)
Pick the best audio codec.
Definition: channel.c:1062
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define ast_mutex_lock(a)
Definition: lock.h:155
struct ast_cdr * cdr
Definition: channel.h:766
long timeout
Definition: app_followme.c:111
struct call_followme::numbers numbers
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
format_t nativeformats
Definition: channel.h:852
char context[AST_MAX_CONTEXT]
Definition: app_followme.c:121
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2353
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: config.c:586
int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const void *connected_info, int caller, int frame)
Run a connected line interception macro and update a channel&#39;s connected line information.
Definition: channel.c:9618
const char * str
Definition: app_jack.c:144
char namerecloc[PATH_MAX]
Definition: app_followme.c:156
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 * data
Definition: channel.h:755
char plsholdprompt[PATH_MAX]
Definition: app_followme.c:129
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:8051
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
Set parameter in profile from configuration file.
Definition: app_followme.c:275
char nextindp[MAX_YN_STRING]
Definition: app_followme.c:125
Utility functions.
Number structure.
Definition: app_followme.c:109
char callfromprompt[PATH_MAX]
Definition: app_followme.c:126
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:874
struct ast_party_id id
Caller party ID.
Definition: channel.h:370
static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
Definition: app_followme.c:543
static void clear_caller(struct findme_user *tmpuser)
Definition: app_followme.c:499
int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
Inherit datastores from a parent to a child.
Definition: channel.c:2573
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
static int reload_followme(int reload)
Reload followme application module.
Definition: app_followme.c:327
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:703
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static char callfromprompt[PATH_MAX]
Definition: app_followme.c:207
#define SENTINEL
Definition: compiler.h:75
const char * value
Definition: config.h:79
struct call_followme::wlnumbers wlnumbers
Data structure for followme scripts.
Definition: app_followme.c:117
static int reload(void)
static int featuredigittimeout
Definition: app_followme.c:202
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
static char optionsprompt[PATH_MAX]
Definition: app_followme.c:209
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
unsigned int pending_in_connected_update
Definition: app_followme.c:151
#define AST_MAX_EXTENSION
Definition: channel.h:135
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
struct ast_party_connected_line connected
Definition: app_followme.c:171
int datalen
Definition: frame.h:148
#define AST_CAUSE_NORMAL_CLEARING
Definition: causes.h:105
bridge configuration
Definition: channel.h:974
void * end_bridge_callback_data
Definition: channel.h:989
char sorryprompt[PATH_MAX]
Definition: app_followme.c:164
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
static struct ast_app_option followme_opts[128]
Definition: app_followme.c:199
static void end_bridge_callback(void *data)
struct sched_context * sched
Definition: channel.h:756
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:108
const char * name
Definition: config.h:77
char context[AST_MAX_CONTEXT]
Definition: app_followme.c:155
int(* timingfunc)(const void *data)
Definition: channel.h:759
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:5400
static char statusprompt[PATH_MAX]
Definition: app_followme.c:211
Core PBX routines and definitions.
int ast_cdr_update(struct ast_channel *chan)
Update CDR on a channel.
Definition: cdr.c:1083
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:806
static char nextindp[MAX_YN_STRING]
Definition: app_followme.c:206
struct ast_flags features_caller
Definition: channel.h:975
static int load_module(void)
char yn[MAX_YN_STRING]
Definition: app_followme.c:177
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:224
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
struct timeval answer
Definition: cdr.h:102
int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected)
Parse connected line indication frame data.
Definition: channel.c:8886
void ast_cdr_start(struct ast_cdr *cdr)
Start a call.
Definition: cdr.c:727
#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
char nextindp[MAX_YN_STRING]
Definition: app_followme.c:158
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
char optionsprompt[PATH_MAX]
Definition: app_followme.c:161
static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
Definition: app_followme.c:533
static struct @350 args
void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src, const struct ast_set_party_connected_line *update)
Set the connected line information based on another connected line source.
Definition: channel.c:2337
enum ast_channel_state _state
Definition: channel.h:839
Connected Line/Party information.
Definition: channel.h:401
const ast_string_field name
Definition: channel.h:787
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:8040
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
struct call_followme::blnumbers blnumbers
#define LOG_NOTICE
Definition: logger.h:133
static char norecordingprompt[PATH_MAX]
Definition: app_followme.c:208
#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
char name[AST_MAX_EXTENSION]
Definition: app_followme.c:119
char * mohclass
Definition: app_followme.c:142
#define ast_channel_unlock(chan)
Definition: channel.h:2467
struct timeval start
Definition: cdr.h:100
struct ast_flags followmeflags
Definition: app_followme.c:165
const char * ast_cause2str(int state) attribute_pure
Gives the string form of a given cause code.
Definition: channel.c:980
#define AST_MAX_CONTEXT
Definition: channel.h:136
static const char name[]
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
Inherits channel variable from parent to child channel.
Definition: channel.c:6241
#define ast_free(a)
Definition: astmm.h:97
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1370
int order
Definition: app_followme.c:112
static struct ast_format f[]
Definition: format_g726.c:181
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:829
struct number::@17 entry
char statusprompt[PATH_MAX]
Definition: app_followme.c:130
void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data)
Set the last executed application.
Definition: cdr.c:822
int ast_call(struct ast_channel *chan, char *addr, int timeout)
Make a call.
Definition: channel.c:5761
const char * ast_config_AST_SPOOL_DIR
Definition: asterisk.c:259
unsigned int cleared
Definition: app_followme.c:179
Structure used to handle boolean flags.
Definition: utils.h:200
static struct call_followme * alloc_profile(const char *fmname)
Allocate and initialize followme profile.
Definition: app_followme.c:241
static struct ast_channel * wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs)
Definition: app_followme.c:557
int ast_sched_runq(struct sched_context *con)
Runs the queue.
Definition: sched.c:600
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
static void findmeexec(struct fm_args *tpargs)
Definition: app_followme.c:873
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2473
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:3107
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:666
static const char * featuredigittostr
Definition: app_followme.c:201
static int app_exec(struct ast_channel *chan, const char *data)
int ast_channel_make_compatible(struct ast_channel *c0, struct ast_channel *c1)
Makes two channel formats compatible.
Definition: channel.c:5970
unsigned int active
Definition: app_followme.c:122
Standard Command Line Interface.
struct ast_channel * ochan
Definition: app_followme.c:169
#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
void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_party_caller *src)
Copy the caller information to the connected line information.
Definition: channel.c:8443
#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
char plsholdprompt[PATH_MAX]
Definition: app_followme.c:162
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is &quot;false&quot;...
Definition: utils.c:1550
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
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:919
int ast_sched_wait(struct sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place Determine the number of s...
Definition: sched.c:334
int status
Definition: app_followme.c:154
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...
const ast_string_field musicclass
Definition: channel.h:787
char number[512]
Definition: app_followme.c:110
const ast_string_field accountcode
Definition: channel.h:787
char moh[MAX_MUSICCLASS]
Definition: app_followme.c:120
Data structure associated with a single frame of data.
Definition: frame.h:142
int hangupcause
Definition: channel.h:849
char takecall[MAX_YN_STRING]
Definition: app_followme.c:124
Internal Asterisk hangup causes.
static int connected
Definition: cdr_pgsql.c:57
uint32_t uint32
Definition: frame.h:160
static int unload_module(void)
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
enum ast_frame_type frametype
Definition: frame.h:144
struct ast_variable * next
Definition: config.h:82
struct ast_channel * chan
Definition: app_followme.c:141
#define ast_mutex_init(pmutex)
Definition: lock.h:152
#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
static const char * defaultmoh
Definition: app_followme.c:203
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:107
Call Parking and Pickup API Includes code and algorithms from the Zapata library. ...
#define ast_mutex_destroy(a)
Definition: lock.h:154
char sorryprompt[PATH_MAX]
Definition: app_followme.c:131
char takecall[MAX_YN_STRING]
Definition: app_followme.c:157
void(* end_bridge_callback)(void *)
Definition: channel.h:988
struct ast_filestream * stream
Definition: channel.h:757
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
static char plsholdprompt[PATH_MAX]
Definition: app_followme.c:210
char optionsprompt[PATH_MAX]
Definition: app_followme.c:128
static struct call_followme * find_realtime(const char *name)
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:1880
Asterisk module definitions.
struct ast_channel * ast_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *status)
Requests a channel.
Definition: channel.c:5695
union ast_frame::@172 data
static char takecall[MAX_YN_STRING]
Definition: app_followme.c:205
static char sorryprompt[PATH_MAX]
Definition: app_followme.c:212
Persistant data storage (akin to *doze registry)
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:247
struct ast_party_connected_line connected_in
Definition: app_followme.c:147
char dialarg[256]
Definition: app_followme.c:175
int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *chan)
Initialize based on a channel.
Definition: cdr.c:897
const ast_string_field language
Definition: channel.h:787
struct ast_cdr * ast_cdr_alloc(void)
Allocate a CDR record.
Definition: cdr.c:499
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:128
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
Structure for mutex and tracking information.
Definition: lock.h:121
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
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: config.c:2650
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
Definition: app.h:721
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
#define ast_mutex_unlock(a)
Definition: lock.h:156
unsigned int pending_connected_update
Definition: app_followme.c:181
char statusprompt[PATH_MAX]
Definition: app_followme.c:163
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:292