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