res_parking.c 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272
  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 Call Parking Resource
  21. *
  22. * \author Jonathan Rose <jrose@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <depend>bridge_holding</depend>
  26. <support_level>core</support_level>
  27. ***/
  28. /*** DOCUMENTATION
  29. <configInfo name="res_parking" language="en_US">
  30. <configFile name="res_parking.conf">
  31. <configObject name="globals">
  32. <synopsis>Options that apply to every parking lot</synopsis>
  33. <configOption name="parkeddynamic">
  34. <synopsis>Enables dynamically created parkinglots.</synopsis>
  35. </configOption>
  36. </configObject>
  37. <configObject name="parking_lot">
  38. <synopsis>Defined parking lots for res_parking to use to park calls on</synopsis>
  39. <configOption name="context" default="parkedcalls">
  40. <synopsis>The name of the context where calls are parked and picked up from.</synopsis>
  41. <description><para>This option is only used if parkext is set.</para></description>
  42. </configOption>
  43. <configOption name="parkext">
  44. <synopsis>Extension to park calls to this parking lot.</synopsis>
  45. <description><para>If this option is used, this extension will automatically be created to place calls into
  46. parking lots. In addition, if parkext_exclusive is set for this parking lot, the name of the parking lot
  47. will be included in the application's arguments so that it only parks to this parking lot. The extension
  48. will be created in <literal>context</literal>. Using this option also creates extensions for retrieving
  49. parked calls from the parking spaces in the same context.</para></description>
  50. </configOption>
  51. <configOption name="parkext_exclusive" default="no">
  52. <synopsis>If yes, the extension registered as parkext will park exclusively to this parking lot.</synopsis>
  53. </configOption>
  54. <configOption name="parkpos" default="701-750">
  55. <synopsis>Numerical range of parking spaces which can be used to retrieve parked calls.</synopsis>
  56. <description><para>If parkext is set, these extensions will automatically be mapped in <literal>context</literal>
  57. in order to pick up calls parked to these parking spaces.</para></description>
  58. </configOption>
  59. <configOption name="parkinghints" default="no">
  60. <synopsis>If yes, this parking lot will add hints automatically for parking spaces.</synopsis>
  61. </configOption>
  62. <configOption name="parkingtime" default="45">
  63. <synopsis>Amount of time a call will remain parked before giving up (in seconds).</synopsis>
  64. </configOption>
  65. <configOption name="parkedmusicclass">
  66. <synopsis>Which music class to use for parked calls. They will use the default if unspecified.</synopsis>
  67. </configOption>
  68. <configOption name="comebacktoorigin" default="yes">
  69. <synopsis>Determines what should be done with the parked channel if no one picks it up before it times out.</synopsis>
  70. <description><para>Valid Options:</para>
  71. <enumlist>
  72. <enum name="yes">
  73. <para>Automatically have the parked channel dial the device that parked the call with dial
  74. timeout set by the <literal>parkingtime</literal> option. When the call times out an extension
  75. to dial the PARKER will automatically be created in the <literal>park-dial</literal> context with
  76. an extension of the flattened parker device name. If the call is not answered, the parked channel
  77. that is timing out will continue in the dial plan at that point if there are more priorities in
  78. the extension (which won't be the case unless the dialplan deliberately includes such priorities
  79. in the <literal>park-dial</literal> context through pattern matching or deliberately written
  80. flattened peer extensions).</para>
  81. </enum>
  82. <enum name="no">
  83. <para>Place the call into the PBX at <literal>comebackcontext</literal> instead. The extension will
  84. still be set as the flattened peer name. If an extension the flattened peer name isn't available
  85. then it will fall back to the <literal>s</literal> extension. If that also is unavailable it will
  86. attempt to fall back to <literal>s@default</literal>. The normal dial extension will still be
  87. created in the <literal>park-dial</literal> context with the extension also being the flattened
  88. peer name.</para>
  89. </enum>
  90. </enumlist>
  91. <note><para>Flattened Peer Names - Extensions can not include slash characters since those are used for pattern
  92. matching. When a peer name is flattened, slashes become underscores. For example if the parker of a call
  93. is called <literal>SIP/0004F2040001</literal> then flattened peer name and therefor the extensions created
  94. and used on timeouts will be <literal>SIP_0004F204001</literal>.</para></note>
  95. <note><para>When parking times out and the channel returns to the dial plan, the following variables are set:
  96. </para></note>
  97. <variablelist>
  98. <variable name="PARKING_SPACE">
  99. <para>extension that the call was parked in prior to timing out.</para>
  100. </variable>
  101. <variable name="PARKINGSLOT">
  102. <para>Deprecated. Use <variable>PARKING_SPACE</variable> instead.</para>
  103. </variable>
  104. <variable name="PARKEDLOT">
  105. <para>name of the lot that the call was parked in prior to timing out.</para>
  106. </variable>
  107. <variable name="PARKER">
  108. <para>The device that parked the call</para>
  109. </variable>
  110. <variable name="PARKER_FLAT">
  111. <para>The flat version of <variable>PARKER</variable></para>
  112. </variable>
  113. </variablelist>
  114. </description>
  115. </configOption>
  116. <configOption name="comebackdialtime" default="30">
  117. <synopsis>Timeout for the Dial extension created to call back the parker when a parked call times out.</synopsis>
  118. </configOption>
  119. <configOption name="comebackcontext" default="parkedcallstimeout">
  120. <synopsis>Context where parked calls will enter the PBX on timeout when comebacktoorigin=no</synopsis>
  121. <description><para>The extension the call enters will prioritize the flattened peer name in this context.
  122. If the flattened peer name extension is unavailable, then the 's' extension in this context will be
  123. used. If that also is unavailable, the 's' extension in the 'default' context will be used.</para>
  124. </description>
  125. </configOption>
  126. <configOption name="courtesytone">
  127. <synopsis>If the name of a sound file is provided, use this as the courtesy tone</synopsis>
  128. <description><para>By default, this tone is only played to the caller of a parked call. Who receives the tone
  129. can be changed using the <literal>parkedplay</literal> option.</para>
  130. </description>
  131. </configOption>
  132. <configOption name="parkedplay" default="caller">
  133. <synopsis>Who we should play the courtesytone to on the pickup of a parked call from this lot</synopsis>
  134. <description>
  135. <enumlist>
  136. <enum name="no"><para>Apply to neither side.</para></enum>
  137. <enum name="caller"><para>Apply only to the call connecting with the call coming out of the parking lot.</para></enum>
  138. <enum name="callee"><para>Apply only to the call coming out of the parking lot.</para></enum>
  139. <enum name="both"><para>Apply to both sides.</para></enum>
  140. </enumlist>
  141. <note><para>If courtesy tone is not specified then this option will be ignored.</para></note>
  142. </description>
  143. </configOption>
  144. <configOption name="parkedcalltransfers" default="no">
  145. <synopsis>Who to apply the DTMF transfer features to when parked calls are picked up or timeout.</synopsis>
  146. <description>
  147. <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
  148. </description>
  149. </configOption>
  150. <configOption name="parkedcallreparking" default="no">
  151. <synopsis>Who to apply the DTMF parking feature to when parked calls are picked up or timeout.</synopsis>
  152. <description>
  153. <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
  154. </description>
  155. </configOption>
  156. <configOption name="parkedcallhangup" default="no">
  157. <synopsis>Who to apply the DTMF hangup feature to when parked calls are picked up or timeout.</synopsis>
  158. <description>
  159. <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
  160. </description>
  161. </configOption>
  162. <configOption name="parkedcallrecording" default="no">
  163. <synopsis>Who to apply the DTMF MixMonitor recording feature to when parked calls are picked up or timeout.</synopsis>
  164. <description>
  165. <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
  166. </description>
  167. </configOption>
  168. <configOption name="findslot" default="first">
  169. <synopsis>Rule to use when trying to figure out which parking space a call should be parked with.</synopsis>
  170. <description>
  171. <enumlist>
  172. <enum name="first"><para>Always try to place in the lowest available space in the parking lot</para></enum>
  173. <enum name="next"><para>Track the last parking space used and always attempt to use the one immediately after.
  174. </para></enum>
  175. </enumlist>
  176. </description>
  177. </configOption>
  178. <configOption name="courtesytone">
  179. <synopsis>If set, the sound set will be played to whomever is set by parkedplay</synopsis>
  180. </configOption>
  181. </configObject>
  182. </configFile>
  183. </configInfo>
  184. ***/
  185. #include "asterisk.h"
  186. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  187. #include "parking/res_parking.h"
  188. #include "asterisk/config.h"
  189. #include "asterisk/config_options.h"
  190. #include "asterisk/utils.h"
  191. #include "asterisk/module.h"
  192. #include "asterisk/cli.h"
  193. #include "asterisk/astobj2.h"
  194. #include "asterisk/features.h"
  195. #include "asterisk/manager.h"
  196. #include "asterisk/pbx.h"
  197. static int parking_lot_sort_fn(const void *obj_left, const void *obj_right, int flags)
  198. {
  199. const struct parking_lot *left = obj_left;
  200. const struct parking_lot *right = obj_right;
  201. const char *right_key = obj_right;
  202. int cmp;
  203. switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
  204. default:
  205. case OBJ_POINTER:
  206. right_key = right->name;
  207. /* Fall through */
  208. case OBJ_KEY:
  209. cmp = strcmp(left->name, right_key);
  210. break;
  211. case OBJ_PARTIAL_KEY:
  212. cmp = strncmp(left->name, right_key, strlen(right_key));
  213. }
  214. return cmp;
  215. }
  216. /*! All parking lots that are currently alive in some fashion can be obtained from here */
  217. static struct ao2_container *parking_lot_container;
  218. static void *parking_config_alloc(void);
  219. static void *parking_lot_cfg_alloc(const char *cat);
  220. static void *named_item_find(struct ao2_container *container, const char *name); /* XXX This is really just a generic string find. Move to astobj2.c? */
  221. static int config_parking_preapply(void);
  222. static void link_configured_disable_marked_lots(void);
  223. struct parking_global_config {
  224. int parkeddynamic;
  225. };
  226. struct parking_config {
  227. struct parking_global_config *global;
  228. struct ao2_container *parking_lots;
  229. };
  230. static struct aco_type global_option = {
  231. .type = ACO_GLOBAL,
  232. .name = "globals",
  233. .item_offset = offsetof(struct parking_config, global),
  234. .category_match = ACO_WHITELIST,
  235. .category = "^general$",
  236. };
  237. struct aco_type *global_options[] = ACO_TYPES(&global_option);
  238. static struct aco_type parking_lot_type = {
  239. .type = ACO_ITEM,
  240. .name = "parking_lot",
  241. .category_match = ACO_BLACKLIST,
  242. .category = "^(general)$",
  243. .item_alloc = parking_lot_cfg_alloc,
  244. .item_find = named_item_find,
  245. .item_offset = offsetof(struct parking_config, parking_lots),
  246. };
  247. struct aco_type *parking_lot_types[] = ACO_TYPES(&parking_lot_type);
  248. struct aco_file parking_lot_conf = {
  249. .filename = "res_parking.conf",
  250. .types = ACO_TYPES(&global_option, &parking_lot_type),
  251. };
  252. static AO2_GLOBAL_OBJ_STATIC(globals);
  253. CONFIG_INFO_STANDARD(cfg_info, globals, parking_config_alloc,
  254. .files = ACO_FILES(&parking_lot_conf),
  255. .pre_apply_config = config_parking_preapply,
  256. .post_apply_config = link_configured_disable_marked_lots,
  257. );
  258. static int parking_lot_cfg_hash_fn(const void *obj, const int flags)
  259. {
  260. const struct parking_lot_cfg *entry;
  261. const char *key;
  262. switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
  263. case OBJ_KEY:
  264. key = obj;
  265. return ast_str_hash(key);
  266. case OBJ_PARTIAL_KEY:
  267. ast_assert(0);
  268. return 0;
  269. default:
  270. entry = obj;
  271. return ast_str_hash(entry->name);
  272. }
  273. }
  274. static int parking_lot_cfg_cmp_fn(void *obj, void *arg, const int flags)
  275. {
  276. struct parking_lot_cfg *entry1 = obj;
  277. char *key;
  278. size_t key_size;
  279. struct parking_lot_cfg *entry2;
  280. switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
  281. case OBJ_KEY:
  282. key = arg;
  283. return (!strcmp(entry1->name, key)) ? CMP_MATCH : 0;
  284. case OBJ_PARTIAL_KEY:
  285. key = arg;
  286. key_size = strlen(key);
  287. return (!strncmp(entry1->name, key, key_size)) ? CMP_MATCH : 0;
  288. case OBJ_POINTER:
  289. entry2 = arg;
  290. return (!strcmp(entry1->name, entry2->name)) ? CMP_MATCH : 0;
  291. default:
  292. return CMP_STOP;
  293. }
  294. }
  295. /*! \brief destructor for parking_config */
  296. static void parking_config_destructor(void *obj)
  297. {
  298. struct parking_config *cfg = obj;
  299. ao2_cleanup(cfg->parking_lots);
  300. ao2_cleanup(cfg->global);
  301. }
  302. /*! \brief destructor for parking_global_config */
  303. static void parking_global_config_destructor(void *obj)
  304. {
  305. /* For now, do nothing. */
  306. }
  307. /*! \brief allocator callback for parking_config. Notice it returns void * since it is only used by the backend config code */
  308. static void *parking_config_alloc(void)
  309. {
  310. RAII_VAR(struct parking_config *, cfg, NULL, ao2_cleanup);
  311. if (!(cfg = ao2_alloc(sizeof(*cfg), parking_config_destructor))) {
  312. return NULL;
  313. }
  314. if (!(cfg->parking_lots = ao2_container_alloc(37, parking_lot_cfg_hash_fn, parking_lot_cfg_cmp_fn))) {
  315. return NULL;
  316. }
  317. if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), parking_global_config_destructor))) {
  318. return NULL;
  319. }
  320. /* Bump the ref count since RAII_VAR is going to eat one */
  321. ao2_ref(cfg, +1);
  322. return cfg;
  323. }
  324. int parking_lot_remove_if_unused(struct parking_lot *lot)
  325. {
  326. if (lot->mode != PARKINGLOT_DISABLED) {
  327. return -1;
  328. }
  329. if (!ao2_container_count(lot->parked_users)) {
  330. ao2_unlink(parking_lot_container, lot);
  331. return 0;
  332. }
  333. return -1;
  334. }
  335. static void parking_lot_disable(struct parking_lot *lot)
  336. {
  337. /* If a dynamic lot wasn't removed, we need to restore it to full functionality afterwards. */
  338. int was_dynamic = (lot->mode == PARKINGLOT_DYNAMIC);
  339. lot->mode = PARKINGLOT_DISABLED;
  340. if (parking_lot_remove_if_unused(lot) && was_dynamic) {
  341. lot->mode = PARKINGLOT_DYNAMIC;
  342. lot->disable_mark = 0;
  343. }
  344. }
  345. /*! \brief Destroy a parking lot cfg object */
  346. static void parking_lot_cfg_destructor(void *obj)
  347. {
  348. struct parking_lot_cfg *lot_cfg = obj;
  349. parking_lot_cfg_remove_extensions(lot_cfg);
  350. ast_string_field_free_memory(lot_cfg);
  351. }
  352. /* The arg just needs to have the parking space with it */
  353. static int parked_user_cmp_fn(void *obj, void *arg, int flags)
  354. {
  355. int *search_space = arg;
  356. struct parked_user *user = obj;
  357. int object_space = user->parking_space;
  358. if (*search_space == object_space) {
  359. return CMP_MATCH;
  360. }
  361. return 0;
  362. }
  363. static int parked_user_sort_fn(const void *obj_left, const void *obj_right, int flags)
  364. {
  365. const struct parked_user *left = obj_left;
  366. const struct parked_user *right = obj_right;
  367. return left->parking_space - right->parking_space;
  368. }
  369. /*!
  370. * \brief create a parking lot structure
  371. * \param cat name given to the parking lot
  372. * \retval NULL failure
  373. * \retval non-NULL successfully allocated parking lot
  374. */
  375. static void *parking_lot_cfg_alloc(const char *cat)
  376. {
  377. struct parking_lot_cfg *lot_cfg;
  378. lot_cfg = ao2_alloc(sizeof(*lot_cfg), parking_lot_cfg_destructor);
  379. if (!lot_cfg) {
  380. return NULL;
  381. }
  382. if (ast_string_field_init(lot_cfg, 32)) {
  383. ao2_cleanup(lot_cfg);
  384. return NULL;
  385. }
  386. ast_string_field_set(lot_cfg, name, cat);
  387. return lot_cfg;
  388. }
  389. #if defined(TEST_FRAMEWORK)
  390. struct parking_lot_cfg *parking_lot_cfg_create(const char *cat)
  391. {
  392. return parking_lot_cfg_alloc(cat);
  393. }
  394. #endif
  395. /*!
  396. * XXX This is actually incredibly generic and might be better placed in something like astobj2 if there isn't already an equivalent
  397. * \brief find an item in a container by its name
  398. *
  399. * \param container ao2container where we want the item from
  400. * \param key name of the item wanted to be found
  401. *
  402. * \retval pointer to the parking lot if available. NULL if not found.
  403. */
  404. static void *named_item_find(struct ao2_container *container, const char *name)
  405. {
  406. return ao2_find(container, name, OBJ_KEY);
  407. }
  408. /*!
  409. * \brief Custom field handler for parking positions
  410. */
  411. static int option_handler_parkpos(const struct aco_option *opt, struct ast_variable *var, void *obj)
  412. {
  413. struct parking_lot_cfg *lot_cfg = obj;
  414. int low;
  415. int high;
  416. if (sscanf(var->value, "%30d-%30d", &low, &high) != 2) {
  417. ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers\n");
  418. } else if (high < low || low <= 0 || high <= 0) {
  419. ast_log(LOG_WARNING, "Format for parking positions is a-b, where a <= b\n");
  420. } else {
  421. lot_cfg->parking_start = low;
  422. lot_cfg->parking_stop = high;
  423. return 0;
  424. }
  425. return -1;
  426. }
  427. /*!
  428. * \brief Custom field handler for the findslot option
  429. */
  430. static int option_handler_findslot(const struct aco_option *opt, struct ast_variable *var, void *obj)
  431. {
  432. struct parking_lot_cfg *lot_cfg = obj;
  433. if (!strcmp(var->value, "first")) {
  434. lot_cfg->parkfindnext = 0;
  435. } else if (!strcmp(var->value, "next")) {
  436. lot_cfg->parkfindnext = 1;
  437. } else {
  438. ast_log(LOG_WARNING, "value '%s' is not valid for findslot option.\n", var->value);
  439. return -1;
  440. }
  441. return 0;
  442. }
  443. /*!
  444. * \brief Maps string values for option_handler_parkedfeature to their ENUM values
  445. */
  446. static int parking_feature_flag_cfg(int *param, const char *var)
  447. {
  448. if (ast_false(var)) {
  449. *param = 0;
  450. } else if (!strcasecmp(var, "both")) {
  451. *param = AST_FEATURE_FLAG_BYBOTH;
  452. } else if (!strcasecmp(var, "caller")) {
  453. *param = AST_FEATURE_FLAG_BYCALLER;
  454. } else if (!strcasecmp(var, "callee")) {
  455. *param = AST_FEATURE_FLAG_BYCALLEE;
  456. } else {
  457. return -1;
  458. }
  459. return 0;
  460. }
  461. /*!
  462. * \brief Custom field handler for feature mapping on parked call pickup options
  463. */
  464. static int option_handler_parkedfeature(const struct aco_option *opt, struct ast_variable *var, void *obj)
  465. {
  466. struct parking_lot_cfg *cfg = obj;
  467. enum parked_call_feature_options option = aco_option_get_flags(opt);
  468. int *parameter = NULL;
  469. switch (option) {
  470. case OPT_PARKEDPLAY:
  471. parameter = &cfg->parkedplay;
  472. break;
  473. case OPT_PARKEDTRANSFERS:
  474. parameter = &cfg->parkedcalltransfers;
  475. break;
  476. case OPT_PARKEDREPARKING:
  477. parameter = &cfg->parkedcallreparking;
  478. break;
  479. case OPT_PARKEDHANGUP:
  480. parameter = &cfg->parkedcallhangup;
  481. break;
  482. case OPT_PARKEDRECORDING:
  483. parameter = &cfg->parkedcallrecording;
  484. break;
  485. }
  486. ast_assert(parameter != NULL);
  487. if (!parameter || parking_feature_flag_cfg(parameter, var->value)) {
  488. return -1;
  489. }
  490. return 0;
  491. }
  492. struct ao2_container *get_parking_lot_container(void)
  493. {
  494. return parking_lot_container;
  495. }
  496. struct parking_lot *parking_lot_find_by_name(const char *lot_name)
  497. {
  498. struct parking_lot *lot = named_item_find(parking_lot_container, lot_name);
  499. return lot;
  500. }
  501. const char *find_channel_parking_lot_name(struct ast_channel *chan)
  502. {
  503. const char *name;
  504. /* The channel variable overrides everything */
  505. name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
  506. if (ast_strlen_zero(name) && !ast_strlen_zero(ast_channel_parkinglot(chan))) {
  507. /* Use the channel's parking lot. */
  508. name = ast_channel_parkinglot(chan);
  509. }
  510. /* If the name couldn't be pulled from that either, use the default parking lot name. */
  511. if (ast_strlen_zero(name)) {
  512. name = DEFAULT_PARKING_LOT;
  513. }
  514. return name;
  515. }
  516. static void parking_lot_destructor(void *obj)
  517. {
  518. struct parking_lot *lot = obj;
  519. if (lot->parking_bridge) {
  520. ast_bridge_destroy(lot->parking_bridge, 0);
  521. }
  522. ao2_cleanup(lot->parked_users);
  523. ao2_cleanup(lot->cfg);
  524. ast_string_field_free_memory(lot);
  525. }
  526. static struct parking_lot *alloc_new_parking_lot(struct parking_lot_cfg *lot_cfg)
  527. {
  528. struct parking_lot *lot;
  529. if (!(lot = ao2_alloc(sizeof(*lot), parking_lot_destructor))) {
  530. return NULL;
  531. }
  532. if (ast_string_field_init(lot, 32)) {
  533. return NULL;
  534. }
  535. /* Create parked user ordered list */
  536. lot->parked_users = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK,
  537. AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
  538. parked_user_sort_fn,
  539. parked_user_cmp_fn);
  540. if (!lot->parked_users) {
  541. ao2_cleanup(lot);
  542. return NULL;
  543. }
  544. ast_string_field_set(lot, name, lot_cfg->name);
  545. return lot;
  546. }
  547. void parking_lot_cfg_remove_extensions(struct parking_lot_cfg *lot_cfg)
  548. {
  549. if (!ast_strlen_zero(lot_cfg->registrar)) {
  550. /* Although the function is called ast_context_destroy, the use of this funtion is
  551. * intended only to remove extensions, hints, etc registered by the parking lot's registrar.
  552. * It won't actually destroy the context unless that context is empty afterwards and it is
  553. * unreferenced.
  554. */
  555. ast_context_destroy(NULL, lot_cfg->registrar);
  556. }
  557. /* If we come back for a second pass, someone else has this registrar now. */
  558. ast_string_field_set(lot_cfg, registrar, "");
  559. }
  560. static void remove_all_configured_parking_lot_extensions(void)
  561. {
  562. RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  563. struct parking_lot_cfg *lot_cfg;
  564. struct ao2_iterator iter;
  565. if (!cfg) {
  566. return;
  567. }
  568. for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
  569. parking_lot_cfg_remove_extensions(lot_cfg);
  570. }
  571. ast_context_destroy(NULL, BASE_REGISTRAR);
  572. ao2_iterator_destroy(&iter);
  573. }
  574. /*!
  575. * \internal
  576. * \since 12
  577. * \brief Create an extension using ast_add_extension2_nolock. This function automatically allocates a duplicate
  578. * of the data string so that whatever calls it doesn't have to deal with freeing it if the ast_add_extension2_nolock
  579. * fails.
  580. *
  581. * \param context a write locked ast_context. Make certain it is write locked prior to calling this function
  582. * \param replace whether the extension should replace existing extensions
  583. * \param extension name of the extension desired
  584. * \param priority priority of the extension we are registering
  585. * \param application name of the application being used for the extension
  586. * \param data application arguments
  587. * \param registrar name of the registrar you should use for the extension.
  588. * Make sure this string doesn't go anywhere while there are still extensions using it.
  589. */
  590. static int parking_add_extension(struct ast_context *context, int replace, const char *extension,
  591. int priority, const char *application, const char *data, const char *registrar)
  592. {
  593. char *data_duplicate = ast_strdup(data);
  594. if (!data_duplicate) {
  595. return -1;
  596. }
  597. if (ast_add_extension2_nolock(context, replace, extension, priority, NULL, NULL,
  598. application, data_duplicate, ast_free_ptr, registrar)) {
  599. ast_free(data_duplicate);
  600. return -1;
  601. }
  602. return 0;
  603. }
  604. static int extension_is_compatible(struct parking_lot_cfg *lot_cfg, const char *app_type, struct ast_exten *extension)
  605. {
  606. const char *extension_registrar = ast_get_extension_registrar(extension);
  607. const char *extension_context = ast_get_context_name(ast_get_extension_context(extension));
  608. const char *extension_name = ast_get_extension_name(extension);
  609. const char *extension_application = ast_get_extension_app(extension);
  610. ast_assert(extension_registrar && extension_context && extension_name && extension_application);
  611. if (strcmp(extension_registrar, BASE_REGISTRAR)) {
  612. ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s', but that extension is already owned by %s.\n",
  613. lot_cfg->name, extension_name, extension_context, extension_registrar);
  614. return 0;
  615. }
  616. if (strcmp(extension_application, app_type)) {
  617. ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s' with a non-exclusive %s application, "
  618. "but a/an %s application is already registered to that extension by %s.\n",
  619. lot_cfg->name, extension_name, extension_context, app_type,
  620. extension_application, BASE_REGISTRAR);
  621. return 0;
  622. }
  623. ast_debug(3, "Parking lot '%s' -- extension '%s@%s' with application %s is compatible.\n",
  624. lot_cfg->name, extension_name, extension_context, app_type);
  625. return 1;
  626. }
  627. int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg)
  628. {
  629. int parkingspace;
  630. struct ast_exten *existing_exten;
  631. struct ast_context *lot_context;
  632. struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
  633. const char *parkext_registrar_pointer; /* Used for park extension */
  634. const char *parkedcall_registrar_pointer; /* Used for parkedcall extensions/hints */
  635. if (ast_strlen_zero(lot_cfg->parkext)) {
  636. return 0;
  637. }
  638. ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
  639. parkedcall_registrar_pointer = lot_cfg->registrar;
  640. if (lot_cfg->parkext_exclusive) {
  641. parkext_registrar_pointer = lot_cfg->registrar;
  642. } else {
  643. parkext_registrar_pointer = BASE_REGISTRAR;
  644. }
  645. /* We need the contexts list locked to safely be able to both read and lock the specific context within */
  646. if (ast_wrlock_contexts()) {
  647. ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
  648. return -1;
  649. }
  650. if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, parkext_registrar_pointer))) {
  651. ast_log(LOG_ERROR, "Parking lot '%s' -- Needs a context '%s' which does not exist and Asterisk was unable to create\n",
  652. lot_cfg->name, lot_cfg->parking_con);
  653. if (ast_unlock_contexts()) {
  654. ast_assert(0);
  655. }
  656. return -1;
  657. }
  658. /* Once we know what context we will be modifying, we need to write lock it because we will be reading extensions
  659. * and we don't want something else to destroy them while we are looking at them.
  660. */
  661. if (ast_wrlock_context(lot_context)) {
  662. ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
  663. return -1;
  664. }
  665. if (ast_unlock_contexts()) {
  666. ast_assert(0);
  667. }
  668. /* Handle generation/confirmation for the Park extension */
  669. if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, lot_cfg->parkext, 1, NULL, NULL, E_MATCH))) {
  670. if (lot_cfg->parkext_exclusive || !extension_is_compatible(lot_cfg, PARK_APPLICATION, existing_exten)) {
  671. ast_unlock_context(lot_context);
  672. return -1;
  673. }
  674. } else if (parking_add_extension(lot_context, 0, lot_cfg->parkext, 1, PARK_APPLICATION,
  675. lot_cfg->parkext_exclusive ? lot_cfg->name : "", parkext_registrar_pointer)) {
  676. ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
  677. lot_cfg->name, PARK_APPLICATION, lot_cfg->parkext, lot_cfg->parking_con);
  678. ast_unlock_context(lot_context);
  679. return -1;
  680. }
  681. /* Handle generation/confirmation for the ParkedCall extensions and hints */
  682. for (parkingspace = lot_cfg->parking_start; parkingspace <= lot_cfg->parking_stop; parkingspace++) {
  683. char space[AST_MAX_EXTENSION];
  684. RAII_VAR(struct ast_str *, arguments_string, NULL, ast_free);
  685. find_info.stacklen = 0; /* reset for pbx_find_exten */
  686. snprintf(space, sizeof(space), "%d", parkingspace);
  687. /* Unlike the Park extensions, ParkedCall extensions and their hints may never be shared for any reason. */
  688. if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, 1, NULL, NULL, E_MATCH))) {
  689. ast_unlock_context(lot_context);
  690. return -1;
  691. }
  692. arguments_string = ast_str_create(32);
  693. if (!arguments_string) {
  694. ast_unlock_context(lot_context);
  695. return -1;
  696. }
  697. ast_str_set(&arguments_string, 0, "%s,%s", lot_cfg->name, space);
  698. if (parking_add_extension(lot_context, 0, space, 1, PARKED_CALL_APPLICATION,
  699. ast_str_buffer(arguments_string), parkedcall_registrar_pointer)) {
  700. ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
  701. lot_cfg->name, PARKED_CALL_APPLICATION, space, lot_cfg->parking_con);
  702. ast_unlock_context(lot_context);
  703. return -1;
  704. }
  705. find_info.stacklen = 0; /* reset for pbx_find_exten */
  706. if (lot_cfg->parkaddhints) {
  707. char hint_device[AST_MAX_EXTENSION];
  708. snprintf(hint_device, sizeof(hint_device), "park:%s@%s", space, lot_cfg->parking_con);
  709. if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, PRIORITY_HINT, NULL, NULL, E_MATCH))) {
  710. ast_log(LOG_ERROR, "Parking lot '%s' -- Needs to add a hint '%s' at '%s@%s' but one already exists owned by %s\n",
  711. lot_cfg->name, hint_device, space, lot_cfg->parking_con, ast_get_extension_registrar(existing_exten));
  712. ast_unlock_context(lot_context);
  713. return -1;
  714. }
  715. if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", parkedcall_registrar_pointer)) {
  716. ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add hint '%s@%s' to the PBX.\n",
  717. lot_cfg->name, space, lot_cfg->parking_con);
  718. ast_unlock_context(lot_context);
  719. return -1;
  720. }
  721. }
  722. }
  723. if (ast_unlock_context(lot_context)) {
  724. ast_assert(0);
  725. }
  726. return 0;
  727. }
  728. struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg, int dynamic)
  729. {
  730. struct parking_lot *lot;
  731. struct parking_lot_cfg *replaced_cfg = NULL;
  732. int found = 0;
  733. /* Start by trying to find it. If that works we can skip the rest. */
  734. lot = named_item_find(parking_lot_container, lot_cfg->name);
  735. if (!lot) {
  736. lot = alloc_new_parking_lot(lot_cfg);
  737. /* If we still don't have a lot, we failed to alloc one. */
  738. if (!lot) {
  739. return NULL;
  740. }
  741. } else {
  742. found = 1;
  743. if (dynamic) {
  744. ast_log(LOG_ERROR, "Tried to create dynamic parking lot with name '%s' but a lot with that name already exists.\n", lot_cfg->name);
  745. ao2_cleanup(lot);
  746. return NULL;
  747. }
  748. }
  749. /* Set the configuration reference. Unref the one currently in the lot if it's there. */
  750. if (lot->cfg) {
  751. replaced_cfg = lot->cfg;
  752. }
  753. ao2_ref(lot_cfg, +1);
  754. lot->cfg = lot_cfg;
  755. ao2_cleanup(replaced_cfg);
  756. /* Set the operating mode to normal since the parking lot has a configuration. */
  757. lot->disable_mark = 0;
  758. lot->mode = dynamic ? PARKINGLOT_DYNAMIC : PARKINGLOT_NORMAL;
  759. if (!found) {
  760. /* Link after configuration is set since a lot without configuration will cause all kinds of trouble. */
  761. ao2_link(parking_lot_container, lot);
  762. };
  763. return lot;
  764. }
  765. static void generate_or_link_lots_to_configs(void)
  766. {
  767. RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  768. struct parking_lot_cfg *lot_cfg;
  769. struct ao2_iterator iter;
  770. iter = ao2_iterator_init(cfg->parking_lots, 0);
  771. for (; (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
  772. ao2_cleanup(parking_lot_build_or_update(lot_cfg, 0));
  773. }
  774. ao2_iterator_destroy(&iter);
  775. }
  776. int parking_dynamic_lots_enabled(void)
  777. {
  778. RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  779. if (!cfg) {
  780. return 0;
  781. }
  782. return cfg->global->parkeddynamic;
  783. }
  784. static struct parking_lot_cfg *clone_parkinglot_cfg(struct parking_lot_cfg *source, const char *name)
  785. {
  786. struct parking_lot_cfg *cfg = parking_lot_cfg_alloc(name);
  787. if (!cfg) {
  788. return NULL;
  789. }
  790. ast_string_fields_copy(cfg, source);
  791. /* Needs to be reset after being copied */
  792. ast_string_field_set(cfg, name, name);
  793. /* Stuff that should be cloned that isn't hit by string field copy */
  794. cfg->parking_start = source->parking_start;
  795. cfg->parking_stop = source->parking_stop;
  796. cfg->parkingtime = source->parkingtime;
  797. cfg->comebackdialtime = source->comebackdialtime;
  798. cfg->parkfindnext = source->parkfindnext;
  799. cfg->parkext_exclusive = source->parkext_exclusive;
  800. cfg->parkaddhints = source->parkaddhints;
  801. cfg->comebacktoorigin = source->comebacktoorigin;
  802. cfg->parkedplay = source->parkedplay;
  803. cfg->parkedcalltransfers = source->parkedcalltransfers;
  804. cfg->parkedcallreparking = source->parkedcallreparking;
  805. cfg->parkedcallhangup = source->parkedcallhangup;
  806. cfg->parkedcallrecording = source->parkedcallrecording;
  807. return cfg;
  808. }
  809. static struct parking_lot *create_dynamic_lot_full(const char *name, struct ast_channel *chan, int forced)
  810. {
  811. RAII_VAR(struct parking_lot_cfg *, cfg, NULL, ao2_cleanup);
  812. RAII_VAR(struct parking_lot *, template_lot, NULL, ao2_cleanup);
  813. struct parking_lot *lot;
  814. const char *dyn_context;
  815. const char *dyn_exten;
  816. const char *dyn_range;
  817. const char *template_name;
  818. const char *chan_template_name;
  819. int dyn_start;
  820. int dyn_end;
  821. if (!forced && !parking_dynamic_lots_enabled()) {
  822. return NULL;
  823. }
  824. ast_channel_lock(chan);
  825. chan_template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
  826. dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
  827. dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
  828. dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
  829. ast_channel_unlock(chan);
  830. template_name = S_OR(chan_template_name, DEFAULT_PARKING_LOT);
  831. template_lot = parking_lot_find_by_name(template_name);
  832. if (!template_lot) {
  833. ast_log(LOG_ERROR, "Lot %s does not exist. Can not use it as a dynamic parking lot template.\n",
  834. template_name);
  835. return NULL;
  836. }
  837. cfg = clone_parkinglot_cfg(template_lot->cfg, name);
  838. if (!cfg) {
  839. ast_log(LOG_ERROR, "Failed to allocate dynamic parking lot configuration.\n");
  840. return NULL;
  841. }
  842. if (!ast_strlen_zero(dyn_exten)) {
  843. ast_string_field_set(cfg, parkext, dyn_exten);
  844. }
  845. if (!ast_strlen_zero(dyn_context)) {
  846. ast_string_field_set(cfg, parking_con, dyn_context);
  847. }
  848. if (!ast_strlen_zero(dyn_range)) {
  849. if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
  850. ast_log(LOG_ERROR,
  851. "Invalid parking range %s specified in PARKINGDYNPOS: could not parse minimum/maximum parking space range\n", dyn_range);
  852. return NULL;
  853. }
  854. if (dyn_end < dyn_start || dyn_start < 0) {
  855. ast_log(LOG_ERROR,
  856. "Invalid parking range %s specified for PARKINGDYNPOS: end parking space must be greater than starting parking space.\n", dyn_range);
  857. return NULL;
  858. }
  859. cfg->parking_start = dyn_start;
  860. cfg->parking_stop = dyn_end;
  861. }
  862. if (parking_lot_cfg_create_extensions(cfg)) {
  863. ast_log(LOG_ERROR, "Extensions for dynamic parking lot '%s' could not be registered. Dynamic lot creation failed.\n", name);
  864. return NULL;
  865. }
  866. ao2_lock(parking_lot_container);
  867. if ((lot = parking_lot_find_by_name(name))) {
  868. ao2_unlock(parking_lot_container);
  869. ast_log(LOG_ERROR, "Started creating dynamic parking lot '%s', but a parking lot with that name already exists.\n", name);
  870. ao2_ref(lot, -1);
  871. return NULL;
  872. }
  873. lot = parking_lot_build_or_update(cfg, 1);
  874. ao2_unlock(parking_lot_container);
  875. if (!lot) {
  876. ast_log(LOG_NOTICE, "Failed to build dynamic parking lot '%s'\n", name);
  877. }
  878. return lot;
  879. }
  880. struct parking_lot *parking_create_dynamic_lot(const char *name, struct ast_channel *chan){
  881. return create_dynamic_lot_full(name, chan, 0);
  882. }
  883. #if defined(TEST_FRAMEWORK)
  884. struct parking_lot *parking_create_dynamic_lot_forced(const char *name, struct ast_channel *chan) {
  885. return create_dynamic_lot_full(name, chan, 1);
  886. }
  887. #endif
  888. /* Preapply */
  889. static int verify_default_parking_lot(void)
  890. {
  891. struct parking_config *cfg = aco_pending_config(&cfg_info);
  892. RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
  893. if (!cfg) {
  894. return 0;
  895. }
  896. lot_cfg = ao2_find(cfg->parking_lots, DEFAULT_PARKING_LOT, OBJ_KEY);
  897. if (!lot_cfg) {
  898. lot_cfg = parking_lot_cfg_alloc(DEFAULT_PARKING_LOT);
  899. if (!lot_cfg) {
  900. return -1;
  901. }
  902. ast_log(AST_LOG_NOTICE, "Adding %s profile to res_parking\n", DEFAULT_PARKING_LOT);
  903. aco_set_defaults(&parking_lot_type, DEFAULT_PARKING_LOT, lot_cfg);
  904. ast_string_field_set(lot_cfg, parkext, DEFAULT_PARKING_EXTEN);
  905. ao2_link(cfg->parking_lots, lot_cfg);
  906. }
  907. return 0;
  908. }
  909. static void remove_pending_parking_lot_extensions(struct parking_config *cfg_pending)
  910. {
  911. struct parking_lot_cfg *lot_cfg;
  912. struct ao2_iterator iter;
  913. for (iter = ao2_iterator_init(cfg_pending->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
  914. parking_lot_cfg_remove_extensions(lot_cfg);
  915. }
  916. ao2_iterator_destroy(&iter);
  917. ast_context_destroy(NULL, BASE_REGISTRAR);
  918. }
  919. static int configure_parking_extensions(void)
  920. {
  921. struct parking_config *cfg = aco_pending_config(&cfg_info);
  922. struct ao2_iterator iter;
  923. RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
  924. int res = 0;
  925. if (!cfg) {
  926. return 0;
  927. }
  928. /* Clear existing extensions */
  929. remove_all_configured_parking_lot_extensions();
  930. /* Attempt to build new extensions for each lot */
  931. for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
  932. if (parking_lot_cfg_create_extensions(lot_cfg)) {
  933. ao2_cleanup(lot_cfg);
  934. lot_cfg = NULL;
  935. res = -1;
  936. break;
  937. }
  938. }
  939. ao2_iterator_destroy(&iter);
  940. if (res) {
  941. remove_pending_parking_lot_extensions(cfg);
  942. ast_log(LOG_ERROR, "Extension registration failed. Previously configured lot extensions were removed and can not be safely restored.\n");
  943. }
  944. return res;
  945. }
  946. static void mark_lots_as_disabled(void)
  947. {
  948. struct ao2_iterator iter;
  949. struct parking_lot *lot;
  950. for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
  951. lot->disable_mark = 1;
  952. }
  953. ao2_iterator_destroy(&iter);
  954. }
  955. static int config_parking_preapply(void)
  956. {
  957. mark_lots_as_disabled();
  958. if (verify_default_parking_lot()) {
  959. return -1;
  960. }
  961. if (configure_parking_extensions()) {
  962. return -1;
  963. }
  964. return 0;
  965. }
  966. static void disable_marked_lots(void)
  967. {
  968. struct ao2_iterator iter;
  969. struct parking_lot *lot;
  970. for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
  971. if (lot->disable_mark) {
  972. parking_lot_disable(lot);
  973. }
  974. }
  975. ao2_iterator_destroy(&iter);
  976. }
  977. static void link_configured_disable_marked_lots(void)
  978. {
  979. generate_or_link_lots_to_configs();
  980. disable_marked_lots();
  981. }
  982. const struct ast_module_info *parking_get_module_info(void)
  983. {
  984. return ast_module_info;
  985. }
  986. static int unload_module(void)
  987. {
  988. unload_parking_bridge_features();
  989. remove_all_configured_parking_lot_extensions();
  990. unload_parking_applications();
  991. unload_parking_manager();
  992. unload_parking_ui();
  993. unload_parking_devstate();
  994. unload_parking_tests();
  995. ao2_cleanup(parking_lot_container);
  996. parking_lot_container = NULL;
  997. aco_info_destroy(&cfg_info);
  998. ao2_global_obj_release(globals);
  999. return 0;
  1000. }
  1001. static int load_module(void)
  1002. {
  1003. parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
  1004. AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
  1005. parking_lot_sort_fn,
  1006. NULL);
  1007. if (!parking_lot_container) {
  1008. goto error;
  1009. }
  1010. if (aco_info_init(&cfg_info)) {
  1011. goto error;
  1012. }
  1013. /* Global options */
  1014. aco_option_register(&cfg_info, "parkeddynamic", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct parking_global_config, parkeddynamic));
  1015. /* Register the per parking lot options. */
  1016. aco_option_register(&cfg_info, "parkext", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parkext));
  1017. aco_option_register(&cfg_info, "context", ACO_EXACT, parking_lot_types, "parkedcalls", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parking_con));
  1018. aco_option_register(&cfg_info, "parkingtime", ACO_EXACT, parking_lot_types, "45", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, parkingtime));
  1019. aco_option_register(&cfg_info, "comebacktoorigin", ACO_EXACT, parking_lot_types, "yes", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, comebacktoorigin));
  1020. aco_option_register(&cfg_info, "comebackcontext", ACO_EXACT, parking_lot_types, "parkedcallstimeout", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, comebackcontext));
  1021. aco_option_register(&cfg_info, "comebackdialtime", ACO_EXACT, parking_lot_types, "30", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, comebackdialtime));
  1022. aco_option_register(&cfg_info, "parkedmusicclass", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, mohclass));
  1023. aco_option_register(&cfg_info, "parkext_exclusive", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkext_exclusive));
  1024. aco_option_register(&cfg_info, "parkinghints", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkaddhints));
  1025. aco_option_register(&cfg_info, "courtesytone", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, courtesytone));
  1026. /* More complicated parking lot options that require special handling */
  1027. aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, parking_lot_types, "701-750", option_handler_parkpos, 0);
  1028. aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, parking_lot_types, "first", option_handler_findslot, 0);
  1029. aco_option_register_custom(&cfg_info, "parkedplay", ACO_EXACT, parking_lot_types, "caller", option_handler_parkedfeature, OPT_PARKEDPLAY);
  1030. aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDTRANSFERS);
  1031. aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDREPARKING);
  1032. aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDHANGUP);
  1033. aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDRECORDING);
  1034. if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
  1035. goto error;
  1036. }
  1037. if (load_parking_applications()) {
  1038. goto error;
  1039. }
  1040. if (load_parking_ui()) {
  1041. goto error;
  1042. }
  1043. if (load_parking_manager()) {
  1044. goto error;
  1045. }
  1046. if (load_parking_bridge_features()) {
  1047. goto error;
  1048. }
  1049. if (load_parking_devstate()) {
  1050. goto error;
  1051. }
  1052. if (load_parking_tests()) {
  1053. goto error;
  1054. }
  1055. return AST_MODULE_LOAD_SUCCESS;
  1056. error:
  1057. unload_module();
  1058. return AST_MODULE_LOAD_DECLINE;
  1059. }
  1060. static int reload_module(void)
  1061. {
  1062. if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
  1063. return AST_MODULE_LOAD_DECLINE;
  1064. }
  1065. return 0;
  1066. }
  1067. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
  1068. .support_level = AST_MODULE_SUPPORT_CORE,
  1069. .load = load_module,
  1070. .unload = unload_module,
  1071. .reload = reload_module,
  1072. );