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