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: 297689 $")
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 if (option_debug > 1)
00805 ast_log(LOG_DEBUG, "Number %s timeout %ld\n", nm->number,nm->timeout);
00806
00807 number = ast_strdupa(nm->number);
00808 if (option_debug > 2)
00809 ast_log(LOG_DEBUG, "examining %s\n", number);
00810 do {
00811 rest = strchr(number, '&');
00812 if (rest) {
00813 *rest = 0;
00814 rest++;
00815 }
00816
00817
00818 if (!ast_exists_extension(caller, tpargs->context, number, 1, caller->cid.cid_num)) {
00819
00820 ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
00821 free(findme_user_list);
00822 return;
00823 }
00824
00825 if (!strcmp(tpargs->context, ""))
00826 snprintf(dialarg, sizeof(dialarg), "%s", number);
00827 else
00828 snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
00829
00830 tmpuser = ast_calloc(1, sizeof(*tmpuser));
00831 if (!tmpuser) {
00832 free(findme_user_list);
00833 return;
00834 }
00835
00836 outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
00837 if (outbound) {
00838 ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
00839 ast_channel_inherit_variables(tpargs->chan, outbound);
00840 ast_channel_datastore_inherit(tpargs->chan, outbound);
00841 ast_string_field_set(outbound, language, tpargs->chan->language);
00842 ast_string_field_set(outbound, accountcode, tpargs->chan->accountcode);
00843 ast_string_field_set(outbound, musicclass, tpargs->chan->musicclass);
00844 if (option_verbose > 2)
00845 ast_verbose(VERBOSE_PREFIX_3 "calling %s\n", dialarg);
00846 if (!ast_call(outbound,dialarg,0)) {
00847 tmpuser->ochan = outbound;
00848 tmpuser->state = 0;
00849 tmpuser->cleared = 0;
00850 ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00851 AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00852 } else {
00853 if (option_verbose > 2)
00854 ast_verbose(VERBOSE_PREFIX_3 "couldn't reach at this number.\n");
00855 if (outbound) {
00856 if (!outbound->cdr)
00857 outbound->cdr = ast_cdr_alloc();
00858 if (outbound->cdr) {
00859 char tmp[256];
00860
00861 ast_cdr_init(outbound->cdr, outbound);
00862 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00863 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00864 ast_cdr_update(outbound);
00865 ast_cdr_start(outbound->cdr);
00866 ast_cdr_end(outbound->cdr);
00867
00868 if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
00869 ast_cdr_failed(outbound->cdr);
00870 } else {
00871 ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00872 ast_hangup(outbound);
00873 outbound = NULL;
00874 }
00875 }
00876
00877 }
00878 } else
00879 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
00880
00881 number = rest;
00882 } while (number);
00883
00884 status = 0;
00885 if (!AST_LIST_EMPTY(findme_user_list))
00886 winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
00887
00888 AST_LIST_TRAVERSE_SAFE_BEGIN(findme_user_list, fmuser, entry) {
00889 if (!fmuser->cleared && fmuser->ochan != winner)
00890 clear_caller(fmuser);
00891 AST_LIST_REMOVE_CURRENT(findme_user_list, entry);
00892 free(fmuser);
00893 }
00894 AST_LIST_TRAVERSE_SAFE_END;
00895
00896 fmuser = NULL;
00897 tmpuser = NULL;
00898 headuser = NULL;
00899 if (winner)
00900 break;
00901
00902 if (!caller || ast_check_hangup(caller)) {
00903 tpargs->status = 1;
00904 free(findme_user_list);
00905 return;
00906 }
00907
00908 idx++;
00909 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
00910 if (nm->order == idx)
00911 break;
00912
00913 }
00914 free(findme_user_list);
00915 if (!winner)
00916 tpargs->status = 1;
00917 else {
00918 tpargs->status = 100;
00919 tpargs->outbound = winner;
00920 }
00921
00922
00923 return;
00924
00925 }
00926
00927 static void end_bridge_callback (void *data)
00928 {
00929 char buf[80];
00930 time_t end;
00931 struct ast_channel *chan = data;
00932
00933 time(&end);
00934
00935 ast_channel_lock(chan);
00936 if (chan->cdr->answer.tv_sec) {
00937 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
00938 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
00939 }
00940
00941 if (chan->cdr->start.tv_sec) {
00942 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
00943 pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
00944 }
00945 ast_channel_unlock(chan);
00946 }
00947
00948 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
00949 {
00950 bconfig->end_bridge_callback_data = originator;
00951 }
00952
00953 static int app_exec(struct ast_channel *chan, void *data)
00954 {
00955 struct fm_args targs = { 0, };
00956 struct ast_bridge_config config;
00957 struct call_followme *f;
00958 struct number *nm, *newnm;
00959 int res = 0;
00960 struct ast_module_user *u;
00961 char *argstr;
00962 char namerecloc[255];
00963 char *fname = NULL;
00964 int duration = 0;
00965 struct ast_channel *caller;
00966 struct ast_channel *outbound;
00967
00968 AST_DECLARE_APP_ARGS(args,
00969 AST_APP_ARG(followmeid);
00970 AST_APP_ARG(options);
00971 );
00972
00973 if (ast_strlen_zero(data)) {
00974 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n",app);
00975 return -1;
00976 }
00977
00978 if (!(argstr = ast_strdupa((char *)data))) {
00979 ast_log(LOG_ERROR, "Out of memory!\n");
00980 return -1;
00981 }
00982
00983
00984 AST_STANDARD_APP_ARGS(args, argstr);
00985 if (ast_strlen_zero(args.followmeid)) {
00986 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
00987 return -1;
00988 }
00989
00990 u = ast_module_user_add(chan);
00991
00992 AST_LIST_LOCK(&followmes);
00993 AST_LIST_TRAVERSE(&followmes, f, entry) {
00994 if (!strcasecmp(f->name, args.followmeid) && (f->active))
00995 break;
00996 }
00997 AST_LIST_UNLOCK(&followmes);
00998
00999 if (option_debug)
01000 ast_log(LOG_DEBUG, "New profile %s.\n", args.followmeid);
01001 if (!f) {
01002 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
01003 res = 0;
01004 } else {
01005
01006
01007
01008 if (args.options)
01009 ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
01010
01011
01012 ast_mutex_lock(&f->lock);
01013 targs.mohclass = ast_strdupa(f->moh);
01014 ast_copy_string(targs.context, f->context, sizeof(targs.context));
01015 ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
01016 ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
01017 ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
01018 ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
01019 ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
01020 ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
01021 ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
01022 ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
01023
01024
01025 AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
01026 AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01027 newnm = create_followme_number(nm->number, "", nm->timeout, nm->order);
01028 AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
01029 }
01030 ast_mutex_unlock(&f->lock);
01031
01032 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
01033 ast_stream_and_wait(chan, targs.statusprompt, chan->language, "");
01034
01035 snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
01036 duration = 5;
01037
01038 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
01039 if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, 128, 0, NULL) < 0)
01040 goto outrun;
01041
01042 if (!ast_fileexists(namerecloc, NULL, chan->language))
01043 ast_copy_string(namerecloc, "", sizeof(namerecloc));
01044
01045 if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
01046 goto outrun;
01047 if (ast_waitstream(chan, "") < 0)
01048 goto outrun;
01049 ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01050
01051 targs.status = 0;
01052 targs.chan = chan;
01053 ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
01054
01055 findmeexec(&targs);
01056
01057 AST_LIST_TRAVERSE_SAFE_BEGIN(&targs.cnumbers, nm, entry) {
01058 AST_LIST_REMOVE_CURRENT(&targs.cnumbers, entry);
01059 free(nm);
01060 }
01061 AST_LIST_TRAVERSE_SAFE_END
01062 if (targs.status != 100) {
01063 ast_moh_stop(chan);
01064 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
01065 ast_stream_and_wait(chan, targs.sorryprompt, chan->language, "");
01066 res = 0;
01067 } else {
01068 caller = chan;
01069 outbound = targs.outbound;
01070
01071
01072 memset(&config,0,sizeof(struct ast_bridge_config));
01073 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01074 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01075 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01076
01077 config.end_bridge_callback = end_bridge_callback;
01078 config.end_bridge_callback_data = chan;
01079 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01080
01081 ast_moh_stop(caller);
01082
01083 ast_deactivate_generator(caller);
01084
01085 res = ast_channel_make_compatible(caller, outbound);
01086 if (res < 0) {
01087 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
01088 ast_hangup(outbound);
01089 goto outrun;
01090 }
01091 res = ast_bridge_call(caller,outbound,&config);
01092 if (outbound)
01093 ast_hangup(outbound);
01094 }
01095 }
01096 outrun:
01097
01098 if (!ast_strlen_zero(namerecloc)){
01099 fname = alloca(strlen(namerecloc) + 5);
01100 sprintf(fname, "%s.sln", namerecloc);
01101 unlink(fname);
01102 }
01103
01104 ast_module_user_remove(u);
01105
01106 return res;
01107 }
01108
01109 static int unload_module(void)
01110 {
01111 struct call_followme *f;
01112
01113 ast_module_user_hangup_all();
01114
01115 ast_unregister_application(app);
01116
01117
01118 AST_LIST_LOCK(&followmes);
01119 while ((f = AST_LIST_REMOVE_HEAD(&followmes, entry))) {
01120 free_numbers(f);
01121 free(f);
01122 }
01123
01124 AST_LIST_UNLOCK(&followmes);
01125
01126 return 0;
01127 }
01128
01129 static int load_module(void)
01130 {
01131 if(!reload_followme())
01132 return AST_MODULE_LOAD_DECLINE;
01133
01134 return ast_register_application(app, app_exec, synopsis, descrip);
01135 }
01136
01137 static int reload(void)
01138 {
01139 reload_followme();
01140
01141 return 0;
01142 }
01143
01144 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
01145 .load = load_module,
01146 .unload = unload_module,
01147 .reload = reload,
01148 );