res_pjsip_notify.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Kevin Harwell <kharwell@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. /*** MODULEINFO
  19. <depend>pjproject</depend>
  20. <depend>res_pjsip</depend>
  21. <support_level>core</support_level>
  22. ***/
  23. #include "asterisk.h"
  24. #include <pjsip.h>
  25. #include "asterisk/cli.h"
  26. #include "asterisk/config.h"
  27. #include "asterisk/manager.h"
  28. #include "asterisk/module.h"
  29. #include "asterisk/pbx.h"
  30. #include "asterisk/res_pjsip.h"
  31. #include "asterisk/sorcery.h"
  32. /*** DOCUMENTATION
  33. <manager name="PJSIPNotify" language="en_US">
  34. <synopsis>
  35. Send a NOTIFY to either an endpoint or an arbitrary URI.
  36. </synopsis>
  37. <syntax>
  38. <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
  39. <parameter name="Endpoint" required="false">
  40. <para>The endpoint to which to send the NOTIFY.</para>
  41. </parameter>
  42. <parameter name="URI" required="false">
  43. <para>Abritrary URI to which to send the NOTIFY.</para>
  44. </parameter>
  45. <parameter name="Variable" required="true">
  46. <para>Appends variables as headers/content to the NOTIFY. If the variable is
  47. named <literal>Content</literal>, then the value will compose the body
  48. of the message if another variable sets <literal>Content-Type</literal>.
  49. <replaceable>name</replaceable>=<replaceable>value</replaceable></para>
  50. </parameter>
  51. </syntax>
  52. <description>
  53. <para>Sends a NOTIFY to an endpoint or an arbitrary URI.</para>
  54. <para>All parameters for this event must be specified in the body of this
  55. request via multiple <literal>Variable: name=value</literal> sequences.</para>
  56. <note><para>One (and only one) of <literal>Endpoint</literal> or
  57. <literal>URI</literal> must be specified. If <literal>URI</literal> is used,
  58. the default outbound endpoint will be used to send the message. If the default
  59. outbound endpoint isn't configured, this command can not send to an arbitrary
  60. URI.</para></note>
  61. </description>
  62. </manager>
  63. <configInfo name="res_pjsip_notify" language="en_US">
  64. <synopsis>Module that supports sending NOTIFY requests to endpoints from external sources</synopsis>
  65. <configFile name="pjsip_notify.conf">
  66. <configObject name="general">
  67. <synopsis>Unused, but reserved.</synopsis>
  68. </configObject>
  69. <configObject name="notify">
  70. <synopsis>Configuration of a NOTIFY request.</synopsis>
  71. <description>
  72. <para>Each key-value pair in a <literal>notify</literal>
  73. configuration section defines either a SIP header to send
  74. in the request or a line of content in the request message
  75. body. A key of <literal>Content</literal> is treated
  76. as part of the message body and is appended in sequential
  77. order; any other header is treated as part of the SIP
  78. request.</para>
  79. </description>
  80. <configOption name="^.*$">
  81. <synopsis>A key/value pair to add to a NOTIFY request.</synopsis>
  82. <description>
  83. <para>If the key is <literal>Content</literal>,
  84. it will be treated as part of the message body. Otherwise,
  85. it will be added as a header in the NOTIFY request.</para>
  86. <para>The following headers are reserved and cannot be
  87. specified:</para>
  88. <enumlist>
  89. <enum name="Call-ID" />
  90. <enum name="Contact" />
  91. <enum name="CSeq" />
  92. <enum name="To" />
  93. <enum name="From" />
  94. <enum name="Record-Route" />
  95. <enum name="Route" />
  96. <enum name="Via" />
  97. </enumlist>
  98. </description>
  99. </configOption>
  100. </configObject>
  101. </configFile>
  102. </configInfo>
  103. ***/
  104. #define CONTENT_TYPE_SIZE 64
  105. #define CONTENT_SIZE 512
  106. /*!
  107. * \internal
  108. * \brief The configuration file containing NOTIFY payload types to send.
  109. */
  110. static const char notify_config[] = "pjsip_notify.conf";
  111. struct notify_option_item {
  112. const char *name;
  113. const char *value;
  114. char buf[0];
  115. };
  116. struct notify_option {
  117. /*! Contains header and/or content information */
  118. struct ao2_container *items;
  119. /*! The name of the notify option */
  120. char name[0];
  121. };
  122. static int notify_option_hash(const void *obj, int flags)
  123. {
  124. const struct notify_option *option = obj;
  125. return ast_str_case_hash(flags & OBJ_KEY ? obj : option->name);
  126. }
  127. static int notify_option_cmp(void *obj, void *arg, int flags)
  128. {
  129. struct notify_option *option1 = obj;
  130. struct notify_option *option2 = arg;
  131. const char *key = flags & OBJ_KEY ? arg : option2->name;
  132. return strcasecmp(option1->name, key) ? 0 : CMP_MATCH;
  133. }
  134. static void notify_option_destroy(void *obj)
  135. {
  136. struct notify_option *option = obj;
  137. ao2_cleanup(option->items);
  138. }
  139. static void *notify_option_alloc(const char *category)
  140. {
  141. int category_size = strlen(category) + 1;
  142. struct notify_option *option = ao2_alloc(
  143. sizeof(*option) + category_size, notify_option_destroy);
  144. if (!option) {
  145. return NULL;
  146. }
  147. ast_copy_string(option->name, category, category_size);
  148. if (!(option->items = ao2_container_alloc_list(
  149. AO2_ALLOC_OPT_LOCK_NOLOCK,
  150. AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW, NULL, NULL))) {
  151. ao2_cleanup(option);
  152. return NULL;
  153. }
  154. return option;
  155. }
  156. static void *notify_option_find(struct ao2_container *container, const char *category)
  157. {
  158. return ao2_find(container, category, OBJ_KEY);
  159. }
  160. static int notify_option_handler(const struct aco_option *opt,
  161. struct ast_variable *var, void *obj)
  162. {
  163. struct notify_option *option = obj;
  164. int name_size = strlen(var->name) + 1;
  165. int value_size = strlen(var->value) + 1;
  166. RAII_VAR(struct notify_option_item *, item,
  167. ao2_alloc(sizeof(*item) + name_size + value_size,
  168. NULL), ao2_cleanup);
  169. item->name = item->buf;
  170. item->value = item->buf + name_size;
  171. ast_copy_string(item->buf, var->name, name_size);
  172. ast_copy_string(item->buf + name_size, var->value, value_size);
  173. if (!ao2_link(option->items, item)) {
  174. return -1;
  175. }
  176. return 0;
  177. }
  178. struct notify_cfg {
  179. struct ao2_container *notify_options;
  180. };
  181. static void notify_cfg_destroy(void *obj)
  182. {
  183. struct notify_cfg *cfg = obj;
  184. ao2_cleanup(cfg->notify_options);
  185. }
  186. static void *notify_cfg_alloc(void)
  187. {
  188. struct notify_cfg *cfg;
  189. if (!(cfg = ao2_alloc(sizeof(*cfg), notify_cfg_destroy))) {
  190. return NULL;
  191. }
  192. if (!(cfg->notify_options = ao2_container_alloc_options(
  193. AO2_ALLOC_OPT_LOCK_NOLOCK, 20, notify_option_hash,
  194. notify_option_cmp))) {
  195. ao2_cleanup(cfg);
  196. return NULL;
  197. }
  198. return cfg;
  199. }
  200. static struct aco_type notify_option = {
  201. .type = ACO_ITEM,
  202. .name = "notify",
  203. .category_match = ACO_BLACKLIST,
  204. .category = "^general$",
  205. .item_offset = offsetof(struct notify_cfg, notify_options),
  206. .item_alloc = notify_option_alloc,
  207. .item_find = notify_option_find
  208. };
  209. static struct aco_type *notify_options[] = ACO_TYPES(&notify_option);
  210. static struct aco_file module_conf = {
  211. .filename = notify_config,
  212. .types = ACO_TYPES(&notify_option),
  213. };
  214. AO2_GLOBAL_OBJ_STATIC(globals);
  215. CONFIG_INFO_STANDARD(notify_cfg, globals, notify_cfg_alloc,
  216. .files = ACO_FILES(&module_conf)
  217. );
  218. /*!
  219. * \internal
  220. * \brief Structure to hold task data for notifications.
  221. */
  222. struct notify_data {
  223. /*! The endpoint being notified */
  224. struct ast_sip_endpoint *endpoint;
  225. /*! The info of headers, types and content */
  226. void *info;
  227. /*! Function to help build notify request */
  228. void (*build_notify)(pjsip_tx_data *, void *);
  229. };
  230. /*!
  231. * \internal
  232. * \brief Destroy the notify CLI data releasing any resources.
  233. */
  234. static void notify_cli_data_destroy(void *obj)
  235. {
  236. struct notify_data *data = obj;
  237. ao2_cleanup(data->endpoint);
  238. ao2_cleanup(data->info);
  239. }
  240. /*!
  241. * \internal
  242. * \brief Structure to hold task data for notifications (URI variant)
  243. */
  244. struct notify_uri_data {
  245. char *uri;
  246. void *info;
  247. void (*build_notify)(pjsip_tx_data *, void *);
  248. };
  249. static void notify_cli_uri_data_destroy(void *obj)
  250. {
  251. struct notify_uri_data *data = obj;
  252. ast_free(data->uri);
  253. ao2_cleanup(data->info);
  254. }
  255. /*!
  256. * \internal
  257. * \brief Destroy the notify CLI data releasing any resources (URI variant)
  258. */
  259. static void build_cli_notify(pjsip_tx_data *tdata, void *info);
  260. /*!
  261. * \internal
  262. * \brief Construct a notify data object for CLI.
  263. */
  264. static struct notify_data* notify_cli_data_create(
  265. struct ast_sip_endpoint *endpoint, void *info)
  266. {
  267. struct notify_data *data = ao2_alloc(sizeof(*data),
  268. notify_cli_data_destroy);
  269. if (!data) {
  270. return NULL;
  271. }
  272. data->endpoint = endpoint;
  273. ao2_ref(data->endpoint, +1);
  274. data->info = info;
  275. ao2_ref(data->info, +1);
  276. data->build_notify = build_cli_notify;
  277. return data;
  278. }
  279. /*!
  280. * \internal
  281. * \brief Construct a notify URI data object for CLI.
  282. */
  283. static struct notify_uri_data* notify_cli_uri_data_create(
  284. const char *uri, void *info)
  285. {
  286. struct notify_uri_data *data = ao2_alloc(sizeof(*data),
  287. notify_cli_uri_data_destroy);
  288. if (!data) {
  289. return NULL;
  290. }
  291. data->uri = ast_strdup(uri);
  292. if (!data->uri) {
  293. ao2_ref(data, -1);
  294. return NULL;
  295. }
  296. data->info = info;
  297. ao2_ref(data->info, +1);
  298. data->build_notify = build_cli_notify;
  299. return data;
  300. }
  301. /*!
  302. * \internal
  303. * \brief Destroy the notify AMI data releasing any resources.
  304. */
  305. static void notify_ami_data_destroy(void *obj)
  306. {
  307. struct notify_data *data = obj;
  308. struct ast_variable *info = data->info;
  309. ao2_cleanup(data->endpoint);
  310. ast_variables_destroy(info);
  311. }
  312. /*!
  313. * \internal
  314. * \brief Destroy the notify AMI URI data releasing any resources.
  315. */
  316. static void notify_ami_uri_data_destroy(void *obj)
  317. {
  318. struct notify_uri_data *data = obj;
  319. struct ast_variable *info = data->info;
  320. ast_free(data->uri);
  321. ast_variables_destroy(info);
  322. }
  323. static void build_ami_notify(pjsip_tx_data *tdata, void *info);
  324. /*!
  325. * \internal
  326. * \brief Construct a notify data object for AMI.
  327. */
  328. static struct notify_data* notify_ami_data_create(
  329. struct ast_sip_endpoint *endpoint, void *info)
  330. {
  331. struct notify_data *data = ao2_alloc(sizeof(*data),
  332. notify_ami_data_destroy);
  333. if (!data) {
  334. return NULL;
  335. }
  336. data->endpoint = endpoint;
  337. ao2_ref(data->endpoint, +1);
  338. data->info = info;
  339. data->build_notify = build_ami_notify;
  340. return data;
  341. }
  342. /*!
  343. * \internal
  344. * \brief Construct a notify URI data object for AMI.
  345. */
  346. static struct notify_uri_data* notify_ami_uri_data_create(
  347. const char *uri, void *info)
  348. {
  349. struct notify_uri_data *data = ao2_alloc(sizeof(*data),
  350. notify_ami_uri_data_destroy);
  351. if (!data) {
  352. return NULL;
  353. }
  354. data->uri = ast_strdup(uri);
  355. if (!data->uri) {
  356. ao2_ref(data, -1);
  357. return NULL;
  358. }
  359. data->info = info;
  360. data->build_notify = build_ami_notify;
  361. return data;
  362. }
  363. /*!
  364. * \internal
  365. * \brief Checks if the given header name is not allowed.
  366. *
  367. * \details Some headers are not allowed to be set by the user within the
  368. * scope of a NOTIFY request. If the given var header name is
  369. * found in the "not allowed" list then return true.
  370. */
  371. static int not_allowed(const char *name)
  372. {
  373. int i;
  374. static const char *names[] = {
  375. "Call-ID",
  376. "Contact",
  377. "CSeq",
  378. "To",
  379. "From",
  380. "Record-Route",
  381. "Route",
  382. "Request-URI",
  383. "Via",
  384. };
  385. for (i = 0; i < ARRAY_LEN(names); ++i) {
  386. if (!strcasecmp(name, names[i])) {
  387. return 1;
  388. }
  389. }
  390. return 0;
  391. }
  392. /*!
  393. * \internal
  394. * \brief If a content type was specified add it and the content body to the
  395. * NOTIFY request.
  396. */
  397. static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type,
  398. struct ast_str *content)
  399. {
  400. if (content_type) {
  401. char *p;
  402. struct ast_sip_body body;
  403. if (content) {
  404. body.body_text = ast_str_buffer(content);
  405. }
  406. body.type = ast_str_buffer(content_type);
  407. if ((p = strchr(body.type, '/'))) {
  408. *p++ = '\0';
  409. body.subtype = p;
  410. }
  411. ast_sip_add_body(tdata, &body);
  412. }
  413. }
  414. /*!
  415. * \internal
  416. * \brief Build the NOTIFY request adding content or header info.
  417. */
  418. static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value,
  419. struct ast_str **content_type, struct ast_str **content)
  420. {
  421. if (not_allowed(name)) {
  422. ast_log(LOG_WARNING, "Cannot specify %s header, "
  423. "ignoring\n", name);
  424. return;
  425. }
  426. if (!strcasecmp(name, "Content-type")) {
  427. if (!(*content_type)) {
  428. *content_type = ast_str_create(CONTENT_TYPE_SIZE);
  429. }
  430. ast_str_set(content_type, 0,"%s", value);
  431. } else if (!strcasecmp(name, "Content")) {
  432. if (!(*content)) {
  433. *content = ast_str_create(CONTENT_SIZE);
  434. }
  435. if (ast_str_strlen(*content)) {
  436. ast_str_append(content, 0, "\r\n");
  437. }
  438. ast_str_append(content, 0, "%s", value);
  439. } else {
  440. ast_sip_add_header(tdata, name, value);
  441. }
  442. }
  443. /*!
  444. * \internal
  445. * \brief Build the NOTIFY request from CLI info adding header and content
  446. * when specified.
  447. */
  448. static void build_cli_notify(pjsip_tx_data *tdata, void *info)
  449. {
  450. struct notify_option *option = info;
  451. RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
  452. RAII_VAR(struct ast_str *, content, NULL, ast_free);
  453. struct notify_option_item *item;
  454. struct ao2_iterator i = ao2_iterator_init(option->items, 0);
  455. while ((item = ao2_iterator_next(&i))) {
  456. build_notify(tdata, item->name, item->value,
  457. &content_type, &content);
  458. ao2_cleanup(item);
  459. }
  460. ao2_iterator_destroy(&i);
  461. build_notify_body(tdata, content_type, content);
  462. }
  463. /*!
  464. * \internal
  465. * \brief Build the NOTIFY request from AMI info adding header and content
  466. * when specified.
  467. */
  468. static void build_ami_notify(pjsip_tx_data *tdata, void *info)
  469. {
  470. struct ast_variable *vars = info;
  471. RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
  472. RAII_VAR(struct ast_str *, content, NULL, ast_free);
  473. struct ast_variable *i;
  474. for (i = vars; i; i = i->next) {
  475. if (!strcasecmp(i->name, "Content-Length")) {
  476. ast_log(LOG_NOTICE, "It is not necessary to specify Content-Length, ignoring.\n");
  477. continue;
  478. }
  479. build_notify(tdata, i->name, i->value,
  480. &content_type, &content);
  481. }
  482. build_notify_body(tdata, content_type, content);
  483. }
  484. /*!
  485. * \internal
  486. * \brief Build and send a NOTIFY request to a contact.
  487. */
  488. static int notify_contact(void *obj, void *arg, int flags)
  489. {
  490. struct ast_sip_contact *contact = obj;
  491. struct notify_data *data = arg;
  492. pjsip_tx_data *tdata;
  493. if (ast_sip_create_request("NOTIFY", NULL, data->endpoint,
  494. NULL, contact, &tdata)) {
  495. ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
  496. "contact %s\n", contact->uri);
  497. return -1;
  498. }
  499. ast_sip_add_header(tdata, "Subscription-State", "terminated");
  500. data->build_notify(tdata, data->info);
  501. if (ast_sip_send_request(tdata, NULL, data->endpoint, NULL, NULL)) {
  502. ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
  503. "contact %s\n", contact->uri);
  504. return -1;
  505. }
  506. return 0;
  507. }
  508. /*!
  509. * \internal
  510. * \brief Send a NOTIFY request to the endpoint.
  511. *
  512. * \detail Iterates over an endpoint's AORs sending a NOTIFY request
  513. * with the appropriate payload information to each contact.
  514. */
  515. static int notify_endpoint(void *obj)
  516. {
  517. RAII_VAR(struct notify_data *, data, obj, ao2_cleanup);
  518. char *aor_name, *aors;
  519. if (ast_strlen_zero(data->endpoint->aors)) {
  520. ast_log(LOG_WARNING, "Unable to NOTIFY - "
  521. "endpoint has no configured AORs\n");
  522. return -1;
  523. }
  524. aors = ast_strdupa(data->endpoint->aors);
  525. while ((aor_name = strsep(&aors, ","))) {
  526. RAII_VAR(struct ast_sip_aor *, aor,
  527. ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
  528. RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
  529. if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
  530. continue;
  531. }
  532. ao2_callback(contacts, OBJ_NODATA, notify_contact, data);
  533. }
  534. return 0;
  535. }
  536. /*!
  537. * \internal
  538. * \brief Send a notify request to the URI.
  539. */
  540. static int notify_uri(void *obj)
  541. {
  542. RAII_VAR(struct notify_uri_data *, data, obj, ao2_cleanup);
  543. RAII_VAR(struct ast_sip_endpoint *, endpoint,
  544. ast_sip_default_outbound_endpoint(), ao2_cleanup);
  545. pjsip_tx_data *tdata;
  546. if (!endpoint) {
  547. ast_log(LOG_WARNING, "No default outbound endpoint set, can not send "
  548. "NOTIFY requests to arbitrary URIs.\n");
  549. return -1;
  550. }
  551. if (ast_strlen_zero(data->uri)) {
  552. ast_log(LOG_WARNING, "Unable to NOTIFY - URI is blank.\n");
  553. return -1;
  554. }
  555. if (ast_sip_create_request("NOTIFY", NULL, endpoint,
  556. data->uri, NULL, &tdata)) {
  557. ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
  558. "uri %s\n", data->uri);
  559. return -1;
  560. }
  561. ast_sip_add_header(tdata, "Subscription-State", "terminated");
  562. data->build_notify(tdata, data->info);
  563. if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
  564. ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
  565. "uri %s\n", data->uri);
  566. return -1;
  567. }
  568. return 0;
  569. }
  570. enum notify_result {
  571. SUCCESS,
  572. INVALID_ENDPOINT,
  573. ALLOC_ERROR,
  574. TASK_PUSH_ERROR
  575. };
  576. typedef struct notify_data *(*task_data_create)(
  577. struct ast_sip_endpoint *, void *info);
  578. typedef struct notify_uri_data *(*task_uri_data_create)(
  579. const char *uri, void *info);
  580. /*!
  581. * \internal
  582. * \brief Send a NOTIFY request to the endpoint within a threaded task.
  583. */
  584. static enum notify_result push_notify(const char *endpoint_name, void *info,
  585. task_data_create data_create)
  586. {
  587. RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
  588. struct notify_data *data;
  589. if (!(endpoint = ast_sorcery_retrieve_by_id(
  590. ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
  591. return INVALID_ENDPOINT;
  592. }
  593. if (!(data = data_create(endpoint, info))) {
  594. return ALLOC_ERROR;
  595. }
  596. if (ast_sip_push_task(NULL, notify_endpoint, data)) {
  597. ao2_cleanup(data);
  598. return TASK_PUSH_ERROR;
  599. }
  600. return SUCCESS;
  601. }
  602. /*!
  603. * \internal
  604. * \brief Send a NOTIFY request to the URI within an threaded task.
  605. */
  606. static enum notify_result push_notify_uri(const char *uri, void *info,
  607. task_uri_data_create data_create)
  608. {
  609. struct notify_uri_data *data;
  610. if (!(data = data_create(uri, info))) {
  611. return ALLOC_ERROR;
  612. }
  613. if (ast_sip_push_task(NULL, notify_uri, data)) {
  614. ao2_cleanup(data);
  615. return TASK_PUSH_ERROR;
  616. }
  617. return SUCCESS;
  618. }
  619. /*!
  620. * \internal
  621. * \brief Do completion on the endpoint.
  622. */
  623. static char *cli_complete_endpoint(const char *word, int state)
  624. {
  625. char *result = NULL;
  626. int wordlen = strlen(word);
  627. int which = 0;
  628. struct ast_sip_endpoint *endpoint;
  629. RAII_VAR(struct ao2_container *, endpoints,
  630. ast_sip_get_endpoints(), ao2_cleanup);
  631. struct ao2_iterator i = ao2_iterator_init(endpoints, 0);
  632. while ((endpoint = ao2_iterator_next(&i))) {
  633. const char *name = ast_sorcery_object_get_id(endpoint);
  634. if (!strncasecmp(word, name, wordlen) && ++which > state) {
  635. result = ast_strdup(name);
  636. }
  637. ao2_cleanup(endpoint);
  638. if (result) {
  639. break;
  640. }
  641. }
  642. ao2_iterator_destroy(&i);
  643. return result;
  644. }
  645. /*!
  646. * \internal
  647. * \brief Do completion on the notify CLI command.
  648. */
  649. static char *cli_complete_notify(const char *line, const char *word,
  650. int pos, int state, int using_uri)
  651. {
  652. char *c = NULL;
  653. if (pos == 3) {
  654. int which = 0;
  655. int wordlen = strlen(word);
  656. RAII_VAR(struct notify_cfg *, cfg,
  657. ao2_global_obj_ref(globals), ao2_cleanup);
  658. struct notify_option *option;
  659. /* do completion for notify type */
  660. struct ao2_iterator i = ao2_iterator_init(cfg->notify_options, 0);
  661. while ((option = ao2_iterator_next(&i))) {
  662. if (!strncasecmp(word, option->name, wordlen) && ++which > state) {
  663. c = ast_strdup(option->name);
  664. }
  665. ao2_cleanup(option);
  666. if (c) {
  667. break;
  668. }
  669. }
  670. ao2_iterator_destroy(&i);
  671. return c;
  672. }
  673. if (pos == 4) {
  674. int wordlen = strlen(word);
  675. if (ast_strlen_zero(word)) {
  676. if (state == 0) {
  677. c = ast_strdup("endpoint");
  678. } else if (state == 1) {
  679. c = ast_strdup("uri");
  680. }
  681. } else if (state == 0) {
  682. if (!strncasecmp(word, "endpoint", wordlen)) {
  683. c = ast_strdup("endpoint");
  684. } else if (!strncasecmp(word, "uri", wordlen)) {
  685. c = ast_strdup("uri");
  686. }
  687. }
  688. return c;
  689. }
  690. return pos > 4 && !using_uri ? cli_complete_endpoint(word, state) : NULL;
  691. }
  692. /*!
  693. * \internal
  694. * \brief CLI command to send a SIP notify to an endpoint.
  695. *
  696. * \details Attempts to match the "type" given in the CLI command to a
  697. * configured one. If found, sends a NOTIFY to the endpoint
  698. * with the associated payload.
  699. */
  700. static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  701. {
  702. RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
  703. RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
  704. int i;
  705. int using_uri = 0;
  706. switch (cmd) {
  707. case CLI_INIT:
  708. e->command = "pjsip send notify";
  709. e->usage =
  710. "Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
  711. " Send a NOTIFY request to an endpoint\n"
  712. " Message types are defined in sip_notify.conf\n";
  713. return NULL;
  714. case CLI_GENERATE:
  715. if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) {
  716. using_uri = 1;
  717. }
  718. return cli_complete_notify(a->line, a->word, a->pos, a->n, using_uri);
  719. }
  720. if (a->argc < 6) {
  721. return CLI_SHOWUSAGE;
  722. }
  723. if (!strcasecmp(a->argv[4], "uri")) {
  724. using_uri = 1;
  725. } else if (strcasecmp(a->argv[4], "endpoint")) {
  726. return CLI_SHOWUSAGE;
  727. }
  728. cfg = ao2_global_obj_ref(globals);
  729. if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
  730. {
  731. ast_cli(a->fd, "Unable to find notify type '%s'\n",
  732. a->argv[3]);
  733. return CLI_FAILURE;
  734. }
  735. for (i = 5; i < a->argc; ++i) {
  736. ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
  737. a->argv[3], a->argv[i]);
  738. switch (using_uri ? push_notify_uri(a->argv[i], option, notify_cli_uri_data_create) :
  739. push_notify(a->argv[i], option, notify_cli_data_create)) {
  740. case INVALID_ENDPOINT:
  741. ast_cli(a->fd, "Unable to retrieve endpoint %s\n",
  742. a->argv[i]);
  743. break;
  744. case ALLOC_ERROR:
  745. ast_cli(a->fd, "Unable to allocate NOTIFY task data\n");
  746. return CLI_FAILURE;
  747. case TASK_PUSH_ERROR:
  748. ast_cli(a->fd, "Unable to push NOTIFY task\n");
  749. return CLI_FAILURE;
  750. default:
  751. break;
  752. }
  753. }
  754. return CLI_SUCCESS;
  755. }
  756. static struct ast_cli_entry cli_options[] = {
  757. AST_CLI_DEFINE(cli_notify, "Send a NOTIFY request to a SIP endpoint")
  758. };
  759. /*!
  760. * \interanl
  761. * \brief Completes SIPNotify AMI command in Endpoint mode.
  762. */
  763. static void manager_notify_endpoint(struct mansession *s,
  764. const struct message *m, const char *endpoint_name)
  765. {
  766. struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
  767. if (!strncasecmp(endpoint_name, "sip/", 4)) {
  768. endpoint_name += 4;
  769. }
  770. if (!strncasecmp(endpoint_name, "pjsip/", 6)) {
  771. endpoint_name += 6;
  772. }
  773. switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
  774. case INVALID_ENDPOINT:
  775. ast_variables_destroy(vars);
  776. astman_send_error_va(s, m, "Unable to retrieve endpoint %s",
  777. endpoint_name);
  778. break;
  779. case ALLOC_ERROR:
  780. ast_variables_destroy(vars);
  781. astman_send_error(s, m, "Unable to allocate NOTIFY task data");
  782. break;
  783. case TASK_PUSH_ERROR:
  784. /* Don't need to destroy vars since it is handled by cleanup in push_notify */
  785. astman_send_error(s, m, "Unable to push NOTIFY task");
  786. break;
  787. case SUCCESS:
  788. astman_send_ack(s, m, "NOTIFY sent");
  789. break;
  790. }
  791. }
  792. /*!
  793. * \internal
  794. * \brief Completes SIPNotify AMI command in URI mode.
  795. */
  796. static void manager_notify_uri(struct mansession *s,
  797. const struct message *m, const char *uri)
  798. {
  799. struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
  800. switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) {
  801. case INVALID_ENDPOINT:
  802. /* Shouldn't be possible. */
  803. ast_assert(0);
  804. break;
  805. case ALLOC_ERROR:
  806. ast_variables_destroy(vars);
  807. astman_send_error(s, m, "Unable to allocate NOTIFY task data");
  808. break;
  809. case TASK_PUSH_ERROR:
  810. /* Don't need to destroy vars since it is handled by cleanup in push_notify_uri */
  811. astman_send_error(s, m, "Unable to push Notify task");
  812. break;
  813. case SUCCESS:
  814. astman_send_ack(s, m, "NOTIFY sent");
  815. break;
  816. }
  817. }
  818. /*!
  819. * \internal
  820. * \brief AMI entry point to send a SIP notify to an endpoint.
  821. */
  822. static int manager_notify(struct mansession *s, const struct message *m)
  823. {
  824. const char *endpoint_name = astman_get_header(m, "Endpoint");
  825. const char *uri = astman_get_header(m, "URI");
  826. if (!ast_strlen_zero(endpoint_name) && !ast_strlen_zero(uri)) {
  827. astman_send_error(s, m, "PJSIPNotify action can not handle a request specifying "
  828. "both 'URI' and 'Endpoint'. You must use only one of the two.\n");
  829. } else if (!ast_strlen_zero(endpoint_name)) {
  830. manager_notify_endpoint(s, m, endpoint_name);
  831. } else if (!ast_strlen_zero(uri)) {
  832. manager_notify_uri(s, m, uri);
  833. } else {
  834. astman_send_error(s, m, "PJSIPNotify requires either an endpoint name or a SIP URI.");
  835. }
  836. return 0;
  837. }
  838. static int load_module(void)
  839. {
  840. CHECK_PJSIP_MODULE_LOADED();
  841. if (aco_info_init(&notify_cfg)) {
  842. return AST_MODULE_LOAD_DECLINE;
  843. }
  844. aco_option_register_custom(&notify_cfg, "^.*$", ACO_REGEX, notify_options,
  845. "", notify_option_handler, 0);
  846. if (aco_process_config(&notify_cfg, 0)) {
  847. aco_info_destroy(&notify_cfg);
  848. return AST_MODULE_LOAD_DECLINE;
  849. }
  850. ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
  851. ast_manager_register_xml("PJSIPNotify", EVENT_FLAG_SYSTEM, manager_notify);
  852. return AST_MODULE_LOAD_SUCCESS;
  853. }
  854. static int reload_module(void)
  855. {
  856. if (aco_process_config(&notify_cfg, 1) == ACO_PROCESS_ERROR) {
  857. return AST_MODULE_LOAD_DECLINE;
  858. }
  859. return 0;
  860. }
  861. static int unload_module(void)
  862. {
  863. ast_manager_unregister("PJSIPNotify");
  864. ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
  865. aco_info_destroy(&notify_cfg);
  866. return 0;
  867. }
  868. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
  869. .support_level = AST_MODULE_SUPPORT_CORE,
  870. .load = load_module,
  871. .reload = reload_module,
  872. .unload = unload_module,
  873. .load_pri = AST_MODPRI_APP_DEPEND,
  874. );