00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "asterisk.h"
00036
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 218577 $")
00038
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <unistd.h>
00042 #include <string.h>
00043 #include <signal.h>
00044
00045 #include "asterisk/lock.h"
00046 #include "asterisk/file.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/module.h"
00052 #include "asterisk/translate.h"
00053 #include "asterisk/say.h"
00054 #include "asterisk/features.h"
00055 #include "asterisk/musiconhold.h"
00056 #include "asterisk/cli.h"
00057 #include "asterisk/manager.h"
00058 #include "asterisk/config.h"
00059 #include "asterisk/monitor.h"
00060 #include "asterisk/utils.h"
00061 #include "asterisk/causes.h"
00062 #include "asterisk/astdb.h"
00063 #include "asterisk/app.h"
00064
00065 static char *app = "FollowMe";
00066 static char *synopsis = "Find-Me/Follow-Me application";
00067 static char *descrip =
00068 " FollowMe(followmeid|options):\n"
00069 "This application performs Find-Me/Follow-Me functionality for the caller\n"
00070 "as defined in the profile matching the <followmeid> parameter in\n"
00071 "followme.conf. If the specified <followmeid> profile doesn't exist in\n"
00072 "followme.conf, execution will be returned to the dialplan and call\n"
00073 "execution will continue at the next priority.\n\n"
00074 " Options:\n"
00075 " s - Playback the incoming status message prior to starting the follow-me step(s)\n"
00076 " a - Record the caller's name so it can be announced to the callee on each step\n"
00077 " n - Playback the unreachable status message if we've run out of steps to reach the\n"
00078 " or the callee has elected not to be reachable.\n"
00079 "Returns -1 on hangup\n";
00080
00081
00082 struct number {
00083 char number[512];
00084 long timeout;
00085 char language[MAX_LANGUAGE];
00086 int order;
00087 AST_LIST_ENTRY(number) entry;
00088 };
00089
00090
00091 struct call_followme {
00092 ast_mutex_t lock;
00093 char name[AST_MAX_EXTENSION];
00094 char moh[AST_MAX_CONTEXT];
00095 char context[AST_MAX_CONTEXT];
00096 unsigned int active;
00097 char takecall[20];
00098 char nextindp[20];
00099 char callfromprompt[PATH_MAX];
00100 char norecordingprompt[PATH_MAX];
00101 char optionsprompt[PATH_MAX];
00102 char plsholdprompt[PATH_MAX];
00103 char statusprompt[PATH_MAX];
00104 char sorryprompt[PATH_MAX];
00105
00106 AST_LIST_HEAD_NOLOCK(numbers, number) numbers;
00107 AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers;
00108 AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers;
00109 AST_LIST_ENTRY(call_followme) entry;
00110 };
00111
00112 struct fm_args {
00113 struct ast_channel *chan;
00114 char *mohclass;
00115 AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
00116 int status;
00117 char context[AST_MAX_CONTEXT];
00118 char namerecloc[AST_MAX_CONTEXT];
00119 struct ast_channel *outbound;
00120 char takecall[20];
00121 char nextindp[20];
00122 char callfromprompt[PATH_MAX];
00123 char norecordingprompt[PATH_MAX];
00124 char optionsprompt[PATH_MAX];
00125 char plsholdprompt[PATH_MAX];
00126 char statusprompt[PATH_MAX];
00127 char sorryprompt[PATH_MAX];
00128 struct ast_flags followmeflags;
00129 };
00130
00131 struct findme_user {
00132 struct ast_channel *ochan;
00133 int state;
00134 char dialarg[256];
00135 char yn[10];
00136 int ynidx;
00137 long digts;
00138 int cleared;
00139 AST_LIST_ENTRY(findme_user) entry;
00140 };
00141
00142 enum {
00143 FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00144 FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00145 FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2)
00146 };
00147
00148 AST_APP_OPTIONS(followme_opts, {
00149 AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ),
00150 AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ),
00151 AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ),
00152 });
00153
00154 static int ynlongest = 0;
00155
00156 static const char *featuredigittostr;
00157 static int featuredigittimeout = 5000;
00158 static const char *defaultmoh = "default";
00159
00160 static char takecall[20] = "1", nextindp[20] = "2";
00161 static char callfromprompt[PATH_MAX] = "followme/call-from";
00162 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
00163 static char optionsprompt[PATH_MAX] = "followme/options";
00164 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
00165 static char statusprompt[PATH_MAX] = "followme/status";
00166 static char sorryprompt[PATH_MAX] = "followme/sorry";
00167
00168
00169 static AST_LIST_HEAD_STATIC(followmes, call_followme);
00170 AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
00171
00172 static void free_numbers(struct call_followme *f)
00173 {
00174
00175 struct number *prev;
00176
00177 while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00178
00179 free(prev);
00180 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00181
00182 while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00183
00184 free(prev);
00185 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00186
00187 while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00188
00189 free(prev);
00190 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00191
00192 }
00193
00194
00195
00196 static struct call_followme *alloc_profile(const char *fmname)
00197 {
00198 struct call_followme *f;
00199
00200 if (!(f = ast_calloc(1, sizeof(*f))))
00201 return NULL;
00202
00203 ast_mutex_init(&f->lock);
00204 ast_copy_string(f->name, fmname, sizeof(f->name));
00205 f->moh[0] = '\0';
00206 f->context[0] = '\0';
00207 ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00208 ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00209 ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00210 ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00211 ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00212 ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00213 ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00214 ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00215 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00216 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00217 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00218 return f;
00219 }
00220
00221 static void init_profile(struct call_followme *f)
00222 {
00223 f->active = 1;
00224 ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00225 }
00226
00227
00228
00229
00230 static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
00231 {
00232
00233 if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
00234 ast_copy_string(f->moh, val, sizeof(f->moh));
00235 else if (!strcasecmp(param, "context"))
00236 ast_copy_string(f->context, val, sizeof(f->context));
00237 else if (!strcasecmp(param, "takecall"))
00238 ast_copy_string(f->takecall, val, sizeof(f->takecall));
00239 else if (!strcasecmp(param, "declinecall"))
00240 ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00241 else if (!strcasecmp(param, "call-from-prompt"))
00242 ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00243 else if (!strcasecmp(param, "followme-norecording-prompt"))
00244 ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00245 else if (!strcasecmp(param, "followme-options-prompt"))
00246 ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00247 else if (!strcasecmp(param, "followme-pls-hold-prompt"))
00248 ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00249 else if (!strcasecmp(param, "followme-status-prompt"))
00250 ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00251 else if (!strcasecmp(param, "followme-sorry-prompt"))
00252 ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00253 else if (failunknown) {
00254 if (linenum >= 0)
00255 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00256 else
00257 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00258 }
00259 }
00260
00261
00262 static struct number *create_followme_number(char *number, char *language, int timeout, int numorder)
00263 {
00264 struct number *cur;
00265 char *tmp;
00266
00267
00268 if (!(cur = ast_calloc(1, sizeof(*cur))))
00269 return NULL;
00270
00271 cur->timeout = timeout;
00272 if ((tmp = strchr(number, ',')))
00273 *tmp = '\0';
00274 ast_copy_string(cur->number, number, sizeof(cur->number));
00275 ast_copy_string(cur->language, language, sizeof(cur->language));
00276 cur->order = numorder;
00277 if (option_debug)
00278 ast_log(LOG_DEBUG, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00279
00280 return cur;
00281 }
00282
00283
00284 static int reload_followme(void)
00285 {
00286 struct call_followme *f;
00287 struct ast_config *cfg;
00288 char *cat = NULL, *tmp;
00289 struct ast_variable *var;
00290 struct number *cur, *nm;
00291 int new, idx;
00292 char numberstr[90];
00293 int timeout;
00294 char *timeoutstr;
00295 int numorder;
00296 const char *takecallstr;
00297 const char *declinecallstr;
00298 const char *tmpstr;
00299
00300 cfg = ast_config_load("followme.conf");
00301 if (!cfg) {
00302 ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00303 return 0;
00304 }
00305
00306 AST_LIST_LOCK(&followmes);
00307
00308
00309 featuredigittimeout = 5000;
00310
00311
00312 AST_LIST_TRAVERSE(&followmes, f, entry) {
00313 f->active = 0;
00314 }
00315 featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00316
00317 if (!ast_strlen_zero(featuredigittostr)) {
00318 if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
00319 featuredigittimeout = 5000;
00320 }
00321
00322 takecallstr = ast_variable_retrieve(cfg, "general", "takecall");
00323 if (!ast_strlen_zero(takecallstr))
00324 ast_copy_string(takecall, takecallstr, sizeof(takecall));
00325
00326 declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall");
00327 if (!ast_strlen_zero(declinecallstr))
00328 ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00329
00330 tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt");
00331 if (!ast_strlen_zero(tmpstr))
00332 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00333
00334 tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt");
00335 if (!ast_strlen_zero(tmpstr))
00336 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00337
00338 tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt");
00339 if (!ast_strlen_zero(tmpstr))
00340 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00341
00342 tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt");
00343 if (!ast_strlen_zero(tmpstr))
00344 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00345
00346 tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt");
00347 if (!ast_strlen_zero(tmpstr))
00348 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00349
00350 tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt");
00351 if (!ast_strlen_zero(tmpstr))
00352 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00353
00354
00355 while ((cat = ast_category_browse(cfg, cat))) {
00356 if (!strcasecmp(cat, "general"))
00357 continue;
00358
00359
00360 AST_LIST_TRAVERSE(&followmes, f, entry) {
00361 if (!strcasecmp(f->name, cat))
00362 break;
00363 }
00364 if (option_debug)
00365 ast_log(LOG_DEBUG, "New profile %s.\n", cat);
00366 if (!f) {
00367
00368 f = alloc_profile(cat);
00369 new = 1;
00370 } else
00371 new = 0;
00372
00373 if (f) {
00374 if (!new)
00375 ast_mutex_lock(&f->lock);
00376
00377 init_profile(f);
00378 free_numbers(f);
00379 var = ast_variable_browse(cfg, cat);
00380 while(var) {
00381 if (!strcasecmp(var->name, "number")) {
00382
00383 ast_copy_string(numberstr, var->value, sizeof(numberstr));
00384 if ((tmp = strchr(numberstr, ','))) {
00385 *tmp = '\0';
00386 tmp++;
00387 timeoutstr = ast_strdupa(tmp);
00388 if ((tmp = strchr(timeoutstr, ','))) {
00389 *tmp = '\0';
00390 tmp++;
00391 numorder = atoi(tmp);
00392 if (numorder < 0)
00393 numorder = 0;
00394 } else
00395 numorder = 0;
00396 timeout = atoi(timeoutstr);
00397 if (timeout < 0)
00398 timeout = 25;
00399 } else {
00400 timeout = 25;
00401 numorder = 0;
00402 }
00403
00404 if (!numorder) {
00405 idx = 1;
00406 AST_LIST_TRAVERSE(&f->numbers, nm, entry)
00407 idx++;
00408 numorder = idx;
00409 }
00410 cur = create_followme_number(numberstr, "", timeout, numorder);
00411 AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00412 } else {
00413 profile_set_param(f, var->name, var->value, var->lineno, 1);
00414 if (option_debug > 1)
00415 ast_log(LOG_DEBUG, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00416 }
00417 var = var->next;
00418 }
00419
00420 if (!new)
00421 ast_mutex_unlock(&f->lock);
00422 else
00423 AST_LIST_INSERT_HEAD(&followmes, f, entry);
00424 }
00425 }
00426 ast_config_destroy(cfg);
00427
00428 AST_LIST_UNLOCK(&followmes);
00429
00430 return 1;
00431 }
00432
00433 static void clear_caller(struct findme_user *tmpuser)
00434 {
00435 struct ast_channel *outbound;
00436
00437 if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00438 outbound = tmpuser->ochan;
00439 if (!outbound->cdr) {
00440 outbound->cdr = ast_cdr_alloc();
00441 if (outbound->cdr)
00442 ast_cdr_init(outbound->cdr, outbound);
00443 }
00444 if (outbound->cdr) {
00445 char tmp[256];
00446
00447 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00448 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00449 ast_cdr_update(outbound);
00450 ast_cdr_start(outbound->cdr);
00451 ast_cdr_end(outbound->cdr);
00452
00453 if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
00454 ast_cdr_failed(outbound->cdr);
00455 } else
00456 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00457 ast_hangup(tmpuser->ochan);
00458 }
00459
00460 }
00461
00462 static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
00463 {
00464 struct findme_user *tmpuser;
00465
00466 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00467 clear_caller(tmpuser);
00468 tmpuser->cleared = 1;
00469 }
00470
00471 }
00472
00473
00474
00475 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)
00476 {
00477 struct ast_channel *watchers[256];
00478 int pos;
00479 struct ast_channel *winner;
00480 struct ast_frame *f;
00481 int ctstatus;
00482 int dg;
00483 struct findme_user *tmpuser;
00484 int to = 0;
00485 int livechannels = 0;
00486 int tmpto;
00487 long totalwait = 0, wtd, towas = 0;
00488 char *callfromname;
00489 char *pressbuttonname;
00490
00491
00492
00493 callfromname = ast_strdupa(tpargs->callfromprompt);
00494 pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00495
00496 if (!AST_LIST_EMPTY(findme_user_list)) {
00497 if (!caller) {
00498 if (option_verbose > 2)
00499 ast_verbose(VERBOSE_PREFIX_3 "Original caller hungup. Cleanup.\n");
00500 clear_calling_tree(findme_user_list);
00501 return NULL;
00502 }
00503 ctstatus = 0;
00504 totalwait = nm->timeout * 1000;
00505 wtd = 0;
00506 while (!ctstatus) {
00507 to = 1000;
00508 pos = 1;
00509 livechannels = 0;
00510 watchers[0] = caller;
00511
00512 dg = 0;
00513 winner = NULL;
00514 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00515 if (tmpuser->state >= 0 && tmpuser->ochan) {
00516 if (tmpuser->state == 3)
00517 tmpuser->digts += (towas - wtd);
00518 if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00519 if (option_verbose > 2)
00520 ast_verbose(VERBOSE_PREFIX_3 "We've been waiting for digits longer than we should have.\n");
00521 if (!ast_strlen_zero(namerecloc)) {
00522 tmpuser->state = 1;
00523 tmpuser->digts = 0;
00524 if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
00525 ast_sched_runq(tmpuser->ochan->sched);
00526 } else {
00527 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00528 return NULL;
00529 }
00530 } else {
00531 tmpuser->state = 2;
00532 tmpuser->digts = 0;
00533 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00534 ast_sched_runq(tmpuser->ochan->sched);
00535 else {
00536 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00537 return NULL;
00538 }
00539 }
00540 }
00541 if (tmpuser->ochan->stream) {
00542 ast_sched_runq(tmpuser->ochan->sched);
00543 tmpto = ast_sched_wait(tmpuser->ochan->sched);
00544 if (tmpto > 0 && tmpto < to)
00545 to = tmpto;
00546 else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
00547 ast_stopstream(tmpuser->ochan);
00548 if (tmpuser->state == 1) {
00549 if (option_verbose > 2)
00550 ast_verbose(VERBOSE_PREFIX_3 "Playback of the call-from file appears to be done.\n");
00551 if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
00552 tmpuser->state = 2;
00553 } else {
00554 ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
00555 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00556 tmpuser->ynidx = 0;
00557 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
00558 tmpuser->state = 3;
00559 else {
00560 ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00561 return NULL;
00562 }
00563 }
00564 } else if (tmpuser->state == 2) {
00565 if (option_verbose > 2)
00566 ast_verbose(VERBOSE_PREFIX_3 "Playback of name file appears to be done.\n");
00567 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00568 tmpuser->ynidx = 0;
00569 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
00570 tmpuser->state = 3;
00571
00572 } else {
00573 return NULL;
00574 }
00575 } else if (tmpuser->state == 3) {
00576 if (option_verbose > 2)
00577 ast_verbose(VERBOSE_PREFIX_3 "Playback of the next step file appears to be done.\n");
00578 tmpuser->digts = 0;
00579 }
00580 }
00581 }
00582 watchers[pos++] = tmpuser->ochan;
00583 livechannels++;
00584 }
00585 }
00586
00587 tmpto = to;
00588 if (to < 0) {
00589 to = 1000;
00590 tmpto = 1000;
00591 }
00592 towas = to;
00593 winner = ast_waitfor_n(watchers, pos, &to);
00594 tmpto -= to;
00595 totalwait -= tmpto;
00596 wtd = to;
00597 if (totalwait <= 0) {
00598 if (option_verbose > 2)
00599 ast_verbose(VERBOSE_PREFIX_3 "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
00600 clear_calling_tree(findme_user_list);
00601 return NULL;
00602 }
00603 if (winner) {
00604
00605 dg = 0;
00606 while ((winner != watchers[dg]) && (dg < 256))
00607 dg++;
00608 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
00609 if (tmpuser->ochan == winner)
00610 break;
00611 f = ast_read(winner);
00612 if (f) {
00613 if (f->frametype == AST_FRAME_CONTROL) {
00614 switch(f->subclass) {
00615 case AST_CONTROL_HANGUP:
00616 if (option_verbose > 2)
00617 ast_verbose( VERBOSE_PREFIX_3 "%s received a hangup frame.\n", winner->name);
00618 if (dg == 0) {
00619 if (option_verbose > 2)
00620 ast_verbose( VERBOSE_PREFIX_3 "The calling channel hungup. Need to drop everyone else.\n");
00621 clear_calling_tree(findme_user_list);
00622 ctstatus = -1;
00623 }
00624 break;
00625 case AST_CONTROL_ANSWER:
00626 if (option_verbose > 2)
00627 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", winner->name, caller->name);
00628
00629 winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00630 caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00631 if (option_verbose > 2)
00632 ast_verbose( VERBOSE_PREFIX_3 "Starting playback of %s\n", callfromname);
00633 if (dg > 0) {
00634 if (!ast_strlen_zero(namerecloc)) {
00635 if (!ast_streamfile(winner, callfromname, winner->language)) {
00636 ast_sched_runq(winner->sched);
00637 tmpuser->state = 1;
00638 } else {
00639 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00640 ast_frfree(f);
00641 return NULL;
00642 }
00643 } else {
00644 tmpuser->state = 2;
00645 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00646 ast_sched_runq(tmpuser->ochan->sched);
00647 else {
00648 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00649 ast_frfree(f);
00650 return NULL;
00651 }
00652 }
00653 }
00654 break;
00655 case AST_CONTROL_BUSY:
00656 if (option_verbose > 2)
00657 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", winner->name);
00658 break;
00659 case AST_CONTROL_CONGESTION:
00660 if (option_verbose > 2)
00661 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", winner->name);
00662 break;
00663 case AST_CONTROL_RINGING:
00664 if (option_verbose > 2)
00665 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", winner->name);
00666 break;
00667 case AST_CONTROL_PROGRESS:
00668 if (option_verbose > 2)
00669 ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", winner->name, caller->name);
00670 break;
00671 case AST_CONTROL_VIDUPDATE:
00672 if (option_verbose > 2)
00673 ast_verbose ( VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", winner->name, caller->name);
00674 break;
00675 case AST_CONTROL_SRCUPDATE:
00676 if (option_verbose > 2)
00677 ast_verbose ( VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", winner->name, caller->name);
00678 break;
00679 case AST_CONTROL_PROCEEDING:
00680 if (option_verbose > 2)
00681 ast_verbose ( VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", winner->name,caller->name);
00682 break;
00683 case AST_CONTROL_HOLD:
00684 if (option_verbose > 2)
00685 ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", winner->name);
00686 break;
00687 case AST_CONTROL_UNHOLD:
00688 if (option_verbose > 2)
00689 ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", winner->name);
00690 break;
00691 case AST_CONTROL_OFFHOOK:
00692 case AST_CONTROL_FLASH:
00693
00694 break;
00695 case -1:
00696 if (option_verbose > 2)
00697 ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", winner->name);
00698 break;
00699 default:
00700 if (option_debug)
00701 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
00702 break;
00703 }
00704 }
00705 if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00706 if (winner->stream)
00707 ast_stopstream(winner);
00708 tmpuser->digts = 0;
00709 if (option_debug)
00710 ast_log(LOG_DEBUG, "DTMF received: %c\n",(char) f->subclass);
00711 tmpuser->yn[tmpuser->ynidx] = (char) f->subclass;
00712 tmpuser->ynidx++;
00713 if (option_debug)
00714 ast_log(LOG_DEBUG, "DTMF string: %s\n", tmpuser->yn);
00715 if (tmpuser->ynidx >= ynlongest) {
00716 if (option_debug)
00717 ast_log(LOG_DEBUG, "reached longest possible match - doing evals\n");
00718 if (!strcmp(tmpuser->yn, tpargs->takecall)) {
00719 if (option_debug)
00720 ast_log(LOG_DEBUG, "Match to take the call!\n");
00721 ast_frfree(f);
00722 return tmpuser->ochan;
00723 }
00724 if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
00725 if (option_debug)
00726 ast_log(LOG_DEBUG, "Next in dial plan step requested.\n");
00727 *status = 1;
00728 ast_frfree(f);
00729 return NULL;
00730 }
00731
00732 }
00733 }
00734
00735 ast_frfree(f);
00736 } else {
00737 if (winner) {
00738 if (option_debug)
00739 ast_log(LOG_DEBUG, "we didn't get a frame. hanging up. dg is %d\n",dg);
00740 if (!dg) {
00741 clear_calling_tree(findme_user_list);
00742 return NULL;
00743 } else {
00744 tmpuser->state = -1;
00745 ast_hangup(winner);
00746 livechannels--;
00747 if (option_debug)
00748 ast_log(LOG_DEBUG, "live channels left %d\n", livechannels);
00749 if (!livechannels) {
00750 if (option_verbose > 2)
00751 ast_verbose(VERBOSE_PREFIX_3 "no live channels left. exiting.\n");
00752 return NULL;
00753 }
00754 }
00755 }
00756 }
00757
00758 } else
00759 if (option_debug)
00760 ast_log(LOG_DEBUG, "timed out waiting for action\n");
00761 }
00762
00763 } else {
00764 if (option_verbose > 2)
00765 ast_verbose(VERBOSE_PREFIX_3 "couldn't reach at this number.\n");
00766 }
00767
00768
00769 return NULL;
00770 }
00771
00772 static void findmeexec(struct fm_args *tpargs)
00773 {
00774 struct number *nm;
00775 struct ast_channel *outbound;
00776 struct ast_channel *caller;
00777 struct ast_channel *winner = NULL;
00778 char dialarg[512];
00779 int dg, idx;
00780 char *rest, *number;
00781 struct findme_user *tmpuser;
00782 struct findme_user *fmuser;
00783 struct findme_user *headuser;
00784 struct findme_user_listptr *findme_user_list;
00785 int status;
00786
00787 findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
00788 AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
00789
00790
00791 ynlongest = 0;
00792 if (strlen(tpargs->takecall) > ynlongest)
00793 ynlongest = strlen(tpargs->takecall);
00794 if (strlen(tpargs->nextindp) > ynlongest)
00795 ynlongest = strlen(tpargs->nextindp);
00796
00797 idx = 1;
00798 caller = tpargs->chan;
00799 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
00800 if (nm->order == idx)
00801 break;
00802
00803 while (nm) {
00804
00805 if (option_debug > 1)
00806 ast_log(LOG_DEBUG, "Number %s timeout %ld\n", nm->number,nm->timeout);
00807
00808 number = ast_strdupa(nm->number);
00809 if (option_debug > 2)
00810 ast_log(LOG_DEBUG, "examining %s\n", number);
00811 do {
00812 rest = strchr(number, '&');
00813 if (rest) {
00814 *rest = 0;
00815 rest++;
00816 }
00817
00818 if (!strcmp(tpargs->context, ""))
00819 snprintf(dialarg, sizeof(dialarg), "%s", number);
00820 else
00821 snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
00822
00823 tmpuser = ast_calloc(1, sizeof(*tmpuser));
00824 if (!tmpuser) {
00825 ast_log(LOG_WARNING, "Out of memory!\n");
00826 free(findme_user_list);
00827 return;
00828 }
00829
00830 outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
00831 if (outbound) {
00832 ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
00833 ast_channel_inherit_variables(tpargs->chan, outbound);
00834 ast_channel_datastore_inherit(tpargs->chan, outbound);
00835 ast_string_field_set(outbound, language, tpargs->chan->language);
00836 ast_string_field_set(outbound, accountcode, tpargs->chan->accountcode);
00837 ast_string_field_set(outbound, musicclass, tpargs->chan->musicclass);
00838 if (option_verbose > 2)
00839 ast_verbose(VERBOSE_PREFIX_3 "calling %s\n", dialarg);
00840 if (!ast_call(outbound,dialarg,0)) {
00841 tmpuser->ochan = outbound;
00842 tmpuser->state = 0;
00843 tmpuser->cleared = 0;
00844 ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00845 AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00846 } else {
00847 if (option_verbose > 2)
00848 ast_verbose(VERBOSE_PREFIX_3 "couldn't reach at this number.\n");
00849 if (outbound) {
00850 if (!outbound->cdr)
00851 outbound->cdr = ast_cdr_alloc();
00852 if (outbound->cdr) {
00853 char tmp[256];
00854
00855 ast_cdr_init(outbound->cdr, outbound);
00856 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00857 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00858 ast_cdr_update(outbound);
00859 ast_cdr_start(outbound->cdr);
00860 ast_cdr_end(outbound->cdr);
00861
00862 if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
00863 ast_cdr_failed(outbound->cdr);
00864 } else {
00865 ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00866 ast_hangup(outbound);
00867 outbound = NULL;
00868 }
00869 }
00870
00871 }
00872 } else
00873 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
00874
00875 number = rest;
00876 } while (number);
00877
00878 status = 0;
00879 if (!AST_LIST_EMPTY(findme_user_list))
00880 winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
00881
00882 AST_LIST_TRAVERSE_SAFE_BEGIN(findme_user_list, fmuser, entry) {
00883 if (!fmuser->cleared && fmuser->ochan != winner)
00884 clear_caller(fmuser);
00885 AST_LIST_REMOVE_CURRENT(findme_user_list, entry);
00886 free(fmuser);
00887 }
00888 AST_LIST_TRAVERSE_SAFE_END;
00889
00890 fmuser = NULL;
00891 tmpuser = NULL;
00892 headuser = NULL;
00893 if (winner)
00894 break;
00895
00896 if (!caller || ast_check_hangup(caller)) {
00897 tpargs->status = 1;
00898 free(findme_user_list);
00899 return;
00900 }
00901
00902 idx++;
00903 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
00904 if (nm->order == idx)
00905 break;
00906
00907 }
00908 free(findme_user_list);
00909 if (!winner)
00910 tpargs->status = 1;
00911 else {
00912 tpargs->status = 100;
00913 tpargs->outbound = winner;
00914 }
00915
00916
00917 return;
00918
00919 }
00920
00921 static void end_bridge_callback (void *data)
00922 {
00923 char buf[80];
00924 time_t end;
00925 struct ast_channel *chan = data;
00926
00927 time(&end);
00928
00929 ast_channel_lock(chan);
00930 if (chan->cdr->answer.tv_sec) {
00931 snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->answer.tv_sec);
00932 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
00933 }
00934
00935 if (chan->cdr->start.tv_sec) {
00936 snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->start.tv_sec);
00937 pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
00938 }
00939 ast_channel_unlock(chan);
00940 }
00941
00942 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
00943 {
00944 bconfig->end_bridge_callback_data = originator;
00945 }
00946
00947 static int app_exec(struct ast_channel *chan, void *data)
00948 {
00949 struct fm_args targs = { 0, };
00950 struct ast_bridge_config config;
00951 struct call_followme *f;
00952 struct number *nm, *newnm;
00953 int res = 0;
00954 struct ast_module_user *u;
00955 char *argstr;
00956 char namerecloc[255];
00957 char *fname = NULL;
00958 int duration = 0;
00959 struct ast_channel *caller;
00960 struct ast_channel *outbound;
00961
00962 AST_DECLARE_APP_ARGS(args,
00963 AST_APP_ARG(followmeid);
00964 AST_APP_ARG(options);
00965 );
00966
00967 if (ast_strlen_zero(data)) {
00968 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n",app);
00969 return -1;
00970 }
00971
00972 if (!(argstr = ast_strdupa((char *)data))) {
00973 ast_log(LOG_ERROR, "Out of memory!\n");
00974 return -1;
00975 }
00976
00977
00978 AST_STANDARD_APP_ARGS(args, argstr);
00979 if (ast_strlen_zero(args.followmeid)) {
00980 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
00981 return -1;
00982 }
00983
00984 u = ast_module_user_add(chan);
00985
00986 AST_LIST_LOCK(&followmes);
00987 AST_LIST_TRAVERSE(&followmes, f, entry) {
00988 if (!strcasecmp(f->name, args.followmeid) && (f->active))
00989 break;
00990 }
00991 AST_LIST_UNLOCK(&followmes);
00992
00993 if (option_debug)
00994 ast_log(LOG_DEBUG, "New profile %s.\n", args.followmeid);
00995 if (!f) {
00996 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
00997 res = 0;
00998 } else {
00999
01000
01001
01002 if (args.options)
01003 ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
01004
01005
01006 ast_mutex_lock(&f->lock);
01007 targs.mohclass = ast_strdupa(f->moh);
01008 ast_copy_string(targs.context, f->context, sizeof(targs.context));
01009 ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
01010 ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
01011 ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
01012 ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
01013 ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
01014 ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
01015 ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
01016 ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
01017
01018
01019 AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
01020 AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01021 newnm = create_followme_number(nm->number, "", nm->timeout, nm->order);
01022 AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
01023 }
01024 ast_mutex_unlock(&f->lock);
01025
01026 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
01027 ast_stream_and_wait(chan, targs.statusprompt, chan->language, "");
01028
01029 snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
01030 duration = 5;
01031
01032 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
01033 if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, 128, 0, NULL) < 0)
01034 goto outrun;
01035
01036 if (!ast_fileexists(namerecloc, NULL, chan->language))
01037 ast_copy_string(namerecloc, "", sizeof(namerecloc));
01038
01039 if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
01040 goto outrun;
01041 if (ast_waitstream(chan, "") < 0)
01042 goto outrun;
01043 ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01044
01045 targs.status = 0;
01046 targs.chan = chan;
01047 ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
01048
01049 findmeexec(&targs);
01050
01051 AST_LIST_TRAVERSE_SAFE_BEGIN(&targs.cnumbers, nm, entry) {
01052 AST_LIST_REMOVE_CURRENT(&targs.cnumbers, entry);
01053 free(nm);
01054 }
01055 AST_LIST_TRAVERSE_SAFE_END
01056 if (targs.status != 100) {
01057 ast_moh_stop(chan);
01058 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
01059 ast_stream_and_wait(chan, targs.sorryprompt, chan->language, "");
01060 res = 0;
01061 } else {
01062 caller = chan;
01063 outbound = targs.outbound;
01064
01065
01066 memset(&config,0,sizeof(struct ast_bridge_config));
01067 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01068 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01069 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01070
01071 config.end_bridge_callback = end_bridge_callback;
01072 config.end_bridge_callback_data = chan;
01073 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01074
01075 ast_moh_stop(caller);
01076
01077 ast_deactivate_generator(caller);
01078
01079 res = ast_channel_make_compatible(caller, outbound);
01080 if (res < 0) {
01081 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
01082 ast_hangup(outbound);
01083 goto outrun;
01084 }
01085 res = ast_bridge_call(caller,outbound,&config);
01086 if (outbound)
01087 ast_hangup(outbound);
01088 }
01089 }
01090 outrun:
01091
01092 if (!ast_strlen_zero(namerecloc)){
01093 fname = alloca(strlen(namerecloc) + 5);
01094 sprintf(fname, "%s.sln", namerecloc);
01095 unlink(fname);
01096 }
01097
01098 ast_module_user_remove(u);
01099
01100 return res;
01101 }
01102
01103 static int unload_module(void)
01104 {
01105 struct call_followme *f;
01106
01107 ast_module_user_hangup_all();
01108
01109 ast_unregister_application(app);
01110
01111
01112 AST_LIST_LOCK(&followmes);
01113 while ((f = AST_LIST_REMOVE_HEAD(&followmes, entry))) {
01114 free_numbers(f);
01115 free(f);
01116 }
01117
01118 AST_LIST_UNLOCK(&followmes);
01119
01120 return 0;
01121 }
01122
01123 static int load_module(void)
01124 {
01125 if(!reload_followme())
01126 return AST_MODULE_LOAD_DECLINE;
01127
01128 return ast_register_application(app, app_exec, synopsis, descrip);
01129 }
01130
01131 static int reload(void)
01132 {
01133 reload_followme();
01134
01135 return 0;
01136 }
01137
01138 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
01139 .load = load_module,
01140 .unload = unload_module,
01141 .reload = reload,
01142 );