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