res_pjsip_notify.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  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 an endpoint.
  36. </synopsis>
  37. <syntax>
  38. <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
  39. <parameter name="Endpoint" required="true">
  40. <para>The endpoint to which to send the NOTIFY.</para>
  41. </parameter>
  42. <parameter name="Variable" required="true">
  43. <para>Appends variables as headers/content to the NOTIFY. If the variable is
  44. named <literal>Content</literal>, then the value will compose the body
  45. of the message if another variable sets <literal>Content-Type</literal>.
  46. <replaceable>name</replaceable>=<replaceable>value</replaceable></para>
  47. </parameter>
  48. </syntax>
  49. <description>
  50. <para>Sends a NOTIFY to an endpoint.</para>
  51. <para>All parameters for this event must be specified in the body of this request
  52. via multiple <literal>Variable: name=value</literal> sequences.</para>
  53. </description>
  54. </manager>
  55. <configInfo name="res_pjsip_notify" language="en_US">
  56. <synopsis>Module that supports sending NOTIFY requests to endpoints from external sources</synopsis>
  57. <configFile name="pjsip_notify.conf">
  58. <configObject name="general">
  59. <synopsis>Unused, but reserved.</synopsis>
  60. </configObject>
  61. <configObject name="notify">
  62. <synopsis>Configuration of a NOTIFY request.</synopsis>
  63. <description>
  64. <para>Each key-value pair in a <literal>notify</literal>
  65. configuration section defines either a SIP header to send
  66. in the request or a line of content in the request message
  67. body. A key of <literal>Content</literal> is treated
  68. as part of the message body and is appended in sequential
  69. order; any other header is treated as part of the SIP
  70. request.</para>
  71. </description>
  72. <configOption name="^.*$">
  73. <synopsis>A key/value pair to add to a NOTIFY request.</synopsis>
  74. <description>
  75. <para>If the key is <literal>Content</literal>,
  76. it will be treated as part of the message body. Otherwise,
  77. it will be added as a header in the NOTIFY request.</para>
  78. <para>The following headers are reserved and cannot be
  79. specified:</para>
  80. <enumlist>
  81. <enum name="Call-ID" />
  82. <enum name="Contact" />
  83. <enum name="CSeq" />
  84. <enum name="To" />
  85. <enum name="From" />
  86. <enum name="Record-Route" />
  87. <enum name="Route" />
  88. <enum name="Via" />
  89. </enumlist>
  90. </description>
  91. </configOption>
  92. </configObject>
  93. </configFile>
  94. </configInfo>
  95. ***/
  96. #define CONTENT_TYPE_SIZE 64
  97. #define CONTENT_SIZE 512
  98. /*!
  99. * \internal
  100. * \brief The configuration file containing NOTIFY payload types to send.
  101. */
  102. static const char notify_config[] = "pjsip_notify.conf";
  103. struct notify_option_item {
  104. const char *name;
  105. const char *value;
  106. char buf[0];
  107. };
  108. struct notify_option {
  109. /*! Contains header and/or content information */
  110. struct ao2_container *items;
  111. /*! The name of the notify option */
  112. char name[0];
  113. };
  114. static int notify_option_hash(const void *obj, int flags)
  115. {
  116. const struct notify_option *option = obj;
  117. return ast_str_case_hash(flags & OBJ_KEY ? obj : option->name);
  118. }
  119. static int notify_option_cmp(void *obj, void *arg, int flags)
  120. {
  121. struct notify_option *option1 = obj;
  122. struct notify_option *option2 = arg;
  123. const char *key = flags & OBJ_KEY ? arg : option2->name;
  124. return strcasecmp(option1->name, key) ? 0 : CMP_MATCH;
  125. }
  126. static void notify_option_destroy(void *obj)
  127. {
  128. struct notify_option *option = obj;
  129. ao2_cleanup(option->items);
  130. }
  131. static void *notify_option_alloc(const char *category)
  132. {
  133. int category_size = strlen(category) + 1;
  134. struct notify_option *option = ao2_alloc(
  135. sizeof(*option) + category_size, notify_option_destroy);
  136. if (!option) {
  137. return NULL;
  138. }
  139. ast_copy_string(option->name, category, category_size);
  140. if (!(option->items = ao2_container_alloc_list(
  141. AO2_ALLOC_OPT_LOCK_NOLOCK,
  142. AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW, NULL, NULL))) {
  143. ao2_cleanup(option);
  144. return NULL;
  145. }
  146. return option;
  147. }
  148. static void *notify_option_find(struct ao2_container *container, const char *category)
  149. {
  150. return ao2_find(container, category, OBJ_KEY);
  151. }
  152. static int notify_option_handler(const struct aco_option *opt,
  153. struct ast_variable *var, void *obj)
  154. {
  155. struct notify_option *option = obj;
  156. int name_size = strlen(var->name) + 1;
  157. int value_size = strlen(var->value) + 1;
  158. RAII_VAR(struct notify_option_item *, item,
  159. ao2_alloc(sizeof(*item) + name_size + value_size,
  160. NULL), ao2_cleanup);
  161. item->name = item->buf;
  162. item->value = item->buf + name_size;
  163. ast_copy_string(item->buf, var->name, name_size);
  164. ast_copy_string(item->buf + name_size, var->value, value_size);
  165. if (!ao2_link(option->items, item)) {
  166. return -1;
  167. }
  168. return 0;
  169. }
  170. struct notify_cfg {
  171. struct ao2_container *notify_options;
  172. };
  173. static void notify_cfg_destroy(void *obj)
  174. {
  175. struct notify_cfg *cfg = obj;
  176. ao2_cleanup(cfg->notify_options);
  177. }
  178. static void *notify_cfg_alloc(void)
  179. {
  180. struct notify_cfg *cfg;
  181. if (!(cfg = ao2_alloc(sizeof(*cfg), notify_cfg_destroy))) {
  182. return NULL;
  183. }
  184. if (!(cfg->notify_options = ao2_container_alloc_options(
  185. AO2_ALLOC_OPT_LOCK_NOLOCK, 20, notify_option_hash,
  186. notify_option_cmp))) {
  187. ao2_cleanup(cfg);
  188. return NULL;
  189. }
  190. return cfg;
  191. }
  192. static struct aco_type notify_option = {
  193. .type = ACO_ITEM,
  194. .name = "notify",
  195. .category_match = ACO_BLACKLIST,
  196. .category = "^general$",
  197. .item_offset = offsetof(struct notify_cfg, notify_options),
  198. .item_alloc = notify_option_alloc,
  199. .item_find = notify_option_find
  200. };
  201. static struct aco_type *notify_options[] = ACO_TYPES(&notify_option);
  202. static struct aco_file module_conf = {
  203. .filename = notify_config,
  204. .types = ACO_TYPES(&notify_option),
  205. };
  206. AO2_GLOBAL_OBJ_STATIC(globals);
  207. CONFIG_INFO_STANDARD(notify_cfg, globals, notify_cfg_alloc,
  208. .files = ACO_FILES(&module_conf)
  209. );
  210. /*!
  211. * \internal
  212. * \brief Structure to hold task data for notifications.
  213. */
  214. struct notify_data {
  215. /*! The endpoint being notified */
  216. struct ast_sip_endpoint *endpoint;
  217. /*! The info of headers, types and content */
  218. void *info;
  219. /*! Function to help build notify request */
  220. void (*build_notify)(pjsip_tx_data *, void *);
  221. };
  222. /*!
  223. * \internal
  224. * \brief Destroy the notify CLI data releasing any resources.
  225. */
  226. static void notify_cli_data_destroy(void *obj)
  227. {
  228. struct notify_data *data = obj;
  229. ao2_cleanup(data->endpoint);
  230. ao2_cleanup(data->info);
  231. }
  232. static void build_cli_notify(pjsip_tx_data *tdata, void *info);
  233. /*!
  234. * \internal
  235. * \brief Construct a notify data object for CLI.
  236. */
  237. static struct notify_data* notify_cli_data_create(
  238. struct ast_sip_endpoint *endpoint, void *info)
  239. {
  240. struct notify_data *data = ao2_alloc(sizeof(*data),
  241. notify_cli_data_destroy);
  242. if (!data) {
  243. return NULL;
  244. }
  245. data->endpoint = endpoint;
  246. ao2_ref(data->endpoint, +1);
  247. data->info = info;
  248. ao2_ref(data->info, +1);
  249. data->build_notify = build_cli_notify;
  250. return data;
  251. }
  252. /*!
  253. * \internal
  254. * \brief Destroy the notify AMI data releasing any resources.
  255. */
  256. static void notify_ami_data_destroy(void *obj)
  257. {
  258. struct notify_data *data = obj;
  259. struct ast_variable *info = data->info;
  260. ao2_cleanup(data->endpoint);
  261. ast_variables_destroy(info);
  262. }
  263. static void build_ami_notify(pjsip_tx_data *tdata, void *info);
  264. /*!
  265. * \internal
  266. * \brief Construct a notify data object for AMI.
  267. */
  268. static struct notify_data* notify_ami_data_create(
  269. struct ast_sip_endpoint *endpoint, void *info)
  270. {
  271. struct notify_data *data = ao2_alloc(sizeof(*data),
  272. notify_ami_data_destroy);
  273. if (!data) {
  274. return NULL;
  275. }
  276. data->endpoint = endpoint;
  277. ao2_ref(data->endpoint, +1);
  278. data->info = info;
  279. data->build_notify = build_ami_notify;
  280. return data;
  281. }
  282. /*!
  283. * \internal
  284. * \brief Checks if the given header name is not allowed.
  285. *
  286. * \details Some headers are not allowed to be set by the user within the
  287. * scope of a NOTIFY request. If the given var header name is
  288. * found in the "not allowed" list then return true.
  289. */
  290. static int not_allowed(const char *name)
  291. {
  292. int i;
  293. static const char *names[] = {
  294. "Call-ID",
  295. "Contact",
  296. "CSeq",
  297. "To",
  298. "From",
  299. "Record-Route",
  300. "Route",
  301. "Request-URI",
  302. "Via",
  303. };
  304. for (i = 0; i < ARRAY_LEN(names); ++i) {
  305. if (!strcasecmp(name, names[i])) {
  306. return 1;
  307. }
  308. }
  309. return 0;
  310. }
  311. /*!
  312. * \internal
  313. * \brief If a content type was specified add it and the content body to the
  314. * NOTIFY request.
  315. */
  316. static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type,
  317. struct ast_str *content)
  318. {
  319. if (content_type) {
  320. char *p;
  321. struct ast_sip_body body;
  322. if (content) {
  323. body.body_text = ast_str_buffer(content);
  324. }
  325. body.type = ast_str_buffer(content_type);
  326. if ((p = strchr(body.type, '/'))) {
  327. *p++ = '\0';
  328. body.subtype = p;
  329. }
  330. ast_sip_add_body(tdata, &body);
  331. }
  332. }
  333. /*!
  334. * \internal
  335. * \brief Build the NOTIFY request adding content or header info.
  336. */
  337. static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value,
  338. struct ast_str **content_type, struct ast_str **content)
  339. {
  340. if (not_allowed(name)) {
  341. ast_log(LOG_WARNING, "Cannot specify %s header, "
  342. "ignoring\n", name);
  343. return;
  344. }
  345. if (!strcasecmp(name, "Content-type")) {
  346. if (!(*content_type)) {
  347. *content_type = ast_str_create(CONTENT_TYPE_SIZE);
  348. }
  349. ast_str_set(content_type, 0,"%s", value);
  350. } else if (!strcasecmp(name, "Content")) {
  351. if (!(*content)) {
  352. *content = ast_str_create(CONTENT_SIZE);
  353. }
  354. if (ast_str_strlen(*content)) {
  355. ast_str_append(content, 0, "\r\n");
  356. }
  357. ast_str_append(content, 0, "%s", value);
  358. } else {
  359. ast_sip_add_header(tdata, name, value);
  360. }
  361. }
  362. /*!
  363. * \internal
  364. * \brief Build the NOTIFY request from CLI info adding header and content
  365. * when specified.
  366. */
  367. static void build_cli_notify(pjsip_tx_data *tdata, void *info)
  368. {
  369. struct notify_option *option = info;
  370. RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
  371. RAII_VAR(struct ast_str *, content, NULL, ast_free);
  372. struct notify_option_item *item;
  373. struct ao2_iterator i = ao2_iterator_init(option->items, 0);
  374. while ((item = ao2_iterator_next(&i))) {
  375. build_notify(tdata, item->name, item->value,
  376. &content_type, &content);
  377. ao2_cleanup(item);
  378. }
  379. ao2_iterator_destroy(&i);
  380. build_notify_body(tdata, content_type, content);
  381. }
  382. /*!
  383. * \internal
  384. * \brief Build the NOTIFY request from AMI info adding header and content
  385. * when specified.
  386. */
  387. static void build_ami_notify(pjsip_tx_data *tdata, void *info)
  388. {
  389. struct ast_variable *vars = info;
  390. RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
  391. RAII_VAR(struct ast_str *, content, NULL, ast_free);
  392. struct ast_variable *i;
  393. for (i = vars; i; i = i->next) {
  394. if (!strcasecmp(i->name, "Content-Length")) {
  395. ast_log(LOG_NOTICE, "It is not necessary to specify Content-Length, ignoring.\n");
  396. continue;
  397. }
  398. build_notify(tdata, i->name, i->value,
  399. &content_type, &content);
  400. }
  401. build_notify_body(tdata, content_type, content);
  402. }
  403. /*!
  404. * \internal
  405. * \brief Build and send a NOTIFY request to a contact.
  406. */
  407. static int notify_contact(void *obj, void *arg, int flags)
  408. {
  409. struct ast_sip_contact *contact = obj;
  410. struct notify_data *data = arg;
  411. pjsip_tx_data *tdata;
  412. if (ast_sip_create_request("NOTIFY", NULL, data->endpoint,
  413. NULL, contact, &tdata)) {
  414. ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
  415. "contact %s\n", contact->uri);
  416. return -1;
  417. }
  418. ast_sip_add_header(tdata, "Subscription-State", "terminated");
  419. data->build_notify(tdata, data->info);
  420. if (ast_sip_send_request(tdata, NULL, data->endpoint, NULL, NULL)) {
  421. ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
  422. "contact %s\n", contact->uri);
  423. return -1;
  424. }
  425. return 0;
  426. }
  427. /*!
  428. * \internal
  429. * \brief Send a NOTIFY request to the endpoint.
  430. *
  431. * \detail Iterates over an endpoint's AORs sending a NOTIFY request
  432. * with the appropriate payload information to each contact.
  433. */
  434. static int notify_endpoint(void *obj)
  435. {
  436. RAII_VAR(struct notify_data *, data, obj, ao2_cleanup);
  437. char *aor_name, *aors;
  438. if (ast_strlen_zero(data->endpoint->aors)) {
  439. ast_log(LOG_WARNING, "Unable to NOTIFY - "
  440. "endpoint has no configured AORs\n");
  441. return -1;
  442. }
  443. aors = ast_strdupa(data->endpoint->aors);
  444. while ((aor_name = strsep(&aors, ","))) {
  445. RAII_VAR(struct ast_sip_aor *, aor,
  446. ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
  447. RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
  448. if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
  449. continue;
  450. }
  451. ao2_callback(contacts, OBJ_NODATA, notify_contact, data);
  452. }
  453. return 0;
  454. }
  455. enum notify_result {
  456. SUCCESS,
  457. INVALID_ENDPOINT,
  458. ALLOC_ERROR,
  459. TASK_PUSH_ERROR
  460. };
  461. typedef struct notify_data *(*task_data_create)(
  462. struct ast_sip_endpoint *, void *info);
  463. /*!
  464. * \internal
  465. * \brief Send a NOTIFY request to the endpoint within a threaded task.
  466. */
  467. static enum notify_result push_notify(const char *endpoint_name, void *info,
  468. task_data_create data_create)
  469. {
  470. RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
  471. struct notify_data *data;
  472. if (!(endpoint = ast_sorcery_retrieve_by_id(
  473. ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
  474. return INVALID_ENDPOINT;
  475. }
  476. if (!(data = data_create(endpoint, info))) {
  477. return ALLOC_ERROR;
  478. }
  479. if (ast_sip_push_task(NULL, notify_endpoint, data)) {
  480. ao2_cleanup(data);
  481. return TASK_PUSH_ERROR;
  482. }
  483. return SUCCESS;
  484. }
  485. /*!
  486. * \internal
  487. * \brief Do completion on the endpoint.
  488. */
  489. static char *cli_complete_endpoint(const char *word, int state)
  490. {
  491. char *result = NULL;
  492. int wordlen = strlen(word);
  493. int which = 0;
  494. struct ast_sip_endpoint *endpoint;
  495. RAII_VAR(struct ao2_container *, endpoints,
  496. ast_sip_get_endpoints(), ao2_cleanup);
  497. struct ao2_iterator i = ao2_iterator_init(endpoints, 0);
  498. while ((endpoint = ao2_iterator_next(&i))) {
  499. const char *name = ast_sorcery_object_get_id(endpoint);
  500. if (!strncasecmp(word, name, wordlen) && ++which > state) {
  501. result = ast_strdup(name);
  502. }
  503. ao2_cleanup(endpoint);
  504. if (result) {
  505. break;
  506. }
  507. }
  508. ao2_iterator_destroy(&i);
  509. return result;
  510. }
  511. /*!
  512. * \internal
  513. * \brief Do completion on the notify CLI command.
  514. */
  515. static char *cli_complete_notify(const char *line, const char *word,
  516. int pos, int state)
  517. {
  518. char *c = NULL;
  519. if (pos == 3) {
  520. int which = 0;
  521. int wordlen = strlen(word);
  522. RAII_VAR(struct notify_cfg *, cfg,
  523. ao2_global_obj_ref(globals), ao2_cleanup);
  524. struct notify_option *option;
  525. /* do completion for notify type */
  526. struct ao2_iterator i = ao2_iterator_init(cfg->notify_options, 0);
  527. while ((option = ao2_iterator_next(&i))) {
  528. if (!strncasecmp(word, option->name, wordlen) && ++which > state) {
  529. c = ast_strdup(option->name);
  530. }
  531. ao2_cleanup(option);
  532. if (c) {
  533. break;
  534. }
  535. }
  536. ao2_iterator_destroy(&i);
  537. return c;
  538. }
  539. return pos > 3 ? cli_complete_endpoint(word, state) : NULL;
  540. }
  541. /*!
  542. * \internal
  543. * \brief CLI command to send a SIP notify to an endpoint.
  544. *
  545. * \details Attempts to match the "type" given in the CLI command to a
  546. * configured one. If found, sends a NOTIFY to the endpoint
  547. * with the associated payload.
  548. */
  549. static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  550. {
  551. RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
  552. RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
  553. int i;
  554. switch (cmd) {
  555. case CLI_INIT:
  556. e->command = "pjsip send notify";
  557. e->usage =
  558. "Usage: pjsip send notify <type> <peer> [<peer>...]\n"
  559. " Send a NOTIFY request to an endpoint\n"
  560. " Message types are defined in sip_notify.conf\n";
  561. return NULL;
  562. case CLI_GENERATE:
  563. return cli_complete_notify(a->line, a->word, a->pos, a->n);
  564. }
  565. if (a->argc < 5) {
  566. return CLI_SHOWUSAGE;
  567. }
  568. cfg = ao2_global_obj_ref(globals);
  569. if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
  570. {
  571. ast_cli(a->fd, "Unable to find notify type '%s'\n",
  572. a->argv[3]);
  573. return CLI_FAILURE;
  574. }
  575. for (i = 4; i < a->argc; ++i) {
  576. ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
  577. a->argv[3], a->argv[i]);
  578. switch (push_notify(a->argv[i], option,
  579. notify_cli_data_create)) {
  580. case INVALID_ENDPOINT:
  581. ast_cli(a->fd, "Unable to retrieve endpoint %s\n",
  582. a->argv[i]);
  583. break;
  584. case ALLOC_ERROR:
  585. ast_cli(a->fd, "Unable to allocate NOTIFY task data\n");
  586. return CLI_FAILURE;
  587. case TASK_PUSH_ERROR:
  588. ast_cli(a->fd, "Unable to push NOTIFY task\n");
  589. return CLI_FAILURE;
  590. default:
  591. break;
  592. }
  593. }
  594. return CLI_SUCCESS;
  595. }
  596. static struct ast_cli_entry cli_options[] = {
  597. AST_CLI_DEFINE(cli_notify, "Send a NOTIFY request to a SIP endpoint")
  598. };
  599. /*!
  600. * \internal
  601. * \brief AMI entry point to send a SIP notify to an endpoint.
  602. */
  603. static int manager_notify(struct mansession *s, const struct message *m)
  604. {
  605. const char *endpoint_name = astman_get_header(m, "Endpoint");
  606. struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
  607. if (ast_strlen_zero(endpoint_name)) {
  608. astman_send_error(s, m, "PJSIPNotify requires an endpoint name");
  609. ast_variables_destroy(vars);
  610. return 0;
  611. }
  612. if (!strncasecmp(endpoint_name, "sip/", 4)) {
  613. endpoint_name += 4;
  614. }
  615. if (!strncasecmp(endpoint_name, "pjsip/", 6)) {
  616. endpoint_name += 6;
  617. }
  618. switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
  619. case INVALID_ENDPOINT:
  620. ast_variables_destroy(vars);
  621. astman_send_error_va(s, m, "Unable to retrieve endpoint %s\n",
  622. endpoint_name);
  623. break;
  624. case ALLOC_ERROR:
  625. ast_variables_destroy(vars);
  626. astman_send_error(s, m, "Unable to allocate NOTIFY task data\n");
  627. break;
  628. case TASK_PUSH_ERROR:
  629. /* Don't need to destroy vars since it is handled by cleanup in push_notify */
  630. astman_send_error(s, m, "Unable to push NOTIFY task\n");
  631. break;
  632. case SUCCESS:
  633. astman_send_ack(s, m, "NOTIFY sent");
  634. break;
  635. }
  636. return 0;
  637. }
  638. static int load_module(void)
  639. {
  640. CHECK_PJSIP_MODULE_LOADED();
  641. if (aco_info_init(&notify_cfg)) {
  642. return AST_MODULE_LOAD_DECLINE;
  643. }
  644. aco_option_register_custom(&notify_cfg, "^.*$", ACO_REGEX, notify_options,
  645. "", notify_option_handler, 0);
  646. if (aco_process_config(&notify_cfg, 0)) {
  647. aco_info_destroy(&notify_cfg);
  648. return AST_MODULE_LOAD_DECLINE;
  649. }
  650. ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
  651. ast_manager_register_xml("PJSIPNotify", EVENT_FLAG_SYSTEM, manager_notify);
  652. return AST_MODULE_LOAD_SUCCESS;
  653. }
  654. static int reload_module(void)
  655. {
  656. if (aco_process_config(&notify_cfg, 1) == ACO_PROCESS_ERROR) {
  657. return AST_MODULE_LOAD_DECLINE;
  658. }
  659. return 0;
  660. }
  661. static int unload_module(void)
  662. {
  663. ast_manager_unregister("PJSIPNotify");
  664. ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
  665. aco_info_destroy(&notify_cfg);
  666. return 0;
  667. }
  668. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
  669. .load = load_module,
  670. .reload = reload_module,
  671. .unload = unload_module,
  672. .load_pri = AST_MODPRI_APP_DEPEND,
  673. );