app_directed_pickup.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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="Technology/Resource" argsep="&amp;" required="true">
  94. <argument name="Technology/Resource" required="true" />
  95. <argument name="Technology2/Resource2" required="false" multiple="true" />
  96. </parameter>
  97. <parameter name="options" required="false">
  98. <optionlist>
  99. <option name="p">
  100. <para>Channel name specified partial name. Used when find channel by callid.</para>
  101. </option>
  102. </optionlist>
  103. </parameter>
  104. </syntax>
  105. <description>
  106. <para>This will pickup a specified <replaceable>channel</replaceable> if ringing.</para>
  107. </description>
  108. </application>
  109. ***/
  110. static const char app[] = "Pickup";
  111. static const char app2[] = "PickupChan";
  112. struct pickup_by_name_args {
  113. const char *name;
  114. size_t len;
  115. };
  116. static int pickup_by_name_cb(void *obj, void *arg, void *data, int flags)
  117. {
  118. struct ast_channel *target = obj;/*!< Potential pickup target */
  119. struct pickup_by_name_args *args = data;
  120. ast_channel_lock(target);
  121. if (!strncasecmp(ast_channel_name(target), args->name, args->len) && ast_can_pickup(target)) {
  122. /* Return with the channel still locked on purpose */
  123. return CMP_MATCH | CMP_STOP;
  124. }
  125. ast_channel_unlock(target);
  126. return 0;
  127. }
  128. /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
  129. static struct ast_channel *my_ast_get_channel_by_name_locked(const char *channame)
  130. {
  131. char *chkchan;
  132. struct pickup_by_name_args pickup_args;
  133. /* Check if channel name contains a '-'.
  134. * In this case the channel name will be interpreted as full channel name.
  135. */
  136. if (strchr(channame, '-')) {
  137. /* check full channel name */
  138. pickup_args.len = strlen(channame);
  139. pickup_args.name = channame;
  140. } else {
  141. /* need to append a '-' for the comparison so we check full channel name,
  142. * i.e SIP/hgc- , use a temporary variable so original stays the same for
  143. * debugging.
  144. */
  145. pickup_args.len = strlen(channame) + 1;
  146. chkchan = ast_alloca(pickup_args.len + 1);
  147. strcpy(chkchan, channame);
  148. strcat(chkchan, "-");
  149. pickup_args.name = chkchan;
  150. }
  151. return ast_channel_callback(pickup_by_name_cb, NULL, &pickup_args, 0);
  152. }
  153. /*! \brief Attempt to pick up named channel, does not use context */
  154. static int pickup_by_channel(struct ast_channel *chan, char *pickup)
  155. {
  156. int res = -1;
  157. struct ast_channel *target;/*!< Potential pickup target */
  158. target = my_ast_get_channel_by_name_locked(pickup);
  159. if (target) {
  160. /* Just check that we are not picking up the SAME as target. (i.e. ourself) */
  161. if (chan != target) {
  162. res = ast_do_pickup(chan, target);
  163. }
  164. ast_channel_unlock(target);
  165. target = ast_channel_unref(target);
  166. }
  167. return res;
  168. }
  169. /* Attempt to pick up specified extension with context */
  170. static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
  171. {
  172. struct ast_channel *target = NULL;/*!< Potential pickup target */
  173. struct ast_channel_iterator *iter;
  174. int res = -1;
  175. if (!(iter = ast_channel_iterator_by_exten_new(exten, context))) {
  176. return -1;
  177. }
  178. while ((target = ast_channel_iterator_next(iter))) {
  179. ast_channel_lock(target);
  180. if ((chan != target) && ast_can_pickup(target)) {
  181. ast_log(LOG_NOTICE, "%s pickup by %s\n", ast_channel_name(target), ast_channel_name(chan));
  182. break;
  183. }
  184. ast_channel_unlock(target);
  185. target = ast_channel_unref(target);
  186. }
  187. ast_channel_iterator_destroy(iter);
  188. if (target) {
  189. res = ast_do_pickup(chan, target);
  190. ast_channel_unlock(target);
  191. target = ast_channel_unref(target);
  192. }
  193. return res;
  194. }
  195. static int find_by_mark(void *obj, void *arg, void *data, int flags)
  196. {
  197. struct ast_channel *target = obj;/*!< Potential pickup target */
  198. const char *mark = data;
  199. const char *tmp;
  200. ast_channel_lock(target);
  201. tmp = pbx_builtin_getvar_helper(target, PICKUPMARK);
  202. if (tmp && !strcasecmp(tmp, mark) && ast_can_pickup(target)) {
  203. /* Return with the channel still locked on purpose */
  204. return CMP_MATCH | CMP_STOP;
  205. }
  206. ast_channel_unlock(target);
  207. return 0;
  208. }
  209. /* Attempt to pick up specified mark */
  210. static int pickup_by_mark(struct ast_channel *chan, const char *mark)
  211. {
  212. struct ast_channel *target;/*!< Potential pickup target */
  213. int res = -1;
  214. /* The found channel is already locked. */
  215. target = ast_channel_callback(find_by_mark, NULL, (char *) mark, 0);
  216. if (target) {
  217. res = ast_do_pickup(chan, target);
  218. ast_channel_unlock(target);
  219. target = ast_channel_unref(target);
  220. }
  221. return res;
  222. }
  223. static int pickup_by_group(struct ast_channel *chan)
  224. {
  225. struct ast_channel *target;/*!< Potential pickup target */
  226. int res = -1;
  227. /* The found channel is already locked. */
  228. target = ast_pickup_find_by_group(chan);
  229. if (target) {
  230. ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
  231. res = ast_do_pickup(chan, target);
  232. ast_channel_unlock(target);
  233. target = ast_channel_unref(target);
  234. }
  235. return res;
  236. }
  237. /* application entry point for Pickup() */
  238. static int pickup_exec(struct ast_channel *chan, const char *data)
  239. {
  240. char *tmp;
  241. char *exten;
  242. char *context;
  243. if (ast_strlen_zero(data)) {
  244. return pickup_by_group(chan) ? 0 : -1;
  245. }
  246. /* Parse extension (and context if there) */
  247. tmp = ast_strdupa(data);
  248. while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
  249. if ((context = strchr(exten, '@')))
  250. *context++ = '\0';
  251. if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
  252. if (!pickup_by_mark(chan, exten)) {
  253. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  254. return -1;
  255. }
  256. } else {
  257. if (ast_strlen_zero(context)) {
  258. context = (char *) ast_channel_context(chan);
  259. }
  260. if (!pickup_by_exten(chan, exten, context)) {
  261. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  262. return -1;
  263. }
  264. }
  265. ast_log(LOG_NOTICE, "No target channel found for %s@%s.\n", exten, context);
  266. }
  267. /* Pickup failed. Keep going in the dialplan. */
  268. return 0;
  269. }
  270. /* Find channel for pick up specified by partial channel name */
  271. static int find_by_part(void *obj, void *arg, void *data, int flags)
  272. {
  273. struct ast_channel *target = obj;/*!< Potential pickup target */
  274. const char *part = data;
  275. int len = strlen(part);
  276. ast_channel_lock(target);
  277. if (len <= strlen(ast_channel_name(target)) && !strncmp(ast_channel_name(target), part, len)
  278. && ast_can_pickup(target)) {
  279. /* Return with the channel still locked on purpose */
  280. return CMP_MATCH | CMP_STOP;
  281. }
  282. ast_channel_unlock(target);
  283. return 0;
  284. }
  285. /* Attempt to pick up specified by partial channel name */
  286. static int pickup_by_part(struct ast_channel *chan, const char *part)
  287. {
  288. struct ast_channel *target;/*!< Potential pickup target */
  289. int res = -1;
  290. /* The found channel is already locked. */
  291. target = ast_channel_callback(find_by_part, NULL, (char *) part, 0);
  292. if (target) {
  293. res = ast_do_pickup(chan, target);
  294. ast_channel_unlock(target);
  295. target = ast_channel_unref(target);
  296. }
  297. return res;
  298. }
  299. /* application entry point for PickupChan() */
  300. static int pickupchan_exec(struct ast_channel *chan, const char *data)
  301. {
  302. int partial_pickup = 0;
  303. char *pickup = NULL;
  304. char *parse = ast_strdupa(data);
  305. AST_DECLARE_APP_ARGS(args,
  306. AST_APP_ARG(channel);
  307. AST_APP_ARG(options);
  308. );
  309. AST_STANDARD_APP_ARGS(args, parse);
  310. if (ast_strlen_zero(args.channel)) {
  311. ast_log(LOG_WARNING, "PickupChan requires an argument (channel)!\n");
  312. /* Pickup failed. Keep going in the dialplan. */
  313. return 0;
  314. }
  315. if (!ast_strlen_zero(args.options) && strchr(args.options, 'p')) {
  316. partial_pickup = 1;
  317. }
  318. /* Parse channel */
  319. while (!ast_strlen_zero(args.channel) && (pickup = strsep(&args.channel, "&"))) {
  320. if (!strncasecmp(ast_channel_name(chan), pickup, strlen(pickup))) {
  321. ast_log(LOG_NOTICE, "Cannot pickup your own channel %s.\n", pickup);
  322. } else {
  323. if (partial_pickup) {
  324. if (!pickup_by_part(chan, pickup)) {
  325. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  326. return -1;
  327. }
  328. } else if (!pickup_by_channel(chan, pickup)) {
  329. /* Pickup successful. Stop the dialplan this channel is a zombie. */
  330. return -1;
  331. }
  332. ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
  333. }
  334. }
  335. /* Pickup failed. Keep going in the dialplan. */
  336. return 0;
  337. }
  338. static int unload_module(void)
  339. {
  340. int res;
  341. res = ast_unregister_application(app);
  342. res |= ast_unregister_application(app2);
  343. return res;
  344. }
  345. static int load_module(void)
  346. {
  347. int res;
  348. res = ast_register_application_xml(app, pickup_exec);
  349. res |= ast_register_application_xml(app2, pickupchan_exec);
  350. return res;
  351. }
  352. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");