Asterisk data retrieval API.
[asterisk/asterisk.git] / apps / app_followme.c
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  ***/
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include <signal.h>
40
41 #include "asterisk/paths.h"     /* use ast_config_AST_SPOOL_DIR */
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/translate.h"
48 #include "asterisk/say.h"
49 #include "asterisk/features.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/cli.h"
52 #include "asterisk/manager.h"
53 #include "asterisk/config.h"
54 #include "asterisk/monitor.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="s">
71                                                 <para>Playback the incoming status message prior to starting
72                                                 the follow-me step(s)</para>
73                                         </option>
74                                         <option name="a">
75                                                 <para>Record the caller's name so it can be announced to the
76                                                 callee on each step.</para>
77                                         </option>
78                                         <option name="n">
79                                                 <para>Playback the unreachable status message if we've run out
80                                                 of steps to reach the or the callee has elected not to be reachable.</para>
81                                         </option>
82                                         <option name="d">
83                                                 <para>Disable the 'Please hold while we try to connect your call' announcement.</para>
84                                         </option>
85                                 </optionlist>
86                         </parameter>
87                 </syntax>
88                 <description>
89                         <para>This application performs Find-Me/Follow-Me functionality for the caller
90                         as defined in the profile matching the <replaceable>followmeid</replaceable> parameter in
91                         <filename>followme.conf</filename>. If the specified <replaceable>followmeid</replaceable>
92                         profile doesn't exist in <filename>followme.conf</filename>, execution will be returned
93                         to the dialplan and call execution will continue at the next priority.</para>
94                         <para>Returns -1 on hangup.</para>
95                 </description>
96         </application>
97  ***/
98
99 static char *app = "FollowMe";
100
101 /*! \brief Number structure */
102 struct number {
103         char number[512];       /*!< Phone Number(s) and/or Extension(s) */
104         long timeout;           /*!< Dial Timeout, if used. */
105         int order;              /*!< The order to dial in */
106         AST_LIST_ENTRY(number) entry; /*!< Next Number record */
107 };
108
109 /*! \brief Data structure for followme scripts */
110 struct call_followme {
111         ast_mutex_t lock;
112         char name[AST_MAX_EXTENSION];   /*!< Name - FollowMeID */
113         char moh[AST_MAX_CONTEXT];      /*!< Music On Hold Class to be used */
114         char context[AST_MAX_CONTEXT];  /*!< Context to dial from */
115         unsigned int active;            /*!< Profile is active (1), or disabled (0). */
116         int realtime;           /*!< Cached from realtime */
117         char takecall[20];              /*!< Digit mapping to take a call */
118         char nextindp[20];              /*!< Digit mapping to decline a call */
119         char callfromprompt[PATH_MAX];  /*!< Sound prompt name and path */
120         char norecordingprompt[PATH_MAX];       /*!< Sound prompt name and path */
121         char optionsprompt[PATH_MAX];   /*!< Sound prompt name and path */
122         char plsholdprompt[PATH_MAX];   /*!< Sound prompt name and path */
123         char statusprompt[PATH_MAX];    /*!< Sound prompt name and path */
124         char sorryprompt[PATH_MAX];     /*!< Sound prompt name and path */
125
126         AST_LIST_HEAD_NOLOCK(numbers, number) numbers;     /*!< Head of the list of follow-me numbers */
127         AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
128         AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
129         AST_LIST_ENTRY(call_followme) entry;           /*!< Next Follow-Me record */
130 };
131
132 struct fm_args {
133         struct ast_channel *chan;
134         char *mohclass;
135         AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
136         int status;
137         char context[AST_MAX_CONTEXT];
138         char namerecloc[AST_MAX_CONTEXT];
139         struct ast_channel *outbound;
140         char takecall[20];              /*!< Digit mapping to take a call */
141         char nextindp[20];              /*!< Digit mapping to decline a call */
142         char callfromprompt[PATH_MAX];  /*!< Sound prompt name and path */
143         char norecordingprompt[PATH_MAX];       /*!< Sound prompt name and path */
144         char optionsprompt[PATH_MAX];   /*!< Sound prompt name and path */
145         char plsholdprompt[PATH_MAX];   /*!< Sound prompt name and path */
146         char statusprompt[PATH_MAX];    /*!< Sound prompt name and path */
147         char sorryprompt[PATH_MAX];     /*!< Sound prompt name and path */
148         struct ast_flags followmeflags;
149 };
150
151 struct findme_user {
152         struct ast_channel *ochan;
153         int state;
154         char dialarg[256];
155         char yn[10];
156         int ynidx; 
157         long digts;
158         int cleared;
159         AST_LIST_ENTRY(findme_user) entry;
160 };
161
162 enum {
163         FOLLOWMEFLAG_STATUSMSG = (1 << 0),
164         FOLLOWMEFLAG_RECORDNAME = (1 << 1),
165         FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
166         FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3)
167 };
168
169 AST_APP_OPTIONS(followme_opts, {
170         AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ),
171         AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ),
172         AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ),
173         AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT ),
174 });
175
176 static int ynlongest = 0;
177
178 static const char *featuredigittostr;
179 static int featuredigittimeout = 5000;          /*!< Feature Digit Timeout */
180 static const char *defaultmoh = "default";      /*!< Default Music-On-Hold Class */
181
182 static char takecall[20] = "1", nextindp[20] = "2";
183 static char callfromprompt[PATH_MAX] = "followme/call-from";
184 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
185 static char optionsprompt[PATH_MAX] = "followme/options";
186 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
187 static char statusprompt[PATH_MAX] = "followme/status";
188 static char sorryprompt[PATH_MAX] = "followme/sorry";
189
190
191 static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
192 AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
193
194 static void free_numbers(struct call_followme *f)
195 {
196         /* Free numbers attached to the profile */
197         struct number *prev;
198
199         while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
200                 /* Free the number */
201                 ast_free(prev);
202         AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
203
204         while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
205                 /* Free the blacklisted number */
206                 ast_free(prev);
207         AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
208
209         while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
210                 /* Free the whitelisted number */
211                 ast_free(prev);
212         AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
213 }
214
215
216 /*! \brief Allocate and initialize followme profile */
217 static struct call_followme *alloc_profile(const char *fmname)
218 {
219         struct call_followme *f;
220
221         if (!(f = ast_calloc(1, sizeof(*f))))
222                 return NULL;
223
224         ast_mutex_init(&f->lock);
225         ast_copy_string(f->name, fmname, sizeof(f->name));
226         f->moh[0] = '\0';
227         f->context[0] = '\0';
228         ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
229         ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
230         ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
231         ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
232         ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
233         ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
234         ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
235         ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
236         AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
237         AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
238         AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
239         return f;
240 }
241
242 static void init_profile(struct call_followme *f)
243 {
244         f->active = 1;
245         ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
246 }
247
248    
249    
250 /*! \brief Set parameter in profile from configuration file */
251 static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
252 {
253
254         if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
255                 ast_copy_string(f->moh, val, sizeof(f->moh));
256         else if (!strcasecmp(param, "context")) 
257                 ast_copy_string(f->context, val, sizeof(f->context));
258         else if (!strcasecmp(param, "takecall"))
259                 ast_copy_string(f->takecall, val, sizeof(f->takecall));
260         else if (!strcasecmp(param, "declinecall"))
261                 ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
262         else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
263                 ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
264         else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
265                 ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
266         else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
267                 ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
268         else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
269                 ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
270         else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
271                 ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
272         else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
273                 ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
274         else if (failunknown) {
275                 if (linenum >= 0)
276                         ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
277                 else
278                         ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
279         }
280 }
281
282 /*! \brief Add a new number */
283 static struct number *create_followme_number(const char *number, int timeout, int numorder)
284 {
285         struct number *cur;
286         char *buf = ast_strdupa(number);
287         char *tmp;
288
289         if (!(cur = ast_calloc(1, sizeof(*cur))))
290                 return NULL;
291
292         cur->timeout = timeout;
293         if ((tmp = strchr(buf, ',')))
294                 *tmp = '\0';
295         ast_copy_string(cur->number, buf, sizeof(cur->number));
296         cur->order = numorder;
297         ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
298
299         return cur;
300 }
301
302 /*! \brief Reload followme application module */
303 static int reload_followme(int reload)
304 {
305         struct call_followme *f;
306         struct ast_config *cfg;
307         char *cat = NULL, *tmp;
308         struct ast_variable *var;
309         struct number *cur, *nm;
310         char numberstr[90];
311         int timeout;
312         char *timeoutstr;
313         int numorder;
314         const char *takecallstr;
315         const char *declinecallstr;
316         const char *tmpstr;
317         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
318
319         if (!(cfg = ast_config_load("followme.conf", config_flags))) {
320                 ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
321                 return 0;
322         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
323                 return 0;
324         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
325                 ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format.  Aborting.\n");
326                 return 0;
327         }
328
329         AST_RWLIST_WRLOCK(&followmes);
330
331         /* Reset Global Var Values */
332         featuredigittimeout = 5000;
333
334         /* Mark all profiles as inactive for the moment */
335         AST_RWLIST_TRAVERSE(&followmes, f, entry) {
336                 f->active = 0;
337         }
338
339         featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
340
341         if (!ast_strlen_zero(featuredigittostr)) {
342                 if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
343                         featuredigittimeout = 5000;
344         }
345
346         if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
347                 ast_copy_string(takecall, takecallstr, sizeof(takecall));
348         }
349
350         if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
351                 ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
352         }
353
354         if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
355                 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
356         } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
357                 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
358         }
359
360         if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
361                 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
362         } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
363                 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
364         }
365
366
367         if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
368                 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
369         } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
370                 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
371         }
372
373         if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
374                 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
375         } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
376                 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
377         }
378
379         if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
380                 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
381         } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
382                 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
383         }
384
385         if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
386                 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
387         } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
388                 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
389         }
390
391         /* Chug through config file */
392         while ((cat = ast_category_browse(cfg, cat))) {
393                 int new = 0;
394
395                 if (!strcasecmp(cat, "general"))
396                         continue;
397
398                 /* Look for an existing one */
399                 AST_LIST_TRAVERSE(&followmes, f, entry) {
400                         if (!strcasecmp(f->name, cat))
401                                 break;
402                 }
403
404                 ast_debug(1, "New profile %s.\n", cat);
405
406                 if (!f) {
407                         /* Make one then */
408                         f = alloc_profile(cat);
409                         new = 1;
410                 }
411
412                 /* Totally fail if we fail to find/create an entry */
413                 if (!f)
414                         continue;
415
416                 if (!new)
417                         ast_mutex_lock(&f->lock);
418                 /* Re-initialize the profile */
419                 init_profile(f);
420                 free_numbers(f);
421                 var = ast_variable_browse(cfg, cat);
422                 while (var) {
423                         if (!strcasecmp(var->name, "number")) {
424                                 int idx = 0;
425
426                                 /* Add a new number */
427                                 ast_copy_string(numberstr, var->value, sizeof(numberstr));
428                                 if ((tmp = strchr(numberstr, ','))) {
429                                         *tmp++ = '\0';
430                                         timeoutstr = ast_strdupa(tmp);
431                                         if ((tmp = strchr(timeoutstr, ','))) {
432                                                 *tmp++ = '\0';
433                                                 numorder = atoi(tmp);
434                                                 if (numorder < 0)
435                                                         numorder = 0;
436                                         } else 
437                                                 numorder = 0;
438                                         timeout = atoi(timeoutstr);
439                                         if (timeout < 0) 
440                                                 timeout = 25;
441                                 } else {
442                                         timeout = 25;
443                                         numorder = 0;
444                                 }
445
446                                 if (!numorder) {
447                                         idx = 1;
448                                         AST_LIST_TRAVERSE(&f->numbers, nm, entry) 
449                                                 idx++;
450                                         numorder = idx;
451                                 }
452                                 cur = create_followme_number(numberstr, timeout, numorder);
453                                 AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
454                         } else {
455                                 profile_set_param(f, var->name, var->value, var->lineno, 1);
456                                 ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
457                         }
458                         var = var->next;
459                 } /* End while(var) loop */
460
461                 if (!new) 
462                         ast_mutex_unlock(&f->lock);
463                 else
464                         AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
465         }
466
467         ast_config_destroy(cfg);
468
469         AST_RWLIST_UNLOCK(&followmes);
470
471         return 1;
472 }
473
474 static void clear_caller(struct findme_user *tmpuser)
475 {
476         struct ast_channel *outbound;
477
478         if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
479                 outbound = tmpuser->ochan;
480                 if (!outbound->cdr) {
481                         outbound->cdr = ast_cdr_alloc();
482                         if (outbound->cdr)
483                                 ast_cdr_init(outbound->cdr, outbound);
484                 }
485                 if (outbound->cdr) {
486                         char tmp[256];
487
488                         snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
489                         ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
490                         ast_cdr_update(outbound);
491                         ast_cdr_start(outbound->cdr);
492                         ast_cdr_end(outbound->cdr);
493                         /* If the cause wasn't handled properly */
494                         if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
495                                 ast_cdr_failed(outbound->cdr);
496                 } else
497                         ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
498                 ast_hangup(tmpuser->ochan);
499         }
500
501 }
502
503 static void clear_calling_tree(struct findme_user_listptr *findme_user_list) 
504 {
505         struct findme_user *tmpuser;
506
507         AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
508                 clear_caller(tmpuser);
509                 tmpuser->cleared = 1;
510         }
511 }
512
513
514
515 static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, int *status, struct fm_args *tpargs) 
516 {
517         struct ast_channel *watchers[256];
518         int pos;
519         struct ast_channel *winner;
520         struct ast_frame *f;
521         int ctstatus = 0;
522         int dg;
523         struct findme_user *tmpuser;
524         int to = 0;
525         int livechannels = 0;
526         int tmpto;
527         long totalwait = 0, wtd = 0, towas = 0;
528         char *callfromname;
529         char *pressbuttonname;
530
531         /* ------------ wait_for_winner_channel start --------------- */ 
532
533         callfromname = ast_strdupa(tpargs->callfromprompt);
534         pressbuttonname = ast_strdupa(tpargs->optionsprompt);
535
536         if (AST_LIST_EMPTY(findme_user_list)) {
537                 ast_verb(3, "couldn't reach at this number.\n");
538                 return NULL;
539         }
540
541         if (!caller) {
542                 ast_verb(3, "Original caller hungup. Cleanup.\n");
543                 clear_calling_tree(findme_user_list);
544                 return NULL;
545         }
546
547         totalwait = nm->timeout * 1000;
548
549         while (!ctstatus) {
550                 to = 1000;
551                 pos = 1; 
552                 livechannels = 0;
553                 watchers[0] = caller;
554
555                 dg = 0;
556                 winner = NULL;
557                 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
558                         if (tmpuser->state >= 0 && tmpuser->ochan) {
559                                 if (tmpuser->state == 3) 
560                                         tmpuser->digts += (towas - wtd);
561                                 if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
562                                         ast_verb(3, "We've been waiting for digits longer than we should have.\n");
563                                         if (!ast_strlen_zero(namerecloc)) {
564                                                 tmpuser->state = 1;
565                                                 tmpuser->digts = 0;
566                                                 if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
567                                                         ast_sched_runq(tmpuser->ochan->sched);
568                                                 } else {
569                                                         ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
570                                                         return NULL;
571                                                 }
572                                         } else {
573                                                 tmpuser->state = 2;
574                                                 tmpuser->digts = 0;
575                                                 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
576                                                         ast_sched_runq(tmpuser->ochan->sched);
577                                                 else {
578                                                         ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
579                                                         return NULL;
580                                                 }
581                                         }
582                                 }
583                                 if (tmpuser->ochan->stream) {
584                                         ast_sched_runq(tmpuser->ochan->sched);
585                                         tmpto = ast_sched_wait(tmpuser->ochan->sched);
586                                         if (tmpto > 0 && tmpto < to)
587                                                 to = tmpto;
588                                         else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
589                                                 ast_stopstream(tmpuser->ochan);
590                                                 if (tmpuser->state == 1) {
591                                                         ast_verb(3, "Playback of the call-from file appears to be done.\n");
592                                                         if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
593                                                                 tmpuser->state = 2;
594                                                         } else {
595                                                                 ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
596                                                                 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
597                                                                 tmpuser->ynidx = 0;
598                                                                 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
599                                                                         tmpuser->state = 3;
600                                                                 else {
601                                                                         ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
602                                                                         return NULL;
603                                                                 } 
604                                                         }
605                                                 } else if (tmpuser->state == 2) {
606                                                         ast_verb(3, "Playback of name file appears to be done.\n");
607                                                         memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
608                                                         tmpuser->ynidx = 0;
609                                                         if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
610                                                                 tmpuser->state = 3;
611                                                         } else {
612                                                                 return NULL;
613                                                         } 
614                                                 } else if (tmpuser->state == 3) {
615                                                         ast_verb(3, "Playback of the next step file appears to be done.\n");
616                                                         tmpuser->digts = 0;
617                                                 }
618                                         }
619                                 }
620                                 watchers[pos++] = tmpuser->ochan;
621                                 livechannels++;
622                         }
623                 }
624
625                 tmpto = to;
626                 if (to < 0) {
627                         to = 1000;
628                         tmpto = 1000;
629                 }
630                 towas = to;
631                 winner = ast_waitfor_n(watchers, pos, &to);
632                 tmpto -= to;
633                 totalwait -= tmpto;
634                 wtd = to;
635                 if (totalwait <= 0) {
636                         ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
637                         clear_calling_tree(findme_user_list);
638                         return NULL;
639                 }
640                 if (winner) {
641                         /* Need to find out which channel this is */
642                         dg = 0;
643                         while ((winner != watchers[dg]) && (dg < 256))
644                                 dg++;
645                         AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
646                                 if (tmpuser->ochan == winner)
647                                         break;
648                         f = ast_read(winner);
649                         if (f) {
650                                 if (f->frametype == AST_FRAME_CONTROL) {
651                                         switch (f->subclass.integer) {
652                                         case AST_CONTROL_HANGUP:
653                                                 ast_verb(3, "%s received a hangup frame.\n", winner->name);
654                                                 if (f->data.uint32) {
655                                                         winner->hangupcause = f->data.uint32;
656                                                 }
657                                                 if (dg == 0) {
658                                                         ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
659                                                         clear_calling_tree(findme_user_list);
660                                                         ctstatus = -1;
661                                                 }
662                                                 break;
663                                         case AST_CONTROL_ANSWER:
664                                                 ast_verb(3, "%s answered %s\n", winner->name, caller->name);
665                                                 /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
666                                                 winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
667                                                 caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
668                                                 ast_verb(3, "Starting playback of %s\n", callfromname);
669                                                 if (dg > 0) {
670                                                         if (!ast_strlen_zero(namerecloc)) {
671                                                                 if (!ast_streamfile(winner, callfromname, winner->language)) {
672                                                                         ast_sched_runq(winner->sched);
673                                                                         tmpuser->state = 1;
674                                                                 } else {
675                                                                         ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
676                                                                         ast_frfree(f);
677                                                                         return NULL;
678                                                                 }
679                                                         } else {
680                                                                 tmpuser->state = 2;
681                                                                 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
682                                                                         ast_sched_runq(tmpuser->ochan->sched);
683                                                                 else {
684                                                                         ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
685                                                                         ast_frfree(f);
686                                                                         return NULL;
687                                                                 }
688                                                         }
689                                                 }
690                                                 break;
691                                         case AST_CONTROL_BUSY:
692                                                 ast_verb(3, "%s is busy\n", winner->name);
693                                                 break;
694                                         case AST_CONTROL_CONGESTION:
695                                                 ast_verb(3, "%s is circuit-busy\n", winner->name);
696                                                 break;
697                                         case AST_CONTROL_RINGING:
698                                                 ast_verb(3, "%s is ringing\n", winner->name);
699                                                 break;
700                                         case AST_CONTROL_PROGRESS:
701                                                 ast_verb(3, "%s is making progress passing it to %s\n", winner->name, caller->name);
702                                                 break;
703                                         case AST_CONTROL_VIDUPDATE:
704                                                 ast_verb(3, "%s requested a video update, passing it to %s\n", winner->name, caller->name);
705                                                 break;
706                                         case AST_CONTROL_SRCUPDATE:
707                                                 ast_verb(3, "%s requested a source update, passing it to %s\n", winner->name, caller->name);
708                                                 break;
709                                         case AST_CONTROL_PROCEEDING:
710                                                 ast_verb(3, "%s is proceeding passing it to %s\n", winner->name,caller->name);
711                                                 break;
712                                         case AST_CONTROL_HOLD:
713                                                 ast_verb(3, "Call on %s placed on hold\n", winner->name);
714                                                 break;
715                                         case AST_CONTROL_UNHOLD:
716                                                 ast_verb(3, "Call on %s left from hold\n", winner->name);
717                                                 break;
718                                         case AST_CONTROL_OFFHOOK:
719                                         case AST_CONTROL_FLASH:
720                                                 /* Ignore going off hook and flash */
721                                                 break;
722                                         case -1:
723                                                 ast_verb(3, "%s stopped sounds\n", winner->name);
724                                                 break;
725                                         default:
726                                                 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
727                                                 break;
728                                         }
729                                 } 
730                                 if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
731                                         if (winner->stream)
732                                                 ast_stopstream(winner);
733                                         tmpuser->digts = 0;
734                                         ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
735                                         tmpuser->yn[tmpuser->ynidx] = (char) f->subclass.integer;
736                                         tmpuser->ynidx++;
737                                         ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
738                                         if (tmpuser->ynidx >= ynlongest) {
739                                                 ast_debug(1, "reached longest possible match - doing evals\n");
740                                                 if (!strcmp(tmpuser->yn, tpargs->takecall)) {
741                                                         ast_debug(1, "Match to take the call!\n");
742                                                         ast_frfree(f);
743                                                         return tmpuser->ochan;
744                                                 }
745                                                 if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
746                                                         ast_debug(1, "Next in dial plan step requested.\n");
747                                                         *status = 1;
748                                                         ast_frfree(f);
749                                                         return NULL;
750                                                 }
751
752                                         }
753                                 }
754
755                                 ast_frfree(f);
756                         } else {
757                                 if (winner) {
758                                         ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg);                                             
759                                         if (!dg) {
760                                                 clear_calling_tree(findme_user_list);
761                                                 return NULL;
762                                         } else {
763                                                 tmpuser->state = -1;
764                                                 ast_hangup(winner);  
765                                                 livechannels--;
766                                                 ast_debug(1, "live channels left %d\n", livechannels);
767                                                 if (!livechannels) {
768                                                         ast_verb(3, "no live channels left. exiting.\n");
769                                                         return NULL;
770                                                 }
771                                         }
772                                 }
773                         }
774
775                 } else
776                         ast_debug(1, "timed out waiting for action\n");
777         }
778
779         /* --- WAIT FOR WINNER NUMBER END! -----------*/
780         return NULL;
781 }
782
783 static void findmeexec(struct fm_args *tpargs)
784 {
785         struct number *nm;
786         struct ast_channel *outbound;
787         struct ast_channel *caller;
788         struct ast_channel *winner = NULL;
789         char dialarg[512];
790         int dg, idx;
791         char *rest, *number;
792         struct findme_user *tmpuser;
793         struct findme_user *fmuser;
794         struct findme_user *headuser;
795         struct findme_user_listptr *findme_user_list;
796         int status;
797
798         findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
799         AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
800
801         /* We're going to figure out what the longest possible string of digits to collect is */
802         ynlongest = 0;
803         if (strlen(tpargs->takecall) > ynlongest)
804                 ynlongest = strlen(tpargs->takecall);
805         if (strlen(tpargs->nextindp) > ynlongest)
806                 ynlongest = strlen(tpargs->nextindp);
807
808         idx = 1;
809         caller = tpargs->chan;
810         AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
811                 if (nm->order == idx)
812                         break;
813
814         while (nm) {
815
816                 ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
817
818                 number = ast_strdupa(nm->number);
819                 ast_debug(3, "examining %s\n", number);
820                 do {
821                         rest = strchr(number, '&');
822                         if (rest) {
823                                 *rest = 0;
824                                 rest++;
825                         }
826
827                         if (!strcmp(tpargs->context, ""))
828                                 snprintf(dialarg, sizeof(dialarg), "%s", number);
829                         else
830                                 snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
831
832                         tmpuser = ast_calloc(1, sizeof(*tmpuser));
833                         if (!tmpuser) {
834                                 ast_log(LOG_WARNING, "Out of memory!\n");
835                                 ast_free(findme_user_list);
836                                 return;
837                         }
838
839                         outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg);
840                         if (outbound) {
841                                 ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
842                                 ast_channel_inherit_variables(tpargs->chan, outbound);
843                                 ast_channel_datastore_inherit(tpargs->chan, outbound);
844                                 ast_string_field_set(outbound, language, tpargs->chan->language);
845                                 ast_string_field_set(outbound, accountcode, tpargs->chan->accountcode);
846                                 ast_string_field_set(outbound, musicclass, tpargs->chan->musicclass);
847                                 ast_verb(3, "calling %s\n", dialarg);
848                                 if (!ast_call(outbound,dialarg,0)) {
849                                         tmpuser->ochan = outbound;
850                                         tmpuser->state = 0;
851                                         tmpuser->cleared = 0;
852                                         ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
853                                         AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
854                                 } else {
855                                         ast_verb(3, "couldn't reach at this number.\n"); 
856                                         if (outbound) {
857                                                 if (!outbound->cdr) 
858                                                         outbound->cdr = ast_cdr_alloc();
859                                                 if (outbound->cdr) {
860                                                         char tmp[256];
861
862                                                         ast_cdr_init(outbound->cdr, outbound);
863                                                         snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
864                                                         ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
865                                                         ast_cdr_update(outbound);
866                                                         ast_cdr_start(outbound->cdr);
867                                                         ast_cdr_end(outbound->cdr);
868                                                         /* If the cause wasn't handled properly */
869                                                         if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
870                                                                 ast_cdr_failed(outbound->cdr);
871                                                 } else {
872                                                         ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
873                                                         ast_hangup(outbound);
874                                                         outbound = NULL;
875                                                 }
876                                         }
877                                 }
878                         } else 
879                                 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
880
881                         number = rest;
882                 } while (number);
883
884                 status = 0;
885                 if (!AST_LIST_EMPTY(findme_user_list))
886                         winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
887
888                 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
889                         if (!fmuser->cleared && fmuser->ochan != winner)
890                                 clear_caller(fmuser);
891                         ast_free(fmuser);
892                 }
893
894                 fmuser = NULL;
895                 tmpuser = NULL;
896                 headuser = NULL;
897                 if (winner)
898                         break;
899
900                 if (!caller || ast_check_hangup(caller)) {
901                         tpargs->status = 1;
902                         ast_free(findme_user_list);
903                         return;
904                 }
905
906                 idx++;
907                 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
908                         if (nm->order == idx)
909                                 break;
910                 }
911         }
912         ast_free(findme_user_list);
913         if (!winner) 
914                 tpargs->status = 1;
915         else {
916                 tpargs->status = 100;
917                 tpargs->outbound = winner;
918         }
919
920         return;
921 }
922
923 static struct call_followme *find_realtime(const char *name)
924 {
925         struct ast_variable *var = ast_load_realtime("followme", "name", name, SENTINEL), *v;
926         struct ast_config *cfg;
927         const char *catg;
928         struct call_followme *new;
929         struct ast_str *str = ast_str_create(16);
930
931         if (!var) {
932                 return NULL;
933         }
934
935         if (!(new = alloc_profile(name))) {
936                 return NULL;
937         }
938
939         for (v = var; v; v = v->next) {
940                 if (!strcasecmp(v->name, "active")) {
941                         if (ast_false(v->value)) {
942                                 ast_mutex_destroy(&new->lock);
943                                 ast_free(new);
944                                 return NULL;
945                         }
946                 } else {
947                         profile_set_param(new, v->name, v->value, 0, 0);
948                 }
949         }
950
951         ast_variables_destroy(var);
952         new->realtime = 1;
953
954         /* Load numbers */
955         if (!(cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name", name, SENTINEL))) {
956                 ast_mutex_destroy(&new->lock);
957                 ast_free(new);
958                 return NULL;
959         }
960
961         for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
962                 const char *numstr, *timeoutstr, *ordstr;
963                 int timeout;
964                 struct number *cur;
965                 if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
966                         continue;
967                 }
968                 if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout")) || sscanf(timeoutstr, "%30d", &timeout) != 1 || timeout < 1) {
969                         timeout = 25;
970                 }
971                 /* This one has to exist; it was part of the query */
972                 ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
973                 ast_str_set(&str, 0, "%s", numstr);
974                 if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
975                         AST_LIST_INSERT_TAIL(&new->numbers, cur, entry);
976                 }
977         }
978         ast_config_destroy(cfg);
979
980         return new;
981 }
982
983 static void end_bridge_callback(void *data)
984 {
985         char buf[80];
986         time_t end;
987         struct ast_channel *chan = data;
988
989         time(&end);
990
991         ast_channel_lock(chan);
992         if (chan->cdr->answer.tv_sec) {
993                 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
994                 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
995         }
996
997         if (chan->cdr->start.tv_sec) {
998                 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
999                 pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
1000         }
1001         ast_channel_unlock(chan);
1002 }
1003
1004 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
1005 {
1006         bconfig->end_bridge_callback_data = originator;
1007 }
1008
1009 static int app_exec(struct ast_channel *chan, const char *data)
1010 {
1011         struct fm_args targs = { 0, };
1012         struct ast_bridge_config config;
1013         struct call_followme *f;
1014         struct number *nm, *newnm;
1015         int res = 0;
1016         char *argstr;
1017         char namerecloc[255];
1018         int duration = 0;
1019         struct ast_channel *caller;
1020         struct ast_channel *outbound;
1021         AST_DECLARE_APP_ARGS(args,
1022                 AST_APP_ARG(followmeid);
1023                 AST_APP_ARG(options);
1024         );
1025
1026         if (ast_strlen_zero(data)) {
1027                 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1028                 return -1;
1029         }
1030
1031         if (!(argstr = ast_strdupa((char *)data))) {
1032                 ast_log(LOG_ERROR, "Out of memory!\n");
1033                 return -1;
1034         }
1035
1036         AST_STANDARD_APP_ARGS(args, argstr);
1037
1038         if (ast_strlen_zero(args.followmeid)) {
1039                 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1040                 return -1;
1041         }
1042
1043         AST_RWLIST_RDLOCK(&followmes);
1044         AST_RWLIST_TRAVERSE(&followmes, f, entry) {
1045                 if (!strcasecmp(f->name, args.followmeid) && (f->active))
1046                         break;
1047         }
1048         AST_RWLIST_UNLOCK(&followmes);
1049
1050         ast_debug(1, "New profile %s.\n", args.followmeid);
1051
1052         if (!f) {
1053                 f = find_realtime(args.followmeid);
1054         }
1055
1056         if (!f) {
1057                 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
1058                 return 0;
1059         }
1060
1061         /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
1062         if (args.options) 
1063                 ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
1064
1065         /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
1066         ast_mutex_lock(&f->lock);
1067         targs.mohclass = ast_strdupa(f->moh);
1068         ast_copy_string(targs.context, f->context, sizeof(targs.context));
1069         ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
1070         ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
1071         ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
1072         ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
1073         ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
1074         ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
1075         ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
1076         ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
1077         /* Copy the numbers we're going to use into another list in case the master list should get modified 
1078            (and locked) while we're trying to do a follow-me */
1079         AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
1080         AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
1081                 newnm = create_followme_number(nm->number, nm->timeout, nm->order);
1082                 AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
1083         }
1084         ast_mutex_unlock(&f->lock);
1085
1086         /* Answer the call */
1087         if (chan->_state != AST_STATE_UP) {
1088                 ast_answer(chan);
1089         }
1090
1091         if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG)) 
1092                 ast_stream_and_wait(chan, targs.statusprompt, "");
1093
1094         snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
1095         duration = 5;
1096
1097         if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) 
1098                 if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0)
1099                         goto outrun;
1100
1101         if (!ast_fileexists(namerecloc, NULL, chan->language))
1102                 ast_copy_string(namerecloc, "", sizeof(namerecloc));
1103         if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
1104                 if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
1105                         goto outrun;
1106                 if (ast_waitstream(chan, "") < 0)
1107                         goto outrun;
1108         }
1109         ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
1110
1111         targs.status = 0;
1112         targs.chan = chan;
1113         ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
1114
1115         findmeexec(&targs);
1116
1117         while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
1118                 ast_free(nm);
1119
1120         if (!ast_strlen_zero(namerecloc))
1121                 unlink(namerecloc);
1122
1123         if (targs.status != 100) {
1124                 ast_moh_stop(chan);
1125                 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) 
1126                         ast_stream_and_wait(chan, targs.sorryprompt, "");
1127                 res = 0;
1128         } else {
1129                 caller = chan;
1130                 outbound = targs.outbound;
1131                 /* Bridge the two channels. */
1132
1133                 memset(&config, 0, sizeof(config));
1134                 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
1135                 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
1136                 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
1137                 config.end_bridge_callback = end_bridge_callback;
1138                 config.end_bridge_callback_data = chan;
1139                 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
1140
1141                 ast_moh_stop(caller);
1142                 /* Be sure no generators are left on it */
1143                 ast_deactivate_generator(caller);
1144                 /* Make sure channels are compatible */
1145                 res = ast_channel_make_compatible(caller, outbound);
1146                 if (res < 0) {
1147                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
1148                         ast_hangup(outbound);
1149                         goto outrun;
1150                 }
1151                 res = ast_bridge_call(caller, outbound, &config);
1152                 if (outbound)
1153                         ast_hangup(outbound);
1154         }
1155
1156         outrun:
1157
1158         if (f->realtime) {
1159                 /* Not in list */
1160                 free_numbers(f);
1161                 ast_free(f);
1162         }
1163
1164         return res;
1165 }
1166
1167 static int unload_module(void)
1168 {
1169         struct call_followme *f;
1170
1171         ast_unregister_application(app);
1172
1173         /* Free Memory. Yeah! I'm free! */
1174         AST_RWLIST_WRLOCK(&followmes);
1175         while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
1176                 free_numbers(f);
1177                 ast_free(f);
1178         }
1179
1180         AST_RWLIST_UNLOCK(&followmes);
1181
1182         return 0;
1183 }
1184
1185 static int load_module(void)
1186 {
1187         if(!reload_followme(0))
1188                 return AST_MODULE_LOAD_DECLINE;
1189
1190         return ast_register_application_xml(app, app_exec);
1191 }
1192
1193 static int reload(void)
1194 {
1195         reload_followme(1);
1196
1197         return 0;
1198 }
1199
1200 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
1201                 .load = load_module,
1202                 .unload = unload_module,
1203                 .reload = reload,
1204                );