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: 192431 $")
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/app.h"
00059
00060 static char *app = "FollowMe";
00061 static char *synopsis = "Find-Me/Follow-Me application";
00062 static char *descrip =
00063 " FollowMe(followmeid[,options]):\n"
00064 "This application performs Find-Me/Follow-Me functionality for the caller\n"
00065 "as defined in the profile matching the <followmeid> parameter in\n"
00066 "followme.conf. If the specified <followmeid> profile doesn't exist in\n"
00067 "followme.conf, execution will be returned to the dialplan and call\n"
00068 "execution will continue at the next priority.\n\n"
00069 " Options:\n"
00070 " s - Playback the incoming status message prior to starting the follow-me step(s)\n"
00071 " a - Record the caller's name so it can be announced to the callee on each step\n"
00072 " n - Playback the unreachable status message if we've run out of steps to reach the\n"
00073 " or the callee has elected not to be reachable.\n"
00074 "Returns -1 on hangup\n";
00075
00076
00077 struct number {
00078 char number[512];
00079 long timeout;
00080 int order;
00081 AST_LIST_ENTRY(number) entry;
00082 };
00083
00084
00085 struct call_followme {
00086 ast_mutex_t lock;
00087 char name[AST_MAX_EXTENSION];
00088 char moh[AST_MAX_CONTEXT];
00089 char context[AST_MAX_CONTEXT];
00090 unsigned int active;
00091 char takecall[20];
00092 char nextindp[20];
00093 char callfromprompt[PATH_MAX];
00094 char norecordingprompt[PATH_MAX];
00095 char optionsprompt[PATH_MAX];
00096 char plsholdprompt[PATH_MAX];
00097 char statusprompt[PATH_MAX];
00098 char sorryprompt[PATH_MAX];
00099
00100 AST_LIST_HEAD_NOLOCK(numbers, number) numbers;
00101 AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers;
00102 AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers;
00103 AST_LIST_ENTRY(call_followme) entry;
00104 };
00105
00106 struct fm_args {
00107 struct ast_channel *chan;
00108 char *mohclass;
00109 AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
00110 int status;
00111 char context[AST_MAX_CONTEXT];
00112 char namerecloc[AST_MAX_CONTEXT];
00113 struct ast_channel *outbound;
00114 char takecall[20];
00115 char nextindp[20];
00116 char callfromprompt[PATH_MAX];
00117 char norecordingprompt[PATH_MAX];
00118 char optionsprompt[PATH_MAX];
00119 char plsholdprompt[PATH_MAX];
00120 char statusprompt[PATH_MAX];
00121 char sorryprompt[PATH_MAX];
00122 struct ast_flags followmeflags;
00123 };
00124
00125 struct findme_user {
00126 struct ast_channel *ochan;
00127 int state;
00128 char dialarg[256];
00129 char yn[10];
00130 int ynidx;
00131 long digts;
00132 int cleared;
00133 AST_LIST_ENTRY(findme_user) entry;
00134 };
00135
00136 enum {
00137 FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00138 FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00139 FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2)
00140 };
00141
00142 AST_APP_OPTIONS(followme_opts, {
00143 AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ),
00144 AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ),
00145 AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ),
00146 });
00147
00148 static int ynlongest = 0;
00149
00150 static const char *featuredigittostr;
00151 static int featuredigittimeout = 5000;
00152 static const char *defaultmoh = "default";
00153
00154 static char takecall[20] = "1", nextindp[20] = "2";
00155 static char callfromprompt[PATH_MAX] = "followme/call-from";
00156 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
00157 static char optionsprompt[PATH_MAX] = "followme/options";
00158 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
00159 static char statusprompt[PATH_MAX] = "followme/status";
00160 static char sorryprompt[PATH_MAX] = "followme/sorry";
00161
00162
00163 static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
00164 AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
00165
00166 static void free_numbers(struct call_followme *f)
00167 {
00168
00169 struct number *prev;
00170
00171 while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00172
00173 ast_free(prev);
00174 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00175
00176 while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00177
00178 ast_free(prev);
00179 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00180
00181 while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00182
00183 ast_free(prev);
00184 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00185
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
00262 if (!(cur = ast_calloc(1, sizeof(*cur))))
00263 return NULL;
00264
00265 cur->timeout = timeout;
00266 if ((tmp = strchr(number, ',')))
00267 *tmp = '\0';
00268 ast_copy_string(cur->number, number, sizeof(cur->number));
00269 cur->order = numorder;
00270 ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00271
00272 return cur;
00273 }
00274
00275
00276 static int reload_followme(int reload)
00277 {
00278 struct call_followme *f;
00279 struct ast_config *cfg;
00280 char *cat = NULL, *tmp;
00281 struct ast_variable *var;
00282 struct number *cur, *nm;
00283 char numberstr[90];
00284 int timeout;
00285 char *timeoutstr;
00286 int numorder;
00287 const char *takecallstr;
00288 const char *declinecallstr;
00289 const char *tmpstr;
00290 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00291
00292 if (!(cfg = ast_config_load("followme.conf", config_flags))) {
00293 ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00294 return 0;
00295 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00296 return 0;
00297
00298 AST_RWLIST_WRLOCK(&followmes);
00299
00300
00301 featuredigittimeout = 5000;
00302
00303
00304 AST_RWLIST_TRAVERSE(&followmes, f, entry) {
00305 f->active = 0;
00306 }
00307
00308 featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00309
00310 if (!ast_strlen_zero(featuredigittostr)) {
00311 if (!sscanf(featuredigittostr, "%d", &featuredigittimeout))
00312 featuredigittimeout = 5000;
00313 }
00314
00315 takecallstr = ast_variable_retrieve(cfg, "general", "takecall");
00316 if (!ast_strlen_zero(takecallstr))
00317 ast_copy_string(takecall, takecallstr, sizeof(takecall));
00318
00319 declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall");
00320 if (!ast_strlen_zero(declinecallstr))
00321 ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00322
00323 tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt");
00324 if (!ast_strlen_zero(tmpstr))
00325 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00326
00327 tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt");
00328 if (!ast_strlen_zero(tmpstr))
00329 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00330
00331 tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt");
00332 if (!ast_strlen_zero(tmpstr))
00333 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00334
00335 tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt");
00336 if (!ast_strlen_zero(tmpstr))
00337 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00338
00339 tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt");
00340 if (!ast_strlen_zero(tmpstr))
00341 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00342
00343 tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt");
00344 if (!ast_strlen_zero(tmpstr))
00345 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00346
00347
00348 while ((cat = ast_category_browse(cfg, cat))) {
00349 int new = 0;
00350
00351 if (!strcasecmp(cat, "general"))
00352 continue;
00353
00354
00355 AST_LIST_TRAVERSE(&followmes, f, entry) {
00356 if (!strcasecmp(f->name, cat))
00357 break;
00358 }
00359
00360 ast_debug(1, "New profile %s.\n", cat);
00361
00362 if (!f) {
00363
00364 f = alloc_profile(cat);
00365 new = 1;
00366 }
00367
00368
00369 if (!f)
00370 continue;
00371
00372 if (!new)
00373 ast_mutex_lock(&f->lock);
00374
00375 init_profile(f);
00376 free_numbers(f);
00377 var = ast_variable_browse(cfg, cat);
00378 while(var) {
00379 if (!strcasecmp(var->name, "number")) {
00380 int idx = 0;
00381
00382
00383 ast_copy_string(numberstr, var->value, sizeof(numberstr));
00384 if ((tmp = strchr(numberstr, ','))) {
00385 *tmp++ = '\0';
00386 timeoutstr = ast_strdupa(tmp);
00387 if ((tmp = strchr(timeoutstr, ','))) {
00388 *tmp++ = '\0';
00389 numorder = atoi(tmp);
00390 if (numorder < 0)
00391 numorder = 0;
00392 } else
00393 numorder = 0;
00394 timeout = atoi(timeoutstr);
00395 if (timeout < 0)
00396 timeout = 25;
00397 } else {
00398 timeout = 25;
00399 numorder = 0;
00400 }
00401
00402 if (!numorder) {
00403 idx = 1;
00404 AST_LIST_TRAVERSE(&f->numbers, nm, entry)
00405 idx++;
00406 numorder = idx;
00407 }
00408 cur = create_followme_number(numberstr, timeout, numorder);
00409 AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00410 } else {
00411 profile_set_param(f, var->name, var->value, var->lineno, 1);
00412 ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00413 }
00414 var = var->next;
00415 }
00416
00417 if (!new)
00418 ast_mutex_unlock(&f->lock);
00419 else
00420 AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
00421 }
00422
00423 ast_config_destroy(cfg);
00424
00425 AST_RWLIST_UNLOCK(&followmes);
00426
00427 return 1;
00428 }
00429
00430 static void clear_caller(struct findme_user *tmpuser)
00431 {
00432 struct ast_channel *outbound;
00433
00434 if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00435 outbound = tmpuser->ochan;
00436 if (!outbound->cdr) {
00437 outbound->cdr = ast_cdr_alloc();
00438 if (outbound->cdr)
00439 ast_cdr_init(outbound->cdr, outbound);
00440 }
00441 if (outbound->cdr) {
00442 char tmp[256];
00443
00444 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00445 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00446 ast_cdr_update(outbound);
00447 ast_cdr_start(outbound->cdr);
00448 ast_cdr_end(outbound->cdr);
00449
00450 if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
00451 ast_cdr_failed(outbound->cdr);
00452 } else
00453 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00454 ast_hangup(tmpuser->ochan);
00455 }
00456
00457 }
00458
00459 static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
00460 {
00461 struct findme_user *tmpuser;
00462
00463 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00464 clear_caller(tmpuser);
00465 tmpuser->cleared = 1;
00466 }
00467
00468 }
00469
00470
00471
00472 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)
00473 {
00474 struct ast_channel *watchers[256];
00475 int pos;
00476 struct ast_channel *winner;
00477 struct ast_frame *f;
00478 int ctstatus = 0;
00479 int dg;
00480 struct findme_user *tmpuser;
00481 int to = 0;
00482 int livechannels = 0;
00483 int tmpto;
00484 long totalwait = 0, wtd = 0, towas = 0;
00485 char *callfromname;
00486 char *pressbuttonname;
00487
00488
00489
00490 callfromname = ast_strdupa(tpargs->callfromprompt);
00491 pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00492
00493 if (AST_LIST_EMPTY(findme_user_list)) {
00494 ast_verb(3, "couldn't reach at this number.\n");
00495 return NULL;
00496 }
00497
00498 if (!caller) {
00499 ast_verb(3, "Original caller hungup. Cleanup.\n");
00500 clear_calling_tree(findme_user_list);
00501 return NULL;
00502 }
00503
00504 totalwait = nm->timeout * 1000;
00505
00506 while (!ctstatus) {
00507 to = 1000;
00508 pos = 1;
00509 livechannels = 0;
00510 watchers[0] = caller;
00511
00512 dg = 0;
00513 winner = NULL;
00514 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00515 if (tmpuser->state >= 0 && tmpuser->ochan) {
00516 if (tmpuser->state == 3)
00517 tmpuser->digts += (towas - wtd);
00518 if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00519 ast_verb(3, "We've been waiting for digits longer than we should have.\n");
00520 if (!ast_strlen_zero(namerecloc)) {
00521 tmpuser->state = 1;
00522 tmpuser->digts = 0;
00523 if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
00524 ast_sched_runq(tmpuser->ochan->sched);
00525 } else {
00526 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00527 return NULL;
00528 }
00529 } else {
00530 tmpuser->state = 2;
00531 tmpuser->digts = 0;
00532 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00533 ast_sched_runq(tmpuser->ochan->sched);
00534 else {
00535 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00536 return NULL;
00537 }
00538 }
00539 }
00540 if (tmpuser->ochan->stream) {
00541 ast_sched_runq(tmpuser->ochan->sched);
00542 tmpto = ast_sched_wait(tmpuser->ochan->sched);
00543 if (tmpto > 0 && tmpto < to)
00544 to = tmpto;
00545 else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
00546 ast_stopstream(tmpuser->ochan);
00547 if (tmpuser->state == 1) {
00548 ast_verb(3, "Playback of the call-from file appears to be done.\n");
00549 if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
00550 tmpuser->state = 2;
00551 } else {
00552 ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
00553 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00554 tmpuser->ynidx = 0;
00555 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
00556 tmpuser->state = 3;
00557 else {
00558 ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00559 return NULL;
00560 }
00561 }
00562 } else if (tmpuser->state == 2) {
00563 ast_verb(3, "Playback of name file appears to be done.\n");
00564 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00565 tmpuser->ynidx = 0;
00566 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
00567 tmpuser->state = 3;
00568
00569 } else {
00570 return NULL;
00571 }
00572 } else if (tmpuser->state == 3) {
00573 ast_verb(3, "Playback of the next step file appears to be done.\n");
00574 tmpuser->digts = 0;
00575 }
00576 }
00577 }
00578 watchers[pos++] = tmpuser->ochan;
00579 livechannels++;
00580 }
00581 }
00582
00583 tmpto = to;
00584 if (to < 0) {
00585 to = 1000;
00586 tmpto = 1000;
00587 }
00588 towas = to;
00589 winner = ast_waitfor_n(watchers, pos, &to);
00590 tmpto -= to;
00591 totalwait -= tmpto;
00592 wtd = to;
00593 if (totalwait <= 0) {
00594 ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
00595 clear_calling_tree(findme_user_list);
00596 return NULL;
00597 }
00598 if (winner) {
00599
00600 dg = 0;
00601 while ((winner != watchers[dg]) && (dg < 256))
00602 dg++;
00603 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
00604 if (tmpuser->ochan == winner)
00605 break;
00606 f = ast_read(winner);
00607 if (f) {
00608 if (f->frametype == AST_FRAME_CONTROL) {
00609 switch(f->subclass) {
00610 case AST_CONTROL_HANGUP:
00611 ast_verb(3, "%s received a hangup frame.\n", winner->name);
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_verb(3, "calling %s\n", dialarg);
00799 if (!ast_call(outbound,dialarg,0)) {
00800 tmpuser->ochan = outbound;
00801 tmpuser->state = 0;
00802 tmpuser->cleared = 0;
00803 ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00804 AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00805 } else {
00806 ast_verb(3, "couldn't reach at this number.\n");
00807 if (outbound) {
00808 if (!outbound->cdr)
00809 outbound->cdr = ast_cdr_alloc();
00810 if (outbound->cdr) {
00811 char tmp[256];
00812
00813 ast_cdr_init(outbound->cdr, outbound);
00814 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00815 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00816 ast_cdr_update(outbound);
00817 ast_cdr_start(outbound->cdr);
00818 ast_cdr_end(outbound->cdr);
00819
00820 if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
00821 ast_cdr_failed(outbound->cdr);
00822 } else {
00823 ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00824 ast_hangup(outbound);
00825 outbound = NULL;
00826 }
00827 }
00828
00829 }
00830 } else
00831 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
00832
00833 number = rest;
00834 } while (number);
00835
00836 status = 0;
00837 if (!AST_LIST_EMPTY(findme_user_list))
00838 winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
00839
00840
00841 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00842 if (!fmuser->cleared && fmuser->ochan != winner)
00843 clear_caller(fmuser);
00844 ast_free(fmuser);
00845 }
00846
00847 fmuser = NULL;
00848 tmpuser = NULL;
00849 headuser = NULL;
00850 if (winner)
00851 break;
00852
00853 if (!caller || ast_check_hangup(caller)) {
00854 tpargs->status = 1;
00855 ast_free(findme_user_list);
00856 return;
00857 }
00858
00859 idx++;
00860 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
00861 if (nm->order == idx)
00862 break;
00863
00864 }
00865 ast_free(findme_user_list);
00866 if (!winner)
00867 tpargs->status = 1;
00868 else {
00869 tpargs->status = 100;
00870 tpargs->outbound = winner;
00871 }
00872
00873
00874 return;
00875
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", 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", 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
00917 AST_DECLARE_APP_ARGS(args,
00918 AST_APP_ARG(followmeid);
00919 AST_APP_ARG(options);
00920 );
00921
00922 if (ast_strlen_zero(data)) {
00923 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
00924 return -1;
00925 }
00926
00927 if (!(argstr = ast_strdupa((char *)data))) {
00928 ast_log(LOG_ERROR, "Out of memory!\n");
00929 return -1;
00930 }
00931
00932 AST_STANDARD_APP_ARGS(args, argstr);
00933
00934 if (ast_strlen_zero(args.followmeid)) {
00935 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
00936 return -1;
00937 }
00938
00939 AST_RWLIST_RDLOCK(&followmes);
00940 AST_RWLIST_TRAVERSE(&followmes, f, entry) {
00941 if (!strcasecmp(f->name, args.followmeid) && (f->active))
00942 break;
00943 }
00944 AST_RWLIST_UNLOCK(&followmes);
00945
00946 ast_debug(1, "New profile %s.\n", args.followmeid);
00947
00948 if (!f) {
00949 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
00950 return 0;
00951 }
00952
00953
00954 if (args.options)
00955 ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
00956
00957
00958 ast_mutex_lock(&f->lock);
00959 targs.mohclass = ast_strdupa(f->moh);
00960 ast_copy_string(targs.context, f->context, sizeof(targs.context));
00961 ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
00962 ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
00963 ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
00964 ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
00965 ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
00966 ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
00967 ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
00968 ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
00969
00970
00971 AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
00972 AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
00973 newnm = create_followme_number(nm->number, nm->timeout, nm->order);
00974 AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
00975 }
00976 ast_mutex_unlock(&f->lock);
00977
00978
00979 if (chan->_state != AST_STATE_UP) {
00980 ast_answer(chan);
00981 }
00982
00983 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
00984 ast_stream_and_wait(chan, targs.statusprompt, "");
00985
00986 snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
00987 duration = 5;
00988
00989 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
00990 if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, 128, 0, NULL) < 0)
00991 goto outrun;
00992
00993 if (!ast_fileexists(namerecloc, NULL, chan->language))
00994 ast_copy_string(namerecloc, "", sizeof(namerecloc));
00995
00996 if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
00997 goto outrun;
00998 if (ast_waitstream(chan, "") < 0)
00999 goto outrun;
01000 ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01001
01002 targs.status = 0;
01003 targs.chan = chan;
01004 ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
01005
01006 findmeexec(&targs);
01007
01008 while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
01009 ast_free(nm);
01010
01011 if (!ast_strlen_zero(namerecloc))
01012 unlink(namerecloc);
01013
01014 if (targs.status != 100) {
01015 ast_moh_stop(chan);
01016 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
01017 ast_stream_and_wait(chan, targs.sorryprompt, "");
01018 res = 0;
01019 } else {
01020 caller = chan;
01021 outbound = targs.outbound;
01022
01023
01024 memset(&config,0,sizeof(struct ast_bridge_config));
01025 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01026 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01027 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01028 config.end_bridge_callback = end_bridge_callback;
01029 config.end_bridge_callback_data = chan;
01030 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01031
01032 ast_moh_stop(caller);
01033
01034 ast_deactivate_generator(caller);
01035
01036 res = ast_channel_make_compatible(caller, outbound);
01037 if (res < 0) {
01038 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
01039 ast_hangup(outbound);
01040 goto outrun;
01041 }
01042 res = ast_bridge_call(caller,outbound,&config);
01043 if (outbound)
01044 ast_hangup(outbound);
01045 }
01046
01047 outrun:
01048
01049 return res;
01050 }
01051
01052 static int unload_module(void)
01053 {
01054 struct call_followme *f;
01055
01056 ast_unregister_application(app);
01057
01058
01059 AST_RWLIST_WRLOCK(&followmes);
01060 while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01061 free_numbers(f);
01062 ast_free(f);
01063 }
01064
01065 AST_RWLIST_UNLOCK(&followmes);
01066
01067 return 0;
01068 }
01069
01070 static int load_module(void)
01071 {
01072 if(!reload_followme(0))
01073 return AST_MODULE_LOAD_DECLINE;
01074
01075 return ast_register_application(app, app_exec, synopsis, descrip);
01076 }
01077
01078 static int reload(void)
01079 {
01080 reload_followme(1);
01081
01082 return 0;
01083 }
01084
01085 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
01086 .load = load_module,
01087 .unload = unload_module,
01088 .reload = reload,
01089 );