app_followme.c 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * A full-featured Find-Me/Follow-Me Application
  5. *
  6. * Copyright (C) 2005-2006, BJ Weschke All Rights Reserved.
  7. *
  8. * BJ Weschke <bweschke@btwtech.com>
  9. *
  10. * This code is released by the author with no restrictions on usage.
  11. *
  12. * See http://www.asterisk.org for more information about
  13. * the Asterisk project. Please do not directly contact
  14. * any of the maintainers of this project for assistance;
  15. * the project provides a web site, mailing lists and IRC
  16. * channels for your use.
  17. *
  18. */
  19. /*! \file
  20. *
  21. * \brief Find-Me Follow-Me application
  22. *
  23. * \author BJ Weschke <bweschke@btwtech.com>
  24. *
  25. * \arg See \ref Config_followme
  26. *
  27. * \ingroup applications
  28. */
  29. /*** MODULEINFO
  30. <depend>chan_local</depend>
  31. <support_level>core</support_level>
  32. ***/
  33. #include "asterisk.h"
  34. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  35. #include <signal.h>
  36. #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
  37. #include "asterisk/lock.h"
  38. #include "asterisk/file.h"
  39. #include "asterisk/channel.h"
  40. #include "asterisk/pbx.h"
  41. #include "asterisk/module.h"
  42. #include "asterisk/translate.h"
  43. #include "asterisk/say.h"
  44. #include "asterisk/features.h"
  45. #include "asterisk/musiconhold.h"
  46. #include "asterisk/cli.h"
  47. #include "asterisk/manager.h"
  48. #include "asterisk/config.h"
  49. #include "asterisk/utils.h"
  50. #include "asterisk/causes.h"
  51. #include "asterisk/astdb.h"
  52. #include "asterisk/dsp.h"
  53. #include "asterisk/app.h"
  54. /*** DOCUMENTATION
  55. <application name="FollowMe" language="en_US">
  56. <synopsis>
  57. Find-Me/Follow-Me application.
  58. </synopsis>
  59. <syntax>
  60. <parameter name="followmeid" required="true" />
  61. <parameter name="options">
  62. <optionlist>
  63. <option name="s">
  64. <para>Playback the incoming status message prior to starting
  65. the follow-me step(s)</para>
  66. </option>
  67. <option name="a">
  68. <para>Record the caller's name so it can be announced to the
  69. callee on each step.</para>
  70. </option>
  71. <option name="n">
  72. <para>Playback the unreachable status message if we've run out
  73. of steps to reach the or the callee has elected not to be reachable.</para>
  74. </option>
  75. <option name="N">
  76. <para>Don't answer the incoming call until we're ready to
  77. connect the caller or give up. This will disable all the other
  78. options while implicitly turning on the 'd' option.</para>
  79. </option>
  80. <option name="d">
  81. <para>Disable the 'Please hold while we try to connect your call' announcement.</para>
  82. </option>
  83. <option name="l">
  84. <para>Disable local call optimization so that applications with
  85. audio hooks between the local bridge don't get dropped when the
  86. calls get joined directly.</para>
  87. </option>
  88. </optionlist>
  89. </parameter>
  90. </syntax>
  91. <description>
  92. <para>This application performs Find-Me/Follow-Me functionality for the caller
  93. as defined in the profile matching the <replaceable>followmeid</replaceable> parameter in
  94. <filename>followme.conf</filename>. If the specified <replaceable>followmeid</replaceable>
  95. profile doesn't exist in <filename>followme.conf</filename>, execution will be returned
  96. to the dialplan and call execution will continue at the next priority.</para>
  97. <para>Returns -1 on hangup.</para>
  98. </description>
  99. </application>
  100. ***/
  101. static char *app = "FollowMe";
  102. /*! \brief Number structure */
  103. struct number {
  104. char number[512]; /*!< Phone Number(s) and/or Extension(s) */
  105. long timeout; /*!< Dial Timeout, if used. */
  106. int order; /*!< The order to dial in */
  107. AST_LIST_ENTRY(number) entry; /*!< Next Number record */
  108. };
  109. /*! \brief Data structure for followme scripts */
  110. struct call_followme {
  111. ast_mutex_t lock;
  112. char name[AST_MAX_EXTENSION]; /*!< Name - FollowMeID */
  113. char moh[AST_MAX_CONTEXT]; /*!< Music On Hold Class to be used */
  114. char context[AST_MAX_CONTEXT]; /*!< Context to dial from */
  115. unsigned int active; /*!< Profile is active (1), or disabled (0). */
  116. int realtime; /*!< Cached from realtime */
  117. char takecall[20]; /*!< Digit mapping to take a call */
  118. char nextindp[20]; /*!< Digit mapping to decline a call */
  119. char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
  120. char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
  121. char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
  122. char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
  123. char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
  124. char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
  125. AST_LIST_HEAD_NOLOCK(numbers, number) numbers; /*!< Head of the list of follow-me numbers */
  126. AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
  127. AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
  128. AST_LIST_ENTRY(call_followme) entry; /*!< Next Follow-Me record */
  129. };
  130. struct fm_args {
  131. struct ast_channel *chan;
  132. char *mohclass;
  133. AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
  134. int status;
  135. char context[AST_MAX_CONTEXT];
  136. char namerecloc[AST_MAX_CONTEXT];
  137. struct ast_channel *outbound;
  138. char takecall[20]; /*!< Digit mapping to take a call */
  139. char nextindp[20]; /*!< Digit mapping to decline a call */
  140. char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
  141. char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
  142. char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
  143. char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
  144. char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
  145. char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
  146. struct ast_flags followmeflags;
  147. };
  148. struct findme_user {
  149. struct ast_channel *ochan;
  150. int state;
  151. char dialarg[256];
  152. char yn[10];
  153. int ynidx;
  154. long digts;
  155. int cleared;
  156. AST_LIST_ENTRY(findme_user) entry;
  157. };
  158. enum {
  159. FOLLOWMEFLAG_STATUSMSG = (1 << 0),
  160. FOLLOWMEFLAG_RECORDNAME = (1 << 1),
  161. FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
  162. FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
  163. FOLLOWMEFLAG_NOANSWER = (1 << 4),
  164. FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5),
  165. };
  166. AST_APP_OPTIONS(followme_opts, {
  167. AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ),
  168. AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ),
  169. AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ),
  170. AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT ),
  171. AST_APP_OPTION('N', FOLLOWMEFLAG_NOANSWER ),
  172. AST_APP_OPTION('l', FOLLOWMEFLAG_DISABLEOPTIMIZATION ),
  173. });
  174. static int ynlongest = 0;
  175. static const char *featuredigittostr;
  176. static int featuredigittimeout = 5000; /*!< Feature Digit Timeout */
  177. static const char *defaultmoh = "default"; /*!< Default Music-On-Hold Class */
  178. static char takecall[20] = "1", nextindp[20] = "2";
  179. static char callfromprompt[PATH_MAX] = "followme/call-from";
  180. static char norecordingprompt[PATH_MAX] = "followme/no-recording";
  181. static char optionsprompt[PATH_MAX] = "followme/options";
  182. static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
  183. static char statusprompt[PATH_MAX] = "followme/status";
  184. static char sorryprompt[PATH_MAX] = "followme/sorry";
  185. static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
  186. AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
  187. static void free_numbers(struct call_followme *f)
  188. {
  189. /* Free numbers attached to the profile */
  190. struct number *prev;
  191. while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
  192. /* Free the number */
  193. ast_free(prev);
  194. AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
  195. while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
  196. /* Free the blacklisted number */
  197. ast_free(prev);
  198. AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
  199. while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
  200. /* Free the whitelisted number */
  201. ast_free(prev);
  202. AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
  203. }
  204. /*! \brief Allocate and initialize followme profile */
  205. static struct call_followme *alloc_profile(const char *fmname)
  206. {
  207. struct call_followme *f;
  208. if (!(f = ast_calloc(1, sizeof(*f))))
  209. return NULL;
  210. ast_mutex_init(&f->lock);
  211. ast_copy_string(f->name, fmname, sizeof(f->name));
  212. f->moh[0] = '\0';
  213. f->context[0] = '\0';
  214. ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
  215. ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
  216. ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
  217. ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
  218. ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
  219. ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
  220. ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
  221. ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
  222. AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
  223. AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
  224. AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
  225. return f;
  226. }
  227. static void init_profile(struct call_followme *f)
  228. {
  229. f->active = 1;
  230. ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
  231. }
  232. /*! \brief Set parameter in profile from configuration file */
  233. static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
  234. {
  235. if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
  236. ast_copy_string(f->moh, val, sizeof(f->moh));
  237. else if (!strcasecmp(param, "context"))
  238. ast_copy_string(f->context, val, sizeof(f->context));
  239. else if (!strcasecmp(param, "takecall"))
  240. ast_copy_string(f->takecall, val, sizeof(f->takecall));
  241. else if (!strcasecmp(param, "declinecall"))
  242. ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
  243. else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
  244. ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
  245. else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))
  246. ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
  247. else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))
  248. ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
  249. else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
  250. ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
  251. else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))
  252. ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
  253. else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))
  254. ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
  255. else if (failunknown) {
  256. if (linenum >= 0)
  257. ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
  258. else
  259. ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
  260. }
  261. }
  262. /*! \brief Add a new number */
  263. static struct number *create_followme_number(const char *number, int timeout, int numorder)
  264. {
  265. struct number *cur;
  266. char *buf = ast_strdupa(number);
  267. char *tmp;
  268. if (!(cur = ast_calloc(1, sizeof(*cur))))
  269. return NULL;
  270. cur->timeout = timeout;
  271. if ((tmp = strchr(buf, ',')))
  272. *tmp = '\0';
  273. ast_copy_string(cur->number, buf, sizeof(cur->number));
  274. cur->order = numorder;
  275. ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
  276. return cur;
  277. }
  278. /*! \brief Reload followme application module */
  279. static int reload_followme(int reload)
  280. {
  281. struct call_followme *f;
  282. struct ast_config *cfg;
  283. char *cat = NULL, *tmp;
  284. struct ast_variable *var;
  285. struct number *cur, *nm;
  286. char numberstr[90];
  287. int timeout;
  288. char *timeoutstr;
  289. int numorder;
  290. const char *takecallstr;
  291. const char *declinecallstr;
  292. const char *tmpstr;
  293. struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
  294. if (!(cfg = ast_config_load("followme.conf", config_flags))) {
  295. ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
  296. return 0;
  297. } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
  298. return 0;
  299. } else if (cfg == CONFIG_STATUS_FILEINVALID) {
  300. ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format. Aborting.\n");
  301. return 0;
  302. }
  303. AST_RWLIST_WRLOCK(&followmes);
  304. /* Reset Global Var Values */
  305. featuredigittimeout = 5000;
  306. /* Mark all profiles as inactive for the moment */
  307. AST_RWLIST_TRAVERSE(&followmes, f, entry) {
  308. f->active = 0;
  309. }
  310. featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
  311. if (!ast_strlen_zero(featuredigittostr)) {
  312. if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
  313. featuredigittimeout = 5000;
  314. }
  315. if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
  316. ast_copy_string(takecall, takecallstr, sizeof(takecall));
  317. }
  318. if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
  319. ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
  320. }
  321. if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
  322. ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
  323. } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
  324. ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
  325. }
  326. if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
  327. ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
  328. } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
  329. ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
  330. }
  331. if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
  332. ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
  333. } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
  334. ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
  335. }
  336. if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
  337. ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
  338. } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
  339. ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
  340. }
  341. if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
  342. ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
  343. } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
  344. ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
  345. }
  346. if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
  347. ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
  348. } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
  349. ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
  350. }
  351. /* Chug through config file */
  352. while ((cat = ast_category_browse(cfg, cat))) {
  353. int new = 0;
  354. if (!strcasecmp(cat, "general"))
  355. continue;
  356. /* Look for an existing one */
  357. AST_LIST_TRAVERSE(&followmes, f, entry) {
  358. if (!strcasecmp(f->name, cat))
  359. break;
  360. }
  361. ast_debug(1, "New profile %s.\n", cat);
  362. if (!f) {
  363. /* Make one then */
  364. f = alloc_profile(cat);
  365. new = 1;
  366. }
  367. /* Totally fail if we fail to find/create an entry */
  368. if (!f)
  369. continue;
  370. if (!new)
  371. ast_mutex_lock(&f->lock);
  372. /* Re-initialize the profile */
  373. init_profile(f);
  374. free_numbers(f);
  375. var = ast_variable_browse(cfg, cat);
  376. while (var) {
  377. if (!strcasecmp(var->name, "number")) {
  378. int idx = 0;
  379. /* Add a new number */
  380. ast_copy_string(numberstr, var->value, sizeof(numberstr));
  381. if ((tmp = strchr(numberstr, ','))) {
  382. *tmp++ = '\0';
  383. timeoutstr = ast_strdupa(tmp);
  384. if ((tmp = strchr(timeoutstr, ','))) {
  385. *tmp++ = '\0';
  386. numorder = atoi(tmp);
  387. if (numorder < 0)
  388. numorder = 0;
  389. } else
  390. numorder = 0;
  391. timeout = atoi(timeoutstr);
  392. if (timeout < 0)
  393. timeout = 25;
  394. } else {
  395. timeout = 25;
  396. numorder = 0;
  397. }
  398. if (!numorder) {
  399. idx = 1;
  400. AST_LIST_TRAVERSE(&f->numbers, nm, entry)
  401. idx++;
  402. numorder = idx;
  403. }
  404. cur = create_followme_number(numberstr, timeout, numorder);
  405. AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
  406. } else {
  407. profile_set_param(f, var->name, var->value, var->lineno, 1);
  408. ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
  409. }
  410. var = var->next;
  411. } /* End while(var) loop */
  412. if (!new)
  413. ast_mutex_unlock(&f->lock);
  414. else
  415. AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
  416. }
  417. ast_config_destroy(cfg);
  418. AST_RWLIST_UNLOCK(&followmes);
  419. return 1;
  420. }
  421. static void clear_caller(struct findme_user *tmpuser)
  422. {
  423. struct ast_channel *outbound;
  424. if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
  425. outbound = tmpuser->ochan;
  426. if (!outbound->cdr) {
  427. outbound->cdr = ast_cdr_alloc();
  428. if (outbound->cdr)
  429. ast_cdr_init(outbound->cdr, outbound);
  430. }
  431. if (outbound->cdr) {
  432. char tmp[256];
  433. snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
  434. ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
  435. ast_cdr_update(outbound);
  436. ast_cdr_start(outbound->cdr);
  437. ast_cdr_end(outbound->cdr);
  438. /* If the cause wasn't handled properly */
  439. if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
  440. ast_cdr_failed(outbound->cdr);
  441. } else
  442. ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
  443. ast_hangup(tmpuser->ochan);
  444. }
  445. }
  446. static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
  447. {
  448. struct findme_user *tmpuser;
  449. AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
  450. clear_caller(tmpuser);
  451. tmpuser->cleared = 1;
  452. }
  453. }
  454. 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)
  455. {
  456. struct ast_channel *watchers[256];
  457. int pos;
  458. struct ast_channel *winner;
  459. struct ast_frame *f;
  460. int ctstatus = 0;
  461. int dg;
  462. struct findme_user *tmpuser;
  463. int to = 0;
  464. int livechannels = 0;
  465. int tmpto;
  466. long totalwait = 0, wtd = 0, towas = 0;
  467. char *callfromname;
  468. char *pressbuttonname;
  469. /* ------------ wait_for_winner_channel start --------------- */
  470. callfromname = ast_strdupa(tpargs->callfromprompt);
  471. pressbuttonname = ast_strdupa(tpargs->optionsprompt);
  472. if (AST_LIST_EMPTY(findme_user_list)) {
  473. ast_verb(3, "couldn't reach at this number.\n");
  474. return NULL;
  475. }
  476. if (!caller) {
  477. ast_verb(3, "Original caller hungup. Cleanup.\n");
  478. clear_calling_tree(findme_user_list);
  479. return NULL;
  480. }
  481. totalwait = nm->timeout * 1000;
  482. while (!ctstatus) {
  483. to = 1000;
  484. pos = 1;
  485. livechannels = 0;
  486. watchers[0] = caller;
  487. dg = 0;
  488. winner = NULL;
  489. AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
  490. if (tmpuser->state >= 0 && tmpuser->ochan) {
  491. if (tmpuser->state == 3)
  492. tmpuser->digts += (towas - wtd);
  493. if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
  494. ast_verb(3, "We've been waiting for digits longer than we should have.\n");
  495. if (!ast_strlen_zero(namerecloc)) {
  496. tmpuser->state = 1;
  497. tmpuser->digts = 0;
  498. if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
  499. ast_sched_runq(tmpuser->ochan->sched);
  500. } else {
  501. ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
  502. return NULL;
  503. }
  504. } else {
  505. tmpuser->state = 2;
  506. tmpuser->digts = 0;
  507. if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
  508. ast_sched_runq(tmpuser->ochan->sched);
  509. else {
  510. ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
  511. return NULL;
  512. }
  513. }
  514. }
  515. if (tmpuser->ochan->stream) {
  516. ast_sched_runq(tmpuser->ochan->sched);
  517. tmpto = ast_sched_wait(tmpuser->ochan->sched);
  518. if (tmpto > 0 && tmpto < to)
  519. to = tmpto;
  520. else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
  521. ast_stopstream(tmpuser->ochan);
  522. if (tmpuser->state == 1) {
  523. ast_verb(3, "Playback of the call-from file appears to be done.\n");
  524. if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
  525. tmpuser->state = 2;
  526. } else {
  527. ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
  528. memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
  529. tmpuser->ynidx = 0;
  530. if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
  531. tmpuser->state = 3;
  532. else {
  533. ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
  534. return NULL;
  535. }
  536. }
  537. } else if (tmpuser->state == 2) {
  538. ast_verb(3, "Playback of name file appears to be done.\n");
  539. memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
  540. tmpuser->ynidx = 0;
  541. if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
  542. tmpuser->state = 3;
  543. } else {
  544. return NULL;
  545. }
  546. } else if (tmpuser->state == 3) {
  547. ast_verb(3, "Playback of the next step file appears to be done.\n");
  548. tmpuser->digts = 0;
  549. }
  550. }
  551. }
  552. watchers[pos++] = tmpuser->ochan;
  553. livechannels++;
  554. }
  555. }
  556. tmpto = to;
  557. if (to < 0) {
  558. to = 1000;
  559. tmpto = 1000;
  560. }
  561. towas = to;
  562. winner = ast_waitfor_n(watchers, pos, &to);
  563. tmpto -= to;
  564. totalwait -= tmpto;
  565. wtd = to;
  566. if (totalwait <= 0) {
  567. ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
  568. clear_calling_tree(findme_user_list);
  569. return NULL;
  570. }
  571. if (winner) {
  572. /* Need to find out which channel this is */
  573. dg = 0;
  574. while ((winner != watchers[dg]) && (dg < 256))
  575. dg++;
  576. AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
  577. if (tmpuser->ochan == winner)
  578. break;
  579. f = ast_read(winner);
  580. if (f) {
  581. if (f->frametype == AST_FRAME_CONTROL) {
  582. switch (f->subclass.integer) {
  583. case AST_CONTROL_HANGUP:
  584. ast_verb(3, "%s received a hangup frame.\n", winner->name);
  585. if (f->data.uint32) {
  586. winner->hangupcause = f->data.uint32;
  587. }
  588. if (dg == 0) {
  589. ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
  590. clear_calling_tree(findme_user_list);
  591. ctstatus = -1;
  592. }
  593. break;
  594. case AST_CONTROL_ANSWER:
  595. ast_verb(3, "%s answered %s\n", winner->name, caller->name);
  596. /* If call has been answered, then the eventual hangup is likely to be normal hangup */
  597. winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
  598. caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
  599. ast_verb(3, "Starting playback of %s\n", callfromname);
  600. if (dg > 0) {
  601. if (!ast_strlen_zero(namerecloc)) {
  602. if (!ast_streamfile(winner, callfromname, winner->language)) {
  603. ast_sched_runq(winner->sched);
  604. tmpuser->state = 1;
  605. } else {
  606. ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
  607. ast_frfree(f);
  608. return NULL;
  609. }
  610. } else {
  611. tmpuser->state = 2;
  612. if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
  613. ast_sched_runq(tmpuser->ochan->sched);
  614. else {
  615. ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
  616. ast_frfree(f);
  617. return NULL;
  618. }
  619. }
  620. }
  621. break;
  622. case AST_CONTROL_BUSY:
  623. ast_verb(3, "%s is busy\n", winner->name);
  624. break;
  625. case AST_CONTROL_CONGESTION:
  626. ast_verb(3, "%s is circuit-busy\n", winner->name);
  627. break;
  628. case AST_CONTROL_RINGING:
  629. ast_verb(3, "%s is ringing\n", winner->name);
  630. break;
  631. case AST_CONTROL_PROGRESS:
  632. ast_verb(3, "%s is making progress passing it to %s\n", winner->name, caller->name);
  633. break;
  634. case AST_CONTROL_VIDUPDATE:
  635. ast_verb(3, "%s requested a video update, passing it to %s\n", winner->name, caller->name);
  636. break;
  637. case AST_CONTROL_SRCUPDATE:
  638. ast_verb(3, "%s requested a source update, passing it to %s\n", winner->name, caller->name);
  639. break;
  640. case AST_CONTROL_PROCEEDING:
  641. ast_verb(3, "%s is proceeding passing it to %s\n", winner->name,caller->name);
  642. break;
  643. case AST_CONTROL_HOLD:
  644. ast_verb(3, "Call on %s placed on hold\n", winner->name);
  645. break;
  646. case AST_CONTROL_UNHOLD:
  647. ast_verb(3, "Call on %s left from hold\n", winner->name);
  648. break;
  649. case AST_CONTROL_OFFHOOK:
  650. case AST_CONTROL_FLASH:
  651. /* Ignore going off hook and flash */
  652. break;
  653. case -1:
  654. ast_verb(3, "%s stopped sounds\n", winner->name);
  655. break;
  656. default:
  657. ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
  658. break;
  659. }
  660. }
  661. if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
  662. if (winner->stream)
  663. ast_stopstream(winner);
  664. tmpuser->digts = 0;
  665. ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
  666. tmpuser->yn[tmpuser->ynidx] = (char) f->subclass.integer;
  667. tmpuser->ynidx++;
  668. ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
  669. if (tmpuser->ynidx >= ynlongest) {
  670. ast_debug(1, "reached longest possible match - doing evals\n");
  671. if (!strcmp(tmpuser->yn, tpargs->takecall)) {
  672. ast_debug(1, "Match to take the call!\n");
  673. ast_frfree(f);
  674. return tmpuser->ochan;
  675. }
  676. if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
  677. ast_debug(1, "Next in dial plan step requested.\n");
  678. *status = 1;
  679. ast_frfree(f);
  680. return NULL;
  681. }
  682. }
  683. }
  684. ast_frfree(f);
  685. } else {
  686. if (winner) {
  687. ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg);
  688. if (!dg) {
  689. clear_calling_tree(findme_user_list);
  690. return NULL;
  691. } else {
  692. tmpuser->state = -1;
  693. ast_hangup(winner);
  694. livechannels--;
  695. ast_debug(1, "live channels left %d\n", livechannels);
  696. if (!livechannels) {
  697. ast_verb(3, "no live channels left. exiting.\n");
  698. return NULL;
  699. }
  700. }
  701. }
  702. }
  703. } else
  704. ast_debug(1, "timed out waiting for action\n");
  705. }
  706. /* --- WAIT FOR WINNER NUMBER END! -----------*/
  707. return NULL;
  708. }
  709. static void findmeexec(struct fm_args *tpargs)
  710. {
  711. struct number *nm;
  712. struct ast_channel *outbound;
  713. struct ast_channel *caller;
  714. struct ast_channel *winner = NULL;
  715. char dialarg[512];
  716. int dg, idx;
  717. char *rest, *number;
  718. struct findme_user *tmpuser;
  719. struct findme_user *fmuser;
  720. struct findme_user_listptr *findme_user_list;
  721. int status;
  722. findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
  723. AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
  724. /* We're going to figure out what the longest possible string of digits to collect is */
  725. ynlongest = 0;
  726. if (strlen(tpargs->takecall) > ynlongest)
  727. ynlongest = strlen(tpargs->takecall);
  728. if (strlen(tpargs->nextindp) > ynlongest)
  729. ynlongest = strlen(tpargs->nextindp);
  730. idx = 1;
  731. caller = tpargs->chan;
  732. AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
  733. if (nm->order == idx)
  734. break;
  735. while (nm) {
  736. ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
  737. number = ast_strdupa(nm->number);
  738. ast_debug(3, "examining %s\n", number);
  739. do {
  740. rest = strchr(number, '&');
  741. if (rest) {
  742. *rest = 0;
  743. rest++;
  744. }
  745. /* We check if that context exists, before creating the ast_channel struct needed */
  746. if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) {
  747. /* XXX Should probably restructure to simply skip this item, instead of returning. XXX */
  748. ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
  749. free(findme_user_list);
  750. return;
  751. }
  752. if (!strcmp(tpargs->context, ""))
  753. snprintf(dialarg, sizeof(dialarg), "%s%s", number, ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION) ? "/n" : "");
  754. else
  755. snprintf(dialarg, sizeof(dialarg), "%s@%s%s", number, tpargs->context, ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION) ? "/n" : "");
  756. tmpuser = ast_calloc(1, sizeof(*tmpuser));
  757. if (!tmpuser) {
  758. ast_free(findme_user_list);
  759. return;
  760. }
  761. outbound = ast_request("Local", caller->nativeformats, caller, dialarg, &dg);
  762. if (outbound) {
  763. ast_set_callerid(outbound,
  764. S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL),
  765. S_COR(caller->caller.id.name.valid, caller->caller.id.name.str, NULL),
  766. S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL));
  767. ast_channel_inherit_variables(tpargs->chan, outbound);
  768. ast_channel_datastore_inherit(tpargs->chan, outbound);
  769. ast_string_field_set(outbound, language, tpargs->chan->language);
  770. ast_string_field_set(outbound, accountcode, tpargs->chan->accountcode);
  771. ast_string_field_set(outbound, musicclass, tpargs->chan->musicclass);
  772. ast_verb(3, "calling %s\n", dialarg);
  773. if (!ast_call(outbound,dialarg,0)) {
  774. tmpuser->ochan = outbound;
  775. tmpuser->state = 0;
  776. tmpuser->cleared = 0;
  777. ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
  778. AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
  779. } else {
  780. ast_verb(3, "couldn't reach at this number.\n");
  781. if (outbound) {
  782. if (!outbound->cdr)
  783. outbound->cdr = ast_cdr_alloc();
  784. if (outbound->cdr) {
  785. char tmp[256];
  786. ast_cdr_init(outbound->cdr, outbound);
  787. snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
  788. ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
  789. ast_cdr_update(outbound);
  790. ast_cdr_start(outbound->cdr);
  791. ast_cdr_end(outbound->cdr);
  792. /* If the cause wasn't handled properly */
  793. if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
  794. ast_cdr_failed(outbound->cdr);
  795. } else {
  796. ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
  797. ast_hangup(outbound);
  798. outbound = NULL;
  799. }
  800. }
  801. }
  802. } else
  803. ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
  804. number = rest;
  805. } while (number);
  806. status = 0;
  807. if (!AST_LIST_EMPTY(findme_user_list))
  808. winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
  809. while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
  810. if (!fmuser->cleared && fmuser->ochan != winner)
  811. clear_caller(fmuser);
  812. ast_free(fmuser);
  813. }
  814. fmuser = NULL;
  815. tmpuser = NULL;
  816. if (winner)
  817. break;
  818. if (!caller || ast_check_hangup(caller)) {
  819. tpargs->status = 1;
  820. ast_free(findme_user_list);
  821. return;
  822. }
  823. idx++;
  824. AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
  825. if (nm->order == idx)
  826. break;
  827. }
  828. }
  829. ast_free(findme_user_list);
  830. if (!winner)
  831. tpargs->status = 1;
  832. else {
  833. tpargs->status = 100;
  834. tpargs->outbound = winner;
  835. }
  836. return;
  837. }
  838. static struct call_followme *find_realtime(const char *name)
  839. {
  840. struct ast_variable *var = ast_load_realtime("followme", "name", name, SENTINEL), *v;
  841. struct ast_config *cfg;
  842. const char *catg;
  843. struct call_followme *new;
  844. struct ast_str *str = ast_str_create(16);
  845. if (!var) {
  846. return NULL;
  847. }
  848. if (!(new = alloc_profile(name))) {
  849. return NULL;
  850. }
  851. for (v = var; v; v = v->next) {
  852. if (!strcasecmp(v->name, "active")) {
  853. if (ast_false(v->value)) {
  854. ast_mutex_destroy(&new->lock);
  855. ast_free(new);
  856. return NULL;
  857. }
  858. } else {
  859. profile_set_param(new, v->name, v->value, 0, 0);
  860. }
  861. }
  862. ast_variables_destroy(var);
  863. new->realtime = 1;
  864. /* Load numbers */
  865. if (!(cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name", name, SENTINEL))) {
  866. ast_mutex_destroy(&new->lock);
  867. ast_free(new);
  868. return NULL;
  869. }
  870. for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
  871. const char *numstr, *timeoutstr, *ordstr;
  872. int timeout;
  873. struct number *cur;
  874. if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
  875. continue;
  876. }
  877. if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout")) || sscanf(timeoutstr, "%30d", &timeout) != 1 || timeout < 1) {
  878. timeout = 25;
  879. }
  880. /* This one has to exist; it was part of the query */
  881. ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
  882. ast_str_set(&str, 0, "%s", numstr);
  883. if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
  884. AST_LIST_INSERT_TAIL(&new->numbers, cur, entry);
  885. }
  886. }
  887. ast_config_destroy(cfg);
  888. return new;
  889. }
  890. static void end_bridge_callback(void *data)
  891. {
  892. char buf[80];
  893. time_t end;
  894. struct ast_channel *chan = data;
  895. time(&end);
  896. ast_channel_lock(chan);
  897. if (chan->cdr->answer.tv_sec) {
  898. snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
  899. pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
  900. }
  901. if (chan->cdr->start.tv_sec) {
  902. snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
  903. pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
  904. }
  905. ast_channel_unlock(chan);
  906. }
  907. static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
  908. {
  909. bconfig->end_bridge_callback_data = originator;
  910. }
  911. static int app_exec(struct ast_channel *chan, const char *data)
  912. {
  913. struct fm_args targs = { 0, };
  914. struct ast_bridge_config config;
  915. struct call_followme *f;
  916. struct number *nm, *newnm;
  917. int res = 0;
  918. char *argstr;
  919. char namerecloc[255];
  920. int duration = 0;
  921. struct ast_channel *caller;
  922. struct ast_channel *outbound;
  923. AST_DECLARE_APP_ARGS(args,
  924. AST_APP_ARG(followmeid);
  925. AST_APP_ARG(options);
  926. );
  927. if (ast_strlen_zero(data)) {
  928. ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
  929. return -1;
  930. }
  931. if (!(argstr = ast_strdupa((char *)data))) {
  932. ast_log(LOG_ERROR, "Out of memory!\n");
  933. return -1;
  934. }
  935. AST_STANDARD_APP_ARGS(args, argstr);
  936. if (ast_strlen_zero(args.followmeid)) {
  937. ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
  938. return -1;
  939. }
  940. AST_RWLIST_RDLOCK(&followmes);
  941. AST_RWLIST_TRAVERSE(&followmes, f, entry) {
  942. if (!strcasecmp(f->name, args.followmeid) && (f->active))
  943. break;
  944. }
  945. AST_RWLIST_UNLOCK(&followmes);
  946. ast_debug(1, "New profile %s.\n", args.followmeid);
  947. if (!f) {
  948. f = find_realtime(args.followmeid);
  949. }
  950. if (!f) {
  951. ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
  952. return 0;
  953. }
  954. /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
  955. if (args.options)
  956. ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
  957. /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
  958. ast_mutex_lock(&f->lock);
  959. targs.mohclass = ast_strdupa(f->moh);
  960. ast_copy_string(targs.context, f->context, sizeof(targs.context));
  961. ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
  962. ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
  963. ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
  964. ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
  965. ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
  966. ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
  967. ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
  968. ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
  969. /* Copy the numbers we're going to use into another list in case the master list should get modified
  970. (and locked) while we're trying to do a follow-me */
  971. AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
  972. AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
  973. newnm = create_followme_number(nm->number, nm->timeout, nm->order);
  974. AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
  975. }
  976. ast_mutex_unlock(&f->lock);
  977. snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
  978. duration = 5;
  979. if (!ast_fileexists(namerecloc, NULL, chan->language))
  980. ast_copy_string(namerecloc, "", sizeof(namerecloc));
  981. if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) {
  982. if (chan->_state != AST_STATE_UP) {
  983. ast_indicate(chan, AST_CONTROL_RINGING);
  984. }
  985. } else {
  986. /* Answer the call */
  987. if (chan->_state != AST_STATE_UP)
  988. ast_answer(chan);
  989. if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
  990. ast_stream_and_wait(chan, targs.statusprompt, "");
  991. if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
  992. if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0)
  993. goto outrun;
  994. if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
  995. if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
  996. goto outrun;
  997. if (ast_waitstream(chan, "") < 0)
  998. goto outrun;
  999. }
  1000. ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
  1001. }
  1002. targs.status = 0;
  1003. targs.chan = chan;
  1004. ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
  1005. findmeexec(&targs);
  1006. while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
  1007. ast_free(nm);
  1008. if (!ast_strlen_zero(namerecloc))
  1009. unlink(namerecloc);
  1010. if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) {
  1011. if (chan->_state != AST_STATE_UP) {
  1012. ast_answer(chan);
  1013. }
  1014. } else {
  1015. ast_moh_stop(chan);
  1016. }
  1017. if (targs.status != 100) {
  1018. if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
  1019. ast_stream_and_wait(chan, targs.sorryprompt, "");
  1020. res = 0;
  1021. } else {
  1022. caller = chan;
  1023. outbound = targs.outbound;
  1024. /* Bridge the two channels. */
  1025. memset(&config, 0, sizeof(config));
  1026. ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
  1027. ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
  1028. ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
  1029. config.end_bridge_callback = end_bridge_callback;
  1030. config.end_bridge_callback_data = chan;
  1031. config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
  1032. /* Be sure no generators are left on it */
  1033. ast_deactivate_generator(caller);
  1034. /* Make sure channels are compatible */
  1035. res = ast_channel_make_compatible(caller, outbound);
  1036. if (res < 0) {
  1037. ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
  1038. ast_hangup(outbound);
  1039. goto outrun;
  1040. }
  1041. res = ast_bridge_call(caller, outbound, &config);
  1042. if (outbound)
  1043. ast_hangup(outbound);
  1044. }
  1045. outrun:
  1046. if (f->realtime) {
  1047. /* Not in list */
  1048. free_numbers(f);
  1049. ast_free(f);
  1050. }
  1051. return res;
  1052. }
  1053. static int unload_module(void)
  1054. {
  1055. struct call_followme *f;
  1056. ast_unregister_application(app);
  1057. /* Free Memory. Yeah! I'm free! */
  1058. AST_RWLIST_WRLOCK(&followmes);
  1059. while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
  1060. free_numbers(f);
  1061. ast_free(f);
  1062. }
  1063. AST_RWLIST_UNLOCK(&followmes);
  1064. return 0;
  1065. }
  1066. static int load_module(void)
  1067. {
  1068. if(!reload_followme(0))
  1069. return AST_MODULE_LOAD_DECLINE;
  1070. return ast_register_application_xml(app, app_exec);
  1071. }
  1072. static int reload(void)
  1073. {
  1074. reload_followme(1);
  1075. return 0;
  1076. }
  1077. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
  1078. .load = load_module,
  1079. .unload = unload_module,
  1080. .reload = reload,
  1081. );