app_directed_pickup.c 12 KB

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