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