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