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