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