parking_controller.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Jonathan Rose <jrose@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. /*! \file
  19. *
  20. * \brief Parking Entry, Exit, and other assorted controls.
  21. *
  22. * \author Jonathan Rose <jrose@digium.com>
  23. */
  24. #include "asterisk.h"
  25. #include "asterisk/logger.h"
  26. #include "res_parking.h"
  27. #include "asterisk/astobj2.h"
  28. #include "asterisk/utils.h"
  29. #include "asterisk/manager.h"
  30. #include "asterisk/test.h"
  31. #include "asterisk/features.h"
  32. #include "asterisk/bridge_basic.h"
  33. struct ast_bridge *parking_lot_get_bridge(struct parking_lot *lot)
  34. {
  35. struct ast_bridge *lot_bridge;
  36. if (lot->parking_bridge) {
  37. ao2_ref(lot->parking_bridge, +1);
  38. return lot->parking_bridge;
  39. }
  40. lot_bridge = bridge_parking_new(lot);
  41. if (!lot_bridge) {
  42. return NULL;
  43. }
  44. /* The parking lot needs a reference to the bridge as well. */
  45. lot->parking_bridge = lot_bridge;
  46. ao2_ref(lot->parking_bridge, +1);
  47. return lot_bridge;
  48. }
  49. int parking_channel_set_roles(struct ast_channel *chan, struct parking_lot *lot, int force_ringing)
  50. {
  51. if (ast_channel_add_bridge_role(chan, "holding_participant")) {
  52. return -1;
  53. }
  54. if (force_ringing) {
  55. if (ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing")) {
  56. return -1;
  57. }
  58. } else {
  59. if (ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold")) {
  60. return -1;
  61. }
  62. if (!ast_strlen_zero(lot->cfg->mohclass)) {
  63. if (ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", lot->cfg->mohclass)) {
  64. return -1;
  65. }
  66. }
  67. }
  68. return 0;
  69. }
  70. struct parking_limits_pvt {
  71. struct parked_user *user;
  72. };
  73. int unpark_parked_user(struct parked_user *pu)
  74. {
  75. if (pu->lot) {
  76. ao2_unlink(pu->lot->parked_users, pu);
  77. parking_lot_remove_if_unused(pu->lot);
  78. return 0;
  79. }
  80. return -1;
  81. }
  82. int parking_lot_get_space(struct parking_lot *lot, int target_override)
  83. {
  84. int original_target;
  85. int current_target;
  86. struct ao2_iterator i;
  87. struct parked_user *user;
  88. int wrap;
  89. if (lot->cfg->parkfindnext) {
  90. /* Use next_space if the lot already has next_space set; otherwise use lot start. */
  91. original_target = lot->next_space ? lot->next_space : lot->cfg->parking_start;
  92. } else {
  93. original_target = lot->cfg->parking_start;
  94. }
  95. if (target_override >= lot->cfg->parking_start && target_override <= lot->cfg->parking_stop) {
  96. original_target = target_override;
  97. }
  98. current_target = original_target;
  99. wrap = lot->cfg->parking_start;
  100. i = ao2_iterator_init(lot->parked_users, 0);
  101. while ((user = ao2_iterator_next(&i))) {
  102. /* Increment the wrap on each pass until we find an empty space */
  103. if (wrap == user->parking_space) {
  104. wrap += 1;
  105. }
  106. if (user->parking_space < current_target) {
  107. /* It's lower than the anticipated target, so we haven't reached the target yet. */
  108. ao2_ref(user, -1);
  109. continue;
  110. }
  111. if (user->parking_space > current_target) {
  112. /* The current target is usable because all items below have been read and the next target is higher than the one we want. */
  113. ao2_ref(user, -1);
  114. break;
  115. }
  116. /* We found one already parked here. */
  117. current_target += 1;
  118. ao2_ref(user, -1);
  119. }
  120. ao2_iterator_destroy(&i);
  121. if (current_target <= lot->cfg->parking_stop) {
  122. return current_target;
  123. }
  124. if (wrap <= lot->cfg->parking_stop) {
  125. return wrap;
  126. }
  127. return -1;
  128. }
  129. static int retrieve_parked_user_targeted(void *obj, void *arg, int flags)
  130. {
  131. int *target = arg;
  132. struct parked_user *user = obj;
  133. if (user->parking_space == *target) {
  134. return CMP_MATCH;
  135. }
  136. return 0;
  137. }
  138. struct parked_user *parking_lot_retrieve_parked_user(struct parking_lot *lot, int target)
  139. {
  140. RAII_VAR(struct parked_user *, user, NULL, ao2_cleanup);
  141. if (target < 0) {
  142. user = ao2_callback(lot->parked_users, 0, NULL, NULL);
  143. } else {
  144. user = ao2_callback(lot->parked_users, 0, retrieve_parked_user_targeted, &target);
  145. }
  146. if (!user) {
  147. return NULL;
  148. }
  149. ao2_lock(user);
  150. if (user->resolution != PARK_UNSET) {
  151. /* Abandon. Something else has resolved the parked user before we got to it. */
  152. ao2_unlock(user);
  153. return NULL;
  154. }
  155. ao2_unlink(lot->parked_users, user);
  156. user->resolution = PARK_ANSWERED;
  157. ao2_unlock(user);
  158. parking_lot_remove_if_unused(user->lot);
  159. /* Bump the ref count by 1 since the RAII_VAR will eat the reference otherwise */
  160. ao2_ref(user, +1);
  161. return user;
  162. }
  163. void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode)
  164. {
  165. /* Enabling features here should be additive to features that are already on the channel. */
  166. struct ast_flags feature_flags = { 0 };
  167. struct ast_flags *existing_features;
  168. ast_channel_lock(chan);
  169. existing_features = ast_bridge_features_ds_get(chan);
  170. if (existing_features) {
  171. feature_flags = *existing_features;
  172. }
  173. if (lot->cfg->parkedcalltransfers & recipient_mode) {
  174. ast_set_flag(&feature_flags, AST_FEATURE_REDIRECT);
  175. }
  176. if (lot->cfg->parkedcallreparking & recipient_mode) {
  177. ast_set_flag(&feature_flags, AST_FEATURE_PARKCALL);
  178. }
  179. if (lot->cfg->parkedcallhangup & recipient_mode) {
  180. ast_set_flag(&feature_flags, AST_FEATURE_DISCONNECT);
  181. }
  182. if (lot->cfg->parkedcallrecording & recipient_mode) {
  183. ast_set_flag(&feature_flags, AST_FEATURE_AUTOMIXMON);
  184. }
  185. ast_bridge_features_ds_set(chan, &feature_flags);
  186. ast_channel_unlock(chan);
  187. return;
  188. }
  189. void flatten_dial_string(char *dialstring)
  190. {
  191. int i;
  192. for (i = 0; dialstring[i]; i++) {
  193. if (dialstring[i] == '/') {
  194. /* The underscore is the flattest character of all. */
  195. dialstring[i] = '_';
  196. }
  197. }
  198. }
  199. int comeback_goto(struct parked_user *pu, struct parking_lot *lot)
  200. {
  201. struct ast_channel *chan = pu->chan;
  202. char *peername_flat = ast_strdupa(pu->parker_dial_string);
  203. /* Flatten the peername so that it can be used for performing the timeout PBX operations */
  204. flatten_dial_string(peername_flat);
  205. if (lot->cfg->comebacktoorigin) {
  206. if (ast_exists_extension(chan, PARK_DIAL_CONTEXT, peername_flat, 1, NULL)) {
  207. ast_async_goto(chan, PARK_DIAL_CONTEXT, peername_flat, 1);
  208. return 0;
  209. } else {
  210. ast_log(LOG_ERROR, "Can not start %s at %s,%s,1 because extension does not exist. Terminating call.\n",
  211. ast_channel_name(chan), PARK_DIAL_CONTEXT, peername_flat);
  212. return -1;
  213. }
  214. }
  215. if (ast_exists_extension(chan, lot->cfg->comebackcontext, peername_flat, 1, NULL)) {
  216. ast_async_goto(chan, lot->cfg->comebackcontext, peername_flat, 1);
  217. return 0;
  218. }
  219. if (ast_exists_extension(chan, lot->cfg->comebackcontext, "s", 1, NULL)) {
  220. ast_verb(2, "Could not start %s at %s,%s,1. Using 's@%s' instead.\n", ast_channel_name(chan),
  221. lot->cfg->comebackcontext, peername_flat, lot->cfg->comebackcontext);
  222. ast_async_goto(chan, lot->cfg->comebackcontext, "s", 1);
  223. return 0;
  224. }
  225. ast_verb(2, "Can not start %s at %s,%s,1 and exten 's@%s' does not exist. Using 's@default'\n",
  226. ast_channel_name(chan),
  227. lot->cfg->comebackcontext, peername_flat, lot->cfg->comebackcontext);
  228. ast_async_goto(chan, "default", "s", 1);
  229. return 0;
  230. }