bridge_after.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2007 - 2009, Digium, Inc.
  5. *
  6. * Richard Mudgett <rmudgett@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*!
  19. * \file
  20. * \brief After Bridge Execution API
  21. *
  22. * \author Richard Mudgett <rmudgett@digium.com>
  23. *
  24. * See Also:
  25. * \arg \ref AstCREDITS
  26. */
  27. /*** MODULEINFO
  28. <support_level>core</support_level>
  29. ***/
  30. #include "asterisk.h"
  31. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  32. #include "asterisk/logger.h"
  33. #include "asterisk/channel.h"
  34. #include "asterisk/pbx.h"
  35. #include "asterisk/bridge_after.h"
  36. struct after_bridge_cb_node {
  37. /*! Next list node. */
  38. AST_LIST_ENTRY(after_bridge_cb_node) list;
  39. /*! Desired callback function. */
  40. ast_bridge_after_cb callback;
  41. /*! After bridge callback will not be called and destroy any resources data may contain. */
  42. ast_bridge_after_cb_failed failed;
  43. /*! Extra data to pass to the callback. */
  44. void *data;
  45. /*! Reason the after bridge callback failed. */
  46. enum ast_bridge_after_cb_reason reason;
  47. };
  48. struct after_bridge_cb_ds {
  49. /*! After bridge callbacks container. */
  50. AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
  51. };
  52. /*!
  53. * \internal
  54. * \brief Indicate after bridge callback failed.
  55. * \since 12.0.0
  56. *
  57. * \param node After bridge callback node.
  58. *
  59. * \return Nothing
  60. */
  61. static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
  62. {
  63. if (node->failed) {
  64. node->failed(node->reason, node->data);
  65. node->failed = NULL;
  66. }
  67. }
  68. /*!
  69. * \internal
  70. * \brief Run discarding any after bridge callbacks.
  71. * \since 12.0.0
  72. *
  73. * \param after_bridge After bridge callback container process.
  74. * \param reason Why are we doing this.
  75. *
  76. * \return Nothing
  77. */
  78. static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_bridge_after_cb_reason reason)
  79. {
  80. struct after_bridge_cb_node *node;
  81. for (;;) {
  82. AST_LIST_LOCK(&after_bridge->callbacks);
  83. node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
  84. AST_LIST_UNLOCK(&after_bridge->callbacks);
  85. if (!node) {
  86. break;
  87. }
  88. if (!node->reason) {
  89. node->reason = reason;
  90. }
  91. after_bridge_cb_failed(node);
  92. ast_free(node);
  93. }
  94. }
  95. /*!
  96. * \internal
  97. * \brief Destroy the after bridge callback datastore.
  98. * \since 12.0.0
  99. *
  100. * \param data After bridge callback data to destroy.
  101. *
  102. * \return Nothing
  103. */
  104. static void after_bridge_cb_destroy(void *data)
  105. {
  106. struct after_bridge_cb_ds *after_bridge = data;
  107. after_bridge_cb_run_discard(after_bridge, AST_BRIDGE_AFTER_CB_REASON_DESTROY);
  108. AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
  109. ast_free(after_bridge);
  110. }
  111. static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan);
  112. /*!
  113. * \internal
  114. * \brief Fixup the after bridge callback datastore.
  115. * \since 12.0.0
  116. *
  117. * \param data After bridge callback data to fixup.
  118. * \param old_chan The datastore is moving from this channel.
  119. * \param new_chan The datastore is moving to this channel.
  120. *
  121. * \return Nothing
  122. */
  123. static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
  124. {
  125. struct after_bridge_cb_ds *after_bridge;
  126. struct after_bridge_cb_node *node;
  127. after_bridge = after_bridge_cb_find(new_chan);
  128. if (!after_bridge) {
  129. return;
  130. }
  131. AST_LIST_LOCK(&after_bridge->callbacks);
  132. node = AST_LIST_LAST(&after_bridge->callbacks);
  133. if (node && !node->reason) {
  134. node->reason = AST_BRIDGE_AFTER_CB_REASON_MASQUERADE;
  135. }
  136. AST_LIST_UNLOCK(&after_bridge->callbacks);
  137. }
  138. static const struct ast_datastore_info after_bridge_cb_info = {
  139. .type = "after-bridge-cb",
  140. .destroy = after_bridge_cb_destroy,
  141. .chan_fixup = after_bridge_cb_fixup,
  142. };
  143. /*!
  144. * \internal
  145. * \brief Find an after bridge callback datastore container.
  146. * \since 12.0.0
  147. *
  148. * \param chan Channel to find the after bridge callback container on.
  149. *
  150. * \retval after_bridge datastore container on success.
  151. * \retval NULL on error.
  152. */
  153. static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
  154. {
  155. struct ast_datastore *datastore;
  156. SCOPED_CHANNELLOCK(lock, chan);
  157. datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
  158. if (!datastore) {
  159. return NULL;
  160. }
  161. return datastore->data;
  162. }
  163. /*!
  164. * \internal
  165. * \brief Setup/create an after bridge callback datastore container.
  166. * \since 12.0.0
  167. *
  168. * \param chan Channel to setup/create the after bridge callback container on.
  169. *
  170. * \retval after_bridge datastore container on success.
  171. * \retval NULL on error.
  172. */
  173. static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
  174. {
  175. struct ast_datastore *datastore;
  176. struct after_bridge_cb_ds *after_bridge;
  177. SCOPED_CHANNELLOCK(lock, chan);
  178. datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
  179. if (datastore) {
  180. return datastore->data;
  181. }
  182. /* Create a new datastore. */
  183. datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
  184. if (!datastore) {
  185. return NULL;
  186. }
  187. after_bridge = ast_calloc(1, sizeof(*after_bridge));
  188. if (!after_bridge) {
  189. ast_datastore_free(datastore);
  190. return NULL;
  191. }
  192. AST_LIST_HEAD_INIT(&after_bridge->callbacks);
  193. datastore->data = after_bridge;
  194. ast_channel_datastore_add(chan, datastore);
  195. return datastore->data;
  196. }
  197. void ast_bridge_run_after_callback(struct ast_channel *chan)
  198. {
  199. struct after_bridge_cb_ds *after_bridge;
  200. struct after_bridge_cb_node *node;
  201. after_bridge = after_bridge_cb_find(chan);
  202. if (!after_bridge) {
  203. return;
  204. }
  205. for (;;) {
  206. AST_LIST_LOCK(&after_bridge->callbacks);
  207. node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
  208. AST_LIST_UNLOCK(&after_bridge->callbacks);
  209. if (!node) {
  210. break;
  211. }
  212. if (node->reason) {
  213. after_bridge_cb_failed(node);
  214. } else {
  215. node->failed = NULL;
  216. node->callback(chan, node->data);
  217. }
  218. ast_free(node);
  219. }
  220. }
  221. void ast_bridge_discard_after_callback(struct ast_channel *chan, enum ast_bridge_after_cb_reason reason)
  222. {
  223. struct after_bridge_cb_ds *after_bridge;
  224. after_bridge = after_bridge_cb_find(chan);
  225. if (!after_bridge) {
  226. return;
  227. }
  228. after_bridge_cb_run_discard(after_bridge, reason);
  229. }
  230. int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb callback, ast_bridge_after_cb_failed failed, void *data)
  231. {
  232. struct after_bridge_cb_ds *after_bridge;
  233. struct after_bridge_cb_node *new_node;
  234. struct after_bridge_cb_node *last_node;
  235. /* Sanity checks. */
  236. ast_assert(chan != NULL);
  237. if (!chan || !callback) {
  238. return -1;
  239. }
  240. after_bridge = after_bridge_cb_setup(chan);
  241. if (!after_bridge) {
  242. return -1;
  243. }
  244. /* Create a new callback node. */
  245. new_node = ast_calloc(1, sizeof(*new_node));
  246. if (!new_node) {
  247. return -1;
  248. }
  249. new_node->callback = callback;
  250. new_node->failed = failed;
  251. new_node->data = data;
  252. /* Put it in the container disabling any previously active one. */
  253. AST_LIST_LOCK(&after_bridge->callbacks);
  254. last_node = AST_LIST_LAST(&after_bridge->callbacks);
  255. if (last_node && !last_node->reason) {
  256. last_node->reason = AST_BRIDGE_AFTER_CB_REASON_REPLACED;
  257. }
  258. AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
  259. AST_LIST_UNLOCK(&after_bridge->callbacks);
  260. return 0;
  261. }
  262. const char *reason_strings[] = {
  263. [AST_BRIDGE_AFTER_CB_REASON_DESTROY] = "Channel destroyed (hungup)",
  264. [AST_BRIDGE_AFTER_CB_REASON_REPLACED] = "Callback was replaced",
  265. [AST_BRIDGE_AFTER_CB_REASON_MASQUERADE] = "Channel masqueraded",
  266. [AST_BRIDGE_AFTER_CB_REASON_DEPART] = "Channel was departed from bridge",
  267. [AST_BRIDGE_AFTER_CB_REASON_REMOVED] = "Callback was removed",
  268. };
  269. const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason)
  270. {
  271. if (reason < AST_BRIDGE_AFTER_CB_REASON_DESTROY
  272. || AST_BRIDGE_AFTER_CB_REASON_REMOVED < reason
  273. || !reason_strings[reason]) {
  274. return "Unknown";
  275. }
  276. return reason_strings[reason];
  277. }
  278. struct after_bridge_goto_ds {
  279. /*! Goto string that can be parsed by ast_parseable_goto(). */
  280. const char *parseable_goto;
  281. /*! Specific goto context or default context for parseable_goto. */
  282. const char *context;
  283. /*! Specific goto exten or default exten for parseable_goto. */
  284. const char *exten;
  285. /*! Specific goto priority or default priority for parseable_goto. */
  286. int priority;
  287. /*! TRUE if the peer should run the h exten. */
  288. unsigned int run_h_exten:1;
  289. /*! Specific goto location */
  290. unsigned int specific:1;
  291. };
  292. /*!
  293. * \internal
  294. * \brief Destroy the after bridge goto datastore.
  295. * \since 12.0.0
  296. *
  297. * \param data After bridge goto data to destroy.
  298. *
  299. * \return Nothing
  300. */
  301. static void after_bridge_goto_destroy(void *data)
  302. {
  303. struct after_bridge_goto_ds *after_bridge = data;
  304. ast_free((char *) after_bridge->parseable_goto);
  305. ast_free((char *) after_bridge->context);
  306. ast_free((char *) after_bridge->exten);
  307. ast_free((char *) after_bridge);
  308. }
  309. /*!
  310. * \internal
  311. * \brief Fixup the after bridge goto datastore.
  312. * \since 12.0.0
  313. *
  314. * \param data After bridge goto data to fixup.
  315. * \param old_chan The datastore is moving from this channel.
  316. * \param new_chan The datastore is moving to this channel.
  317. *
  318. * \return Nothing
  319. */
  320. static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
  321. {
  322. /* There can be only one. Discard any already on the new channel. */
  323. ast_bridge_discard_after_goto(new_chan);
  324. }
  325. static const struct ast_datastore_info after_bridge_goto_info = {
  326. .type = "after-bridge-goto",
  327. .destroy = after_bridge_goto_destroy,
  328. .chan_fixup = after_bridge_goto_fixup,
  329. };
  330. /*!
  331. * \internal
  332. * \brief Remove channel goto location after the bridge and return it.
  333. * \since 12.0.0
  334. *
  335. * \param chan Channel to remove after bridge goto location.
  336. *
  337. * \retval datastore on success.
  338. * \retval NULL on error or not found.
  339. */
  340. static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan)
  341. {
  342. struct ast_datastore *datastore;
  343. ast_channel_lock(chan);
  344. datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
  345. if (datastore && ast_channel_datastore_remove(chan, datastore)) {
  346. datastore = NULL;
  347. }
  348. ast_channel_unlock(chan);
  349. return datastore;
  350. }
  351. void ast_bridge_discard_after_goto(struct ast_channel *chan)
  352. {
  353. struct ast_datastore *datastore;
  354. datastore = after_bridge_goto_remove(chan);
  355. if (datastore) {
  356. ast_datastore_free(datastore);
  357. }
  358. }
  359. void ast_bridge_read_after_goto(struct ast_channel *chan, char *buffer, size_t buf_size)
  360. {
  361. struct ast_datastore *datastore;
  362. struct after_bridge_goto_ds *after_bridge;
  363. char *current_pos = buffer;
  364. size_t remaining_size = buf_size;
  365. SCOPED_CHANNELLOCK(lock, chan);
  366. datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
  367. if (!datastore) {
  368. buffer[0] = '\0';
  369. return;
  370. }
  371. after_bridge = datastore->data;
  372. if (after_bridge->parseable_goto) {
  373. snprintf(buffer, buf_size, "%s", after_bridge->parseable_goto);
  374. return;
  375. }
  376. if (!ast_strlen_zero(after_bridge->context)) {
  377. snprintf(current_pos, remaining_size, "%s,", after_bridge->context);
  378. remaining_size = remaining_size - strlen(current_pos);
  379. current_pos += strlen(current_pos);
  380. }
  381. if (after_bridge->run_h_exten) {
  382. snprintf(current_pos, remaining_size, "h,");
  383. remaining_size = remaining_size - strlen(current_pos);
  384. current_pos += strlen(current_pos);
  385. } else if (!ast_strlen_zero(after_bridge->exten)) {
  386. snprintf(current_pos, remaining_size, "%s,", after_bridge->exten);
  387. remaining_size = remaining_size - strlen(current_pos);
  388. current_pos += strlen(current_pos);
  389. }
  390. snprintf(current_pos, remaining_size, "%d", after_bridge->priority);
  391. }
  392. int ast_bridge_setup_after_goto(struct ast_channel *chan)
  393. {
  394. struct ast_datastore *datastore;
  395. struct after_bridge_goto_ds *after_bridge;
  396. int goto_failed = -1;
  397. /* We are going to be leaving the bridging system now;
  398. * clear any pending unbridge flags
  399. */
  400. ast_channel_set_unbridged(chan, 0);
  401. /* Determine if we are going to setup a dialplan location and where. */
  402. if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
  403. /* An async goto has already setup a location. */
  404. ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO);
  405. if (!ast_check_hangup(chan)) {
  406. goto_failed = 0;
  407. }
  408. return goto_failed;
  409. }
  410. /* Get after bridge goto datastore. */
  411. datastore = after_bridge_goto_remove(chan);
  412. if (!datastore) {
  413. return goto_failed;
  414. }
  415. after_bridge = datastore->data;
  416. if (after_bridge->run_h_exten) {
  417. if (ast_exists_extension(chan, after_bridge->context, "h", 1,
  418. S_COR(ast_channel_caller(chan)->id.number.valid,
  419. ast_channel_caller(chan)->id.number.str, NULL))) {
  420. ast_debug(1, "Running after bridge goto h exten %s,h,1\n",
  421. ast_channel_context(chan));
  422. ast_pbx_h_exten_run(chan, after_bridge->context);
  423. }
  424. } else if (!ast_check_hangup(chan)) {
  425. /* Clear the outgoing flag */
  426. ast_clear_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
  427. if (after_bridge->specific) {
  428. goto_failed = ast_explicit_goto(chan, after_bridge->context,
  429. after_bridge->exten, after_bridge->priority);
  430. } else if (!ast_strlen_zero(after_bridge->parseable_goto)) {
  431. char *context;
  432. char *exten;
  433. int priority;
  434. /* Option F(x) for Bridge(), Dial(), and Queue() */
  435. /* Save current dialplan location in case of failure. */
  436. context = ast_strdupa(ast_channel_context(chan));
  437. exten = ast_strdupa(ast_channel_exten(chan));
  438. priority = ast_channel_priority(chan);
  439. /* Set current dialplan position to default dialplan position */
  440. ast_explicit_goto(chan, after_bridge->context, after_bridge->exten,
  441. after_bridge->priority);
  442. /* Then perform the goto */
  443. goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto);
  444. if (goto_failed) {
  445. /* Restore original dialplan location. */
  446. ast_channel_context_set(chan, context);
  447. ast_channel_exten_set(chan, exten);
  448. ast_channel_priority_set(chan, priority);
  449. }
  450. } else {
  451. /* Option F() for Bridge(), Dial(), and Queue() */
  452. goto_failed = ast_goto_if_exists(chan, after_bridge->context,
  453. after_bridge->exten, after_bridge->priority + 1);
  454. }
  455. if (!goto_failed) {
  456. if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
  457. ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
  458. }
  459. ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n",
  460. ast_channel_context(chan),
  461. ast_channel_exten(chan),
  462. ast_channel_priority(chan));
  463. }
  464. }
  465. /* Discard after bridge goto datastore. */
  466. ast_datastore_free(datastore);
  467. return goto_failed;
  468. }
  469. void ast_bridge_run_after_goto(struct ast_channel *chan)
  470. {
  471. int goto_failed;
  472. goto_failed = ast_bridge_setup_after_goto(chan);
  473. if (goto_failed || ast_pbx_run(chan)) {
  474. ast_hangup(chan);
  475. }
  476. }
  477. /*!
  478. * \internal
  479. * \brief Set after bridge goto location of channel.
  480. * \since 12.0.0
  481. *
  482. * \param chan Channel to setup after bridge goto location.
  483. * \param run_h_exten TRUE if the h exten should be run.
  484. * \param specific TRUE if the context/exten/priority is exactly specified.
  485. * \param context Context to goto after bridge.
  486. * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten)
  487. * \param priority Priority to goto after bridge.
  488. * \param parseable_goto User specified goto string. (Could be NULL)
  489. *
  490. * \details Add a channel datastore to setup the goto location
  491. * when the channel leaves the bridge and run a PBX from there.
  492. *
  493. * If run_h_exten then execute the h exten found in the given context.
  494. * Else if specific then goto the given context/exten/priority.
  495. * Else if parseable_goto then use the given context/exten/priority
  496. * as the relative position for the parseable_goto.
  497. * Else goto the given context/exten/priority+1.
  498. *
  499. * \return Nothing
  500. */
  501. static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto)
  502. {
  503. struct ast_datastore *datastore;
  504. struct after_bridge_goto_ds *after_bridge;
  505. /* Sanity checks. */
  506. ast_assert(chan != NULL);
  507. if (!chan) {
  508. return;
  509. }
  510. if (run_h_exten) {
  511. ast_assert(run_h_exten && context);
  512. if (!context) {
  513. return;
  514. }
  515. } else {
  516. ast_assert(context && exten && 0 < priority);
  517. if (!context || !exten || priority < 1) {
  518. return;
  519. }
  520. }
  521. /* Create a new datastore. */
  522. datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL);
  523. if (!datastore) {
  524. return;
  525. }
  526. after_bridge = ast_calloc(1, sizeof(*after_bridge));
  527. if (!after_bridge) {
  528. ast_datastore_free(datastore);
  529. return;
  530. }
  531. /* Initialize it. */
  532. after_bridge->parseable_goto = ast_strdup(parseable_goto);
  533. after_bridge->context = ast_strdup(context);
  534. after_bridge->exten = ast_strdup(exten);
  535. after_bridge->priority = priority;
  536. after_bridge->run_h_exten = run_h_exten ? 1 : 0;
  537. after_bridge->specific = specific ? 1 : 0;
  538. datastore->data = after_bridge;
  539. if ((parseable_goto && !after_bridge->parseable_goto)
  540. || (context && !after_bridge->context)
  541. || (exten && !after_bridge->exten)) {
  542. ast_datastore_free(datastore);
  543. return;
  544. }
  545. /* Put it on the channel replacing any existing one. */
  546. ast_channel_lock(chan);
  547. ast_bridge_discard_after_goto(chan);
  548. ast_channel_datastore_add(chan, datastore);
  549. ast_channel_unlock(chan);
  550. }
  551. void ast_bridge_set_after_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
  552. {
  553. __after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL);
  554. }
  555. void ast_bridge_set_after_h(struct ast_channel *chan, const char *context)
  556. {
  557. __after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL);
  558. }
  559. void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto)
  560. {
  561. char *p_goto;
  562. if (!ast_strlen_zero(parseable_goto)) {
  563. p_goto = ast_strdupa(parseable_goto);
  564. ast_replace_subargument_delimiter(p_goto);
  565. } else {
  566. p_goto = NULL;
  567. }
  568. __after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto);
  569. }