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