app_directed_pickup.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2005, Joshua Colp
  5. *
  6. * Joshua Colp <jcolp@digium.com>
  7. *
  8. * Portions merged from app_pickupchan, which was
  9. * Copyright (C) 2008, Gary Cook
  10. *
  11. * See http://www.asterisk.org for more information about
  12. * the Asterisk project. Please do not directly contact
  13. * any of the maintainers of this project for assistance;
  14. * the project provides a web site, mailing lists and IRC
  15. * channels for your use.
  16. *
  17. * This program is free software, distributed under the terms of
  18. * the GNU General Public License Version 2. See the LICENSE file
  19. * at the top of the source tree.
  20. */
  21. /*! \file
  22. *
  23. * \brief Directed Call Pickup Support
  24. *
  25. * \author Joshua Colp <jcolp@digium.com>
  26. * \author Gary Cook
  27. *
  28. * \ingroup applications
  29. */
  30. /*** MODULEINFO
  31. <support_level>core</support_level>
  32. ***/
  33. #include "asterisk.h"
  34. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  35. #include "asterisk/file.h"
  36. #include "asterisk/channel.h"
  37. #include "asterisk/pbx.h"
  38. #include "asterisk/module.h"
  39. #include "asterisk/lock.h"
  40. #include "asterisk/app.h"
  41. #include "asterisk/pickup.h"
  42. #include "asterisk/manager.h"
  43. #include "asterisk/callerid.h"
  44. #define PICKUPMARK "PICKUPMARK"
  45. /*** DOCUMENTATION
  46. <application name="Pickup" language="en_US">
  47. <synopsis>
  48. Directed extension call pickup.
  49. </synopsis>
  50. <syntax>
  51. <parameter name="targets" argsep="&amp;">
  52. <argument name="extension" argsep="@" required="true">
  53. <para>Specification of the pickup target.</para>
  54. <argument name="extension" required="true"/>
  55. <argument name="context" />
  56. </argument>
  57. <argument name="extension2" argsep="@" multiple="true">
  58. <para>Additional specifications of pickup targets.</para>
  59. <argument name="extension2" required="true"/>
  60. <argument name="context2"/>
  61. </argument>
  62. </parameter>
  63. </syntax>
  64. <description>
  65. <para>This application can pickup a specified ringing channel. The channel
  66. to pickup can be specified in the following ways.</para>
  67. <para>1) If no <replaceable>extension</replaceable> targets are specified,
  68. the application will pickup a channel matching the pickup group of the
  69. requesting channel.</para>
  70. <para>2) If the <replaceable>extension</replaceable> is specified with a
  71. <replaceable>context</replaceable> of the special string
  72. <literal>PICKUPMARK</literal> (for example 10@PICKUPMARK), the application
  73. will pickup a channel which has defined the channel variable
  74. <variable>PICKUPMARK</variable> with the same value as
  75. <replaceable>extension</replaceable> (in this example,
  76. <literal>10</literal>).</para>
  77. <para>3) If the <replaceable>extension</replaceable> is specified
  78. with or without a <replaceable>context</replaceable>, the channel with a
  79. matching <replaceable>extension</replaceable> and <replaceable>context</replaceable>
  80. will be picked up. If no <replaceable>context</replaceable> is specified,
  81. the current context will be used.</para>
  82. <note><para>The <replaceable>extension</replaceable> is typically set on
  83. matching channels by the dial application that created the channel. The
  84. <replaceable>context</replaceable> is set on matching channels by the
  85. channel driver for the device.</para></note>
  86. </description>
  87. </application>
  88. <application name="PickupChan" language="en_US">
  89. <synopsis>
  90. Pickup a ringing channel.
  91. </synopsis>
  92. <syntax >
  93. <parameter name="channel" argsep="&amp;" required="true">
  94. <argument name="channel" required="true" />
  95. <argument name="channel2" required="false" multiple="true" />
  96. <para>List of channel names or channel uniqueids to pickup if ringing.
  97. For example, a channel name could be <literal>SIP/bob</literal> or
  98. <literal>SIP/bob-00000000</literal> to find
  99. <literal>SIP/bob-00000000</literal>.
  100. </para>
  101. </parameter>
  102. <parameter name="options" required="false">
  103. <optionlist>
  104. <option name="p">
  105. <para>Supplied channel names are prefixes. For example,
  106. <literal>SIP/bob</literal> will match
  107. <literal>SIP/bob-00000000</literal> and
  108. <literal>SIP/bobby-00000000</literal>.
  109. </para>
  110. </option>
  111. </optionlist>
  112. </parameter>
  113. </syntax>
  114. <description>
  115. <para>Pickup a specified <replaceable>channel</replaceable> if ringing.</para>
  116. </description>
  117. </application>
  118. ***/
  119. static const char app[] = "Pickup";
  120. static const char app2[] = "PickupChan";
  121. struct pickup_by_name_args {
  122. /*! Channel attempting to pickup a call. */
  123. struct ast_channel *chan;
  124. /*! Channel uniqueid or partial channel name to match. */
  125. const char *name;
  126. /*! Length of partial channel name to match. */
  127. size_t len;
  128. };
  129. static int find_by_name(void *obj, void *arg, void *data, int flags)
  130. {
  131. struct ast_channel *target = obj;/*!< Potential pickup target */
  132. struct pickup_by_name_args *args = data;
  133. if (args->chan == target) {
  134. /* The channel attempting to pickup a call cannot pickup itself. */
  135. return 0;
  136. }
  137. ast_channel_lock(target);
  138. if (!strncasecmp(ast_channel_name(target), args->name, args->len)
  139. && ast_can_pickup(target)) {
  140. /* Return with the channel still locked on purpose */
  141. return CMP_MATCH | CMP_STOP;
  142. }
  143. ast_channel_unlock(target);
  144. return 0;
  145. }
  146. static int find_by_uniqueid(void *obj, void *arg, void *data, int flags)
  147. {
  148. struct ast_channel *target = obj;/*!< Potential pickup target */
  149. struct pickup_by_name_args *args = data;
  150. if (args->chan == target) {
  151. /* The channel attempting to pickup a call cannot pickup itself. */
  152. return 0;
  153. }
  154. ast_channel_lock(target);
  155. if (!strcasecmp(ast_channel_uniqueid(target), args->name)
  156. && ast_can_pickup(target)) {
  157. /* Return with the channel still locked on purpose */
  158. return CMP_MATCH | CMP_STOP;
  159. }
  160. ast_channel_unlock(target);
  161. return 0;
  162. }
  163. /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
  164. static struct ast_channel *find_by_channel(struct ast_channel *chan, const char *channame)
  165. {
  166. struct ast_channel *target;
  167. char *chkchan;
  168. struct pickup_by_name_args pickup_args;
  169. pickup_args.chan = chan;
  170. if (strchr(channame, '-')) {
  171. /*
  172. * Use the given channel name string as-is. This allows a full channel
  173. * name with a typical sequence number to be used as well as still
  174. * allowing the odd partial channel name that has a '-' in it to still
  175. * work, i.e. Local/bob@en-phone.
  176. */
  177. pickup_args.len = strlen(channame);
  178. pickup_args.name = channame;
  179. } else {
  180. /*
  181. * Append a '-' for the comparison so we check the channel name less
  182. * a sequence number, i.e Find SIP/bob- and not SIP/bobby.
  183. */
  184. pickup_args.len = strlen(channame) + 1;
  185. chkchan = ast_alloca(pickup_args.len + 1);
  186. strcpy(chkchan, channame);/* Safe */
  187. strcat(chkchan, "-");
  188. pickup_args.name = chkchan;
  189. }
  190. target = ast_channel_callback(find_by_name, NULL, &pickup_args, 0);
  191. if (target) {
  192. return target;
  193. }
  194. /* Now try a search for uniqueid. */
  195. pickup_args.name = channame;
  196. pickup_args.len = 0;
  197. return ast_channel_callback(find_by_uniqueid, NULL, &pickup_args, 0);
  198. }
  199. /*! \brief Attempt to pick up named channel. */
  200. static int pickup_by_channel(struct ast_channel *chan, const char *name)
  201. {
  202. int res = -1;
  203. struct ast_channel *target;/*!< Potential pickup target */
  204. /* The found channel is already locked. */
  205. target = find_by_channel(chan, name);
  206. if (target) {
  207. res = ast_do_pickup(chan, target);
  208. ast_channel_unlock(target);
  209. target = ast_channel_unref(target);
  210. }
  211. return res;
  212. }
  213. /* Attempt to pick up specified extension with context */
  214. static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
  215. {
  216. struct ast_channel *target = NULL;/*!< Potential pickup target */
  217. struct ast_channel_iterator *iter;
  218. int res = -1;
  219. if (!(iter = ast_channel_iterator_by_exten_new(exten, context))) {
  220. return -1;
  221. }
  222. while ((target = ast_channel_iterator_next(iter))) {
  223. ast_channel_lock(target);
  224. if ((chan != target) && ast_can_pickup(target)) {
  225. ast_log(LOG_NOTICE, "%s pickup by %s\n", ast_channel_name(target), ast_channel_name(chan));
  226. break;
  227. }
  228. ast_channel_unlock(target);
  229. target = ast_channel_unref(target);
  230. }
  231. ast_channel_iterator_destroy(iter);
  232. if (target) {
  233. res = ast_do_pickup(chan, target);
  234. ast_channel_unlock(target);
  235. target = ast_channel_unref(target);
  236. }
  237. return res;
  238. }
  239. static int find_by_mark(void *obj, void *arg, void *data, int flags)
  240. {
  241. struct ast_channel *target = obj;/*!< Potential pickup target */
  242. struct ast_channel *chan = arg;
  243. const char *mark = data;
  244. const char *tmp;
  245. if (chan == target) {
  246. /* The channel attempting to pickup a call cannot pickup itself. */
  247. return 0;
  248. }
  249. ast_channel_lock(target);
  250. tmp = pbx_builtin_getvar_helper(target, PICKUPMARK);
  251. if (tmp && !strcasecmp(tmp, mark) && ast_can_pickup(target)) {
  252. /* Return with the channel still locked on purpose */
  253. return CMP_MATCH | CMP_STOP;
  254. }
  255. ast_channel_unlock(target);
  256. return 0;
  257. }
  258. /* Attempt to pick up specified mark */
  259. static int pickup_by_mark(struct ast_channel *chan, const char *mark)
  260. {
  261. struct ast_channel *target;/*!< Potential pickup target */
  262. int res = -1;
  263. /* The found channel is already locked. */
  264. target = ast_channel_callback(find_by_mark, chan, (char *) mark, 0);
  265. if (target) {
  266. res = ast_do_pickup(chan, target);
  267. ast_channel_unlock(target);
  268. target = ast_channel_unref(target);
  269. }
  270. return res;
  271. }
  272. static int pickup_by_group(struct ast_channel *chan)
  273. {
  274. struct ast_channel *target;/*!< Potential pickup target */
  275. int res = -1;
  276. /* The found channel is already locked. */
  277. target = ast_pickup_find_by_group(chan);
  278. if (target) {
  279. ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
  280. res = ast_do_pickup(chan, target);
  281. ast_channel_unlock(target);
  282. target = ast_channel_unref(target);
  283. }
  284. return res;
  285. }
  286. /* application entry point for Pickup() */
  287. static int pickup_exec(struct ast_channel *chan, const char *data)
  288. {
  289. char *parse;
  290. char *exten;
  291. char *context;
  292. if (ast_strlen_zero(data)) {
  293. return pickup_by_group(chan) ? 0 : -1;
  294. }
  295. /* Parse extension (and context if there) */
  296. parse = ast_strdupa(data);
  297. for (;;) {
  298. if (ast_strlen_zero(parse)) {
  299. break;
  300. }
  301. exten = strsep(&parse, "&");
  302. if (ast_strlen_zero(exten)) {
  303. continue;
  304. }
  305. context = strchr(exten, '@');
  306. if (context) {
  307. *context++ = '\0';
  308. }
  309. if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
  310. if (!pickup_by_mark(chan, exten)) {
  311. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  312. return -1;
  313. }
  314. } else {
  315. if (ast_strlen_zero(context)) {
  316. context = (char *) ast_channel_context(chan);
  317. }
  318. if (!pickup_by_exten(chan, exten, context)) {
  319. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  320. return -1;
  321. }
  322. }
  323. ast_log(LOG_NOTICE, "No target channel found for %s@%s.\n", exten, context);
  324. }
  325. /* Pickup failed. Keep going in the dialplan. */
  326. return 0;
  327. }
  328. /* Find channel for pick up specified by partial channel name */
  329. static struct ast_channel *find_by_part(struct ast_channel *chan, const char *part)
  330. {
  331. struct ast_channel *target;
  332. struct pickup_by_name_args pickup_args;
  333. pickup_args.chan = chan;
  334. /* Try a partial channel name search. */
  335. pickup_args.name = part;
  336. pickup_args.len = strlen(part);
  337. target = ast_channel_callback(find_by_name, NULL, &pickup_args, 0);
  338. if (target) {
  339. return target;
  340. }
  341. /* Now try a search for uniqueid. */
  342. pickup_args.name = part;
  343. pickup_args.len = 0;
  344. return ast_channel_callback(find_by_uniqueid, NULL, &pickup_args, 0);
  345. }
  346. /* Attempt to pick up specified by partial channel name */
  347. static int pickup_by_part(struct ast_channel *chan, const char *part)
  348. {
  349. struct ast_channel *target;/*!< Potential pickup target */
  350. int res = -1;
  351. /* The found channel is already locked. */
  352. target = find_by_part(chan, part);
  353. if (target) {
  354. res = ast_do_pickup(chan, target);
  355. ast_channel_unlock(target);
  356. target = ast_channel_unref(target);
  357. }
  358. return res;
  359. }
  360. enum OPT_PICKUPCHAN_FLAGS {
  361. OPT_PICKUPCHAN_PARTIAL = (1 << 0), /* Channel name is a partial name. */
  362. };
  363. AST_APP_OPTIONS(pickupchan_opts, BEGIN_OPTIONS
  364. AST_APP_OPTION('p', OPT_PICKUPCHAN_PARTIAL),
  365. END_OPTIONS);
  366. /* application entry point for PickupChan() */
  367. static int pickupchan_exec(struct ast_channel *chan, const char *data)
  368. {
  369. char *pickup = NULL;
  370. char *parse = ast_strdupa(data);
  371. AST_DECLARE_APP_ARGS(args,
  372. AST_APP_ARG(channel);
  373. AST_APP_ARG(options);
  374. AST_APP_ARG(other); /* Any remining unused arguments */
  375. );
  376. struct ast_flags opts;
  377. AST_STANDARD_APP_ARGS(args, parse);
  378. if (ast_strlen_zero(args.channel)) {
  379. ast_log(LOG_WARNING, "PickupChan requires an argument (channel)!\n");
  380. /* Pickup failed. Keep going in the dialplan. */
  381. return 0;
  382. }
  383. if (ast_app_parse_options(pickupchan_opts, &opts, NULL, args.options)) {
  384. /*
  385. * General invalid option syntax.
  386. * Pickup failed. Keep going in the dialplan.
  387. */
  388. return 0;
  389. }
  390. /* Parse channel */
  391. for (;;) {
  392. if (ast_strlen_zero(args.channel)) {
  393. break;
  394. }
  395. pickup = strsep(&args.channel, "&");
  396. if (ast_strlen_zero(pickup)) {
  397. continue;
  398. }
  399. if (ast_test_flag(&opts, OPT_PICKUPCHAN_PARTIAL)) {
  400. if (!pickup_by_part(chan, pickup)) {
  401. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  402. return -1;
  403. }
  404. } else if (!pickup_by_channel(chan, pickup)) {
  405. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  406. return -1;
  407. }
  408. ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
  409. }
  410. /* Pickup failed. Keep going in the dialplan. */
  411. return 0;
  412. }
  413. static int unload_module(void)
  414. {
  415. int res;
  416. res = ast_unregister_application(app);
  417. res |= ast_unregister_application(app2);
  418. return res;
  419. }
  420. static int load_module(void)
  421. {
  422. int res;
  423. res = ast_register_application_xml(app, pickup_exec);
  424. res |= ast_register_application_xml(app2, pickupchan_exec);
  425. return res;
  426. }
  427. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");