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