func_cdr.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999-2006, Digium, Inc.
  5. *
  6. * Portions Copyright (C) 2005, Anthony Minessale II
  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. /*! \file
  19. *
  20. * \brief Call Detail Record related dialplan functions
  21. *
  22. * \author Anthony Minessale II
  23. *
  24. * \ingroup functions
  25. */
  26. /*** MODULEINFO
  27. <support_level>core</support_level>
  28. ***/
  29. #include "asterisk.h"
  30. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  31. #include "asterisk/module.h"
  32. #include "asterisk/channel.h"
  33. #include "asterisk/pbx.h"
  34. #include "asterisk/utils.h"
  35. #include "asterisk/app.h"
  36. #include "asterisk/cdr.h"
  37. #include "asterisk/stasis.h"
  38. #include "asterisk/stasis_message_router.h"
  39. /*** DOCUMENTATION
  40. <function name="CDR" language="en_US">
  41. <synopsis>
  42. Gets or sets a CDR variable.
  43. </synopsis>
  44. <syntax>
  45. <parameter name="name" required="true">
  46. <para>CDR field name:</para>
  47. <enumlist>
  48. <enum name="clid">
  49. <para>Caller ID.</para>
  50. </enum>
  51. <enum name="lastdata">
  52. <para>Last application arguments.</para>
  53. </enum>
  54. <enum name="disposition">
  55. <para>The final state of the CDR.</para>
  56. <enumlist>
  57. <enum name="0">
  58. <para><literal>NO ANSWER</literal></para>
  59. </enum>
  60. <enum name="1">
  61. <para><literal>NO ANSWER</literal> (NULL record)</para>
  62. </enum>
  63. <enum name="2">
  64. <para><literal>FAILED</literal></para>
  65. </enum>
  66. <enum name="4">
  67. <para><literal>BUSY</literal></para>
  68. </enum>
  69. <enum name="8">
  70. <para><literal>ANSWERED</literal></para>
  71. </enum>
  72. <enum name="16">
  73. <para><literal>CONGESTION</literal></para>
  74. </enum>
  75. </enumlist>
  76. </enum>
  77. <enum name="src">
  78. <para>Source.</para>
  79. </enum>
  80. <enum name="start">
  81. <para>Time the call started.</para>
  82. </enum>
  83. <enum name="amaflags">
  84. <para>R/W the Automatic Message Accounting (AMA) flags on the channel.
  85. When read from a channel, the integer value will always be returned.
  86. When written to a channel, both the string format or integer value
  87. is accepted.</para>
  88. <enumlist>
  89. <enum name="1"><para><literal>OMIT</literal></para></enum>
  90. <enum name="2"><para><literal>BILLING</literal></para></enum>
  91. <enum name="3"><para><literal>DOCUMENTATION</literal></para></enum>
  92. </enumlist>
  93. <warning><para>Accessing this setting is deprecated in CDR. Please use the CHANNEL function instead.</para></warning>
  94. </enum>
  95. <enum name="dst">
  96. <para>Destination.</para>
  97. </enum>
  98. <enum name="answer">
  99. <para>Time the call was answered.</para>
  100. </enum>
  101. <enum name="accountcode">
  102. <para>The channel's account code.</para>
  103. <warning><para>Accessing this setting is deprecated in CDR. Please use the CHANNEL function instead.</para></warning>
  104. </enum>
  105. <enum name="dcontext">
  106. <para>Destination context.</para>
  107. </enum>
  108. <enum name="end">
  109. <para>Time the call ended.</para>
  110. </enum>
  111. <enum name="uniqueid">
  112. <para>The channel's unique id.</para>
  113. </enum>
  114. <enum name="dstchannel">
  115. <para>Destination channel.</para>
  116. </enum>
  117. <enum name="duration">
  118. <para>Duration of the call.</para>
  119. </enum>
  120. <enum name="userfield">
  121. <para>The channel's user specified field.</para>
  122. </enum>
  123. <enum name="lastapp">
  124. <para>Last application.</para>
  125. </enum>
  126. <enum name="billsec">
  127. <para>Duration of the call once it was answered.</para>
  128. </enum>
  129. <enum name="channel">
  130. <para>Channel name.</para>
  131. </enum>
  132. <enum name="sequence">
  133. <para>CDR sequence number.</para>
  134. </enum>
  135. </enumlist>
  136. </parameter>
  137. <parameter name="options" required="false">
  138. <optionlist>
  139. <option name="f">
  140. <para>Returns billsec or duration fields as floating point values.</para>
  141. </option>
  142. <option name="u">
  143. <para>Retrieves the raw, unprocessed value.</para>
  144. <para>For example, 'start', 'answer', and 'end' will be retrieved as epoch
  145. values, when the <literal>u</literal> option is passed, but formatted as YYYY-MM-DD HH:MM:SS
  146. otherwise. Similarly, disposition and amaflags will return their raw
  147. integral values.</para>
  148. </option>
  149. </optionlist>
  150. </parameter>
  151. </syntax>
  152. <description>
  153. <para>All of the CDR field names are read-only, except for <literal>accountcode</literal>,
  154. <literal>userfield</literal>, and <literal>amaflags</literal>. You may, however, supply
  155. a name not on the above list, and create your own variable, whose value can be changed
  156. with this function, and this variable will be stored on the CDR.</para>
  157. <note><para>CDRs can only be modified before the bridge between two channels is
  158. torn down. For example, CDRs may not be modified after the <literal>Dial</literal>
  159. application has returned.</para></note>
  160. <para>Example: exten => 1,1,Set(CDR(userfield)=test)</para>
  161. </description>
  162. </function>
  163. <function name="CDR_PROP" language="en_US">
  164. <synopsis>
  165. Set a property on a channel's CDR.
  166. </synopsis>
  167. <syntax>
  168. <parameter name="name" required="true">
  169. <para>The property to set on the CDR.</para>
  170. <enumlist>
  171. <enum name="party_a">
  172. <para>Set this channel as the preferred Party A when
  173. channels are associated together.</para>
  174. <para>Write-Only</para>
  175. </enum>
  176. <enum name="disable">
  177. <para>Setting to 1 will disable CDRs for this channel.
  178. Setting to 0 will enable CDRs for this channel.</para>
  179. <para>Write-Only</para>
  180. </enum>
  181. </enumlist>
  182. </parameter>
  183. </syntax>
  184. <description>
  185. <para>This function sets a property on a channel's CDR. Properties
  186. alter the behavior of how the CDR operates for that channel.</para>
  187. </description>
  188. </function>
  189. ***/
  190. enum cdr_option_flags {
  191. OPT_UNPARSED = (1 << 1),
  192. OPT_FLOAT = (1 << 2),
  193. };
  194. AST_APP_OPTIONS(cdr_func_options, {
  195. AST_APP_OPTION('f', OPT_FLOAT),
  196. AST_APP_OPTION('u', OPT_UNPARSED),
  197. });
  198. struct cdr_func_payload {
  199. struct ast_channel *chan;
  200. const char *cmd;
  201. const char *arguments;
  202. const char *value;
  203. void *data;
  204. };
  205. struct cdr_func_data {
  206. char *buf;
  207. size_t len;
  208. };
  209. STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_read_message_type);
  210. STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_write_message_type);
  211. STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_prop_write_message_type);
  212. static struct timeval cdr_retrieve_time(struct ast_channel *chan, const char *time_name)
  213. {
  214. struct timeval time = { 0 };
  215. char *value = NULL;
  216. char tempbuf[128];
  217. long int tv_sec;
  218. long int tv_usec;
  219. if (ast_strlen_zero(ast_channel_name(chan))) {
  220. /* Format request on a dummy channel */
  221. ast_cdr_format_var(ast_channel_cdr(chan), time_name, &value, tempbuf, sizeof(tempbuf), 1);
  222. } else {
  223. ast_cdr_getvar(ast_channel_name(chan), time_name, tempbuf, sizeof(tempbuf));
  224. }
  225. /* time.tv_usec is suseconds_t, which could be int or long */
  226. if (sscanf(tempbuf, "%ld.%ld", &tv_sec, &tv_usec) == 2) {
  227. time.tv_sec = tv_sec;
  228. time.tv_usec = tv_usec;
  229. } else {
  230. ast_log(AST_LOG_WARNING, "Failed to fully extract '%s' from CDR\n", time_name);
  231. }
  232. return time;
  233. }
  234. static void cdr_read_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
  235. {
  236. struct cdr_func_payload *payload = stasis_message_data(message);
  237. struct cdr_func_data *output;
  238. char *info;
  239. char *value = NULL;
  240. struct ast_flags flags = { 0 };
  241. char tempbuf[512];
  242. AST_DECLARE_APP_ARGS(args,
  243. AST_APP_ARG(variable);
  244. AST_APP_ARG(options);
  245. );
  246. if (cdr_read_message_type() != stasis_message_type(message)) {
  247. return;
  248. }
  249. ast_assert(payload != NULL);
  250. output = payload->data;
  251. ast_assert(output != NULL);
  252. if (ast_strlen_zero(payload->arguments)) {
  253. ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable[,option]))\n)",
  254. payload->cmd, payload->cmd);
  255. return;
  256. }
  257. info = ast_strdupa(payload->arguments);
  258. AST_STANDARD_APP_ARGS(args, info);
  259. if (!ast_strlen_zero(args.options)) {
  260. ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
  261. }
  262. if (ast_strlen_zero(ast_channel_name(payload->chan))) {
  263. /* Format request on a dummy channel */
  264. ast_cdr_format_var(ast_channel_cdr(payload->chan), args.variable, &value, tempbuf, sizeof(tempbuf), ast_test_flag(&flags, OPT_UNPARSED));
  265. if (ast_strlen_zero(value)) {
  266. return;
  267. }
  268. ast_copy_string(tempbuf, value, sizeof(tempbuf));
  269. ast_set_flag(&flags, OPT_UNPARSED);
  270. } else if (ast_cdr_getvar(ast_channel_name(payload->chan), args.variable, tempbuf, sizeof(tempbuf))) {
  271. return;
  272. }
  273. if (ast_test_flag(&flags, OPT_FLOAT)
  274. && (!strcasecmp("billsec", args.variable) || !strcasecmp("duration", args.variable))) {
  275. struct timeval start = cdr_retrieve_time(payload->chan, !strcasecmp("billsec", args.variable) ? "answer" : "start");
  276. struct timeval finish = cdr_retrieve_time(payload->chan, "end");
  277. double delta;
  278. if (ast_tvzero(finish)) {
  279. finish = ast_tvnow();
  280. }
  281. if (ast_tvzero(start)) {
  282. delta = 0.0;
  283. } else {
  284. delta = (double)(ast_tvdiff_us(finish, start) / 1000000.0);
  285. }
  286. snprintf(tempbuf, sizeof(tempbuf), "%lf", delta);
  287. } else if (!ast_test_flag(&flags, OPT_UNPARSED)) {
  288. if (!strcasecmp("start", args.variable)
  289. || !strcasecmp("end", args.variable)
  290. || !strcasecmp("answer", args.variable)) {
  291. struct timeval fmt_time;
  292. struct ast_tm tm;
  293. /* tv_usec is suseconds_t, which could be int or long */
  294. long int tv_sec;
  295. long int tv_usec;
  296. if (sscanf(tempbuf, "%ld.%ld", &tv_sec, &tv_usec) != 2) {
  297. ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
  298. args.variable, tempbuf, ast_channel_name(payload->chan));
  299. return;
  300. }
  301. if (tv_sec) {
  302. fmt_time.tv_sec = tv_sec;
  303. fmt_time.tv_usec = tv_usec;
  304. ast_localtime(&fmt_time, &tm, NULL);
  305. ast_strftime(tempbuf, sizeof(tempbuf), "%Y-%m-%d %T", &tm);
  306. } else {
  307. tempbuf[0] = '\0';
  308. }
  309. } else if (!strcasecmp("disposition", args.variable)) {
  310. int disposition;
  311. if (sscanf(tempbuf, "%8d", &disposition) != 1) {
  312. ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
  313. args.variable, tempbuf, ast_channel_name(payload->chan));
  314. return;
  315. }
  316. snprintf(tempbuf, sizeof(tempbuf), "%s", ast_cdr_disp2str(disposition));
  317. } else if (!strcasecmp("amaflags", args.variable)) {
  318. int amaflags;
  319. if (sscanf(tempbuf, "%8d", &amaflags) != 1) {
  320. ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
  321. args.variable, tempbuf, ast_channel_name(payload->chan));
  322. return;
  323. }
  324. snprintf(tempbuf, sizeof(tempbuf), "%s", ast_channel_amaflags2string(amaflags));
  325. }
  326. }
  327. ast_copy_string(output->buf, tempbuf, output->len);
  328. }
  329. static void cdr_write_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
  330. {
  331. struct cdr_func_payload *payload;
  332. struct ast_flags flags = { 0 };
  333. AST_DECLARE_APP_ARGS(args,
  334. AST_APP_ARG(variable);
  335. AST_APP_ARG(options);
  336. );
  337. char *parse;
  338. if (cdr_write_message_type() != stasis_message_type(message)) {
  339. return;
  340. }
  341. payload = stasis_message_data(message);
  342. if (!payload) {
  343. return;
  344. }
  345. if (ast_strlen_zero(payload->arguments)
  346. || !payload->value) {
  347. /* Sanity check. cdr_write() could never send these bad messages */
  348. ast_assert(0);
  349. return;
  350. }
  351. parse = ast_strdupa(payload->arguments);
  352. AST_STANDARD_APP_ARGS(args, parse);
  353. if (!ast_strlen_zero(args.options)) {
  354. ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
  355. }
  356. /* These are already handled by cdr_write() */
  357. ast_assert(strcasecmp(args.variable, "accountcode")
  358. && strcasecmp(args.variable, "peeraccount")
  359. && strcasecmp(args.variable, "amaflags"));
  360. if (!strcasecmp(args.variable, "userfield")) {
  361. ast_cdr_setuserfield(ast_channel_name(payload->chan), payload->value);
  362. } else {
  363. ast_cdr_setvar(ast_channel_name(payload->chan), args.variable, payload->value);
  364. }
  365. }
  366. static void cdr_prop_write_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
  367. {
  368. struct cdr_func_payload *payload = stasis_message_data(message);
  369. enum ast_cdr_options option;
  370. char *parse;
  371. AST_DECLARE_APP_ARGS(args,
  372. AST_APP_ARG(variable);
  373. AST_APP_ARG(options);
  374. );
  375. if (cdr_prop_write_message_type() != stasis_message_type(message)) {
  376. return;
  377. }
  378. if (!payload) {
  379. return;
  380. }
  381. if (ast_strlen_zero(payload->arguments)) {
  382. ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable)=value)\n)",
  383. payload->cmd, payload->cmd);
  384. return;
  385. }
  386. if (ast_strlen_zero(payload->value)) {
  387. ast_log(AST_LOG_WARNING, "%s requires a value (%s(variable)=value)\n)",
  388. payload->cmd, payload->cmd);
  389. return;
  390. }
  391. parse = ast_strdupa(payload->arguments);
  392. AST_STANDARD_APP_ARGS(args, parse);
  393. if (!strcasecmp("party_a", args.variable)) {
  394. option = AST_CDR_FLAG_PARTY_A;
  395. } else if (!strcasecmp("disable", args.variable)) {
  396. option = AST_CDR_FLAG_DISABLE_ALL;
  397. } else {
  398. ast_log(AST_LOG_WARNING, "Unknown option %s used with %s\n", args.variable, payload->cmd);
  399. return;
  400. }
  401. if (ast_true(payload->value)) {
  402. ast_cdr_set_property(ast_channel_name(payload->chan), option);
  403. } else {
  404. ast_cdr_clear_property(ast_channel_name(payload->chan), option);
  405. }
  406. }
  407. static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
  408. char *buf, size_t len)
  409. {
  410. RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
  411. RAII_VAR(struct cdr_func_payload *, payload, NULL, ao2_cleanup);
  412. struct cdr_func_data output = { 0, };
  413. if (!chan) {
  414. ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
  415. return -1;
  416. }
  417. if (!cdr_read_message_type()) {
  418. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: message type not available\n",
  419. ast_channel_name(chan));
  420. return -1;
  421. }
  422. payload = ao2_alloc(sizeof(*payload), NULL);
  423. if (!payload) {
  424. return -1;
  425. }
  426. payload->chan = chan;
  427. payload->cmd = cmd;
  428. payload->arguments = parse;
  429. payload->data = &output;
  430. buf[0] = '\0';/* Ensure the buffer is initialized. */
  431. output.buf = buf;
  432. output.len = len;
  433. message = stasis_message_create(cdr_read_message_type(), payload);
  434. if (!message) {
  435. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
  436. ast_channel_name(chan));
  437. return -1;
  438. }
  439. /* If this is a request on a dummy channel, we're doing post-processing on an
  440. * already dispatched CDR. Simply call the callback to calculate the value and
  441. * return, instead of posting to Stasis as we would for a running channel.
  442. */
  443. if (ast_strlen_zero(ast_channel_name(chan))) {
  444. cdr_read_callback(NULL, NULL, message);
  445. } else {
  446. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  447. if (!router) {
  448. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n",
  449. ast_channel_name(chan));
  450. return -1;
  451. }
  452. stasis_message_router_publish_sync(router, message);
  453. }
  454. return 0;
  455. }
  456. static int cdr_write(struct ast_channel *chan, const char *cmd, char *arguments,
  457. const char *value)
  458. {
  459. struct stasis_message *message;
  460. struct cdr_func_payload *payload;
  461. struct stasis_message_router *router;
  462. AST_DECLARE_APP_ARGS(args,
  463. AST_APP_ARG(variable);
  464. AST_APP_ARG(options);
  465. );
  466. char *parse;
  467. if (!chan) {
  468. ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
  469. return -1;
  470. }
  471. if (ast_strlen_zero(arguments)) {
  472. ast_log(LOG_WARNING, "%s requires a variable (%s(variable)=value)\n)",
  473. cmd, cmd);
  474. return -1;
  475. }
  476. if (!value) {
  477. ast_log(LOG_WARNING, "%s requires a value (%s(variable)=value)\n)",
  478. cmd, cmd);
  479. return -1;
  480. }
  481. parse = ast_strdupa(arguments);
  482. AST_STANDARD_APP_ARGS(args, parse);
  483. /* These CDR variables are no longer supported or set directly on the channel */
  484. if (!strcasecmp(args.variable, "accountcode")) {
  485. ast_log(LOG_WARNING, "Using the %s function to set 'accountcode' is deprecated. Please use the CHANNEL function instead.\n",
  486. cmd);
  487. ast_channel_lock(chan);
  488. ast_channel_accountcode_set(chan, value);
  489. ast_channel_unlock(chan);
  490. return 0;
  491. }
  492. if (!strcasecmp(args.variable, "amaflags")) {
  493. int amaflags;
  494. ast_log(LOG_WARNING, "Using the %s function to set 'amaflags' is deprecated. Please use the CHANNEL function instead.\n",
  495. cmd);
  496. if (isdigit(*value)) {
  497. if (sscanf(value, "%30d", &amaflags) != 1) {
  498. amaflags = AST_AMA_NONE;
  499. }
  500. } else {
  501. amaflags = ast_channel_string2amaflag(value);
  502. }
  503. ast_channel_lock(chan);
  504. ast_channel_amaflags_set(chan, amaflags);
  505. ast_channel_unlock(chan);
  506. return 0;
  507. }
  508. if (!strcasecmp(args.variable, "peeraccount")) {
  509. ast_log(LOG_WARNING, "The 'peeraccount' setting is not supported. Please set the 'accountcode' on the appropriate channel using the CHANNEL function.\n");
  510. return 0;
  511. }
  512. /* The remaining CDR variables are handled by CDR processing code */
  513. if (!cdr_write_message_type()) {
  514. ast_log(LOG_WARNING, "Failed to manipulate CDR for channel %s: message type not available\n",
  515. ast_channel_name(chan));
  516. return -1;
  517. }
  518. payload = ao2_alloc(sizeof(*payload), NULL);
  519. if (!payload) {
  520. return -1;
  521. }
  522. payload->chan = chan;
  523. payload->cmd = cmd;
  524. payload->arguments = arguments;
  525. payload->value = value;
  526. message = stasis_message_create(cdr_write_message_type(), payload);
  527. ao2_ref(payload, -1);
  528. if (!message) {
  529. ast_log(LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
  530. ast_channel_name(chan));
  531. return -1;
  532. }
  533. router = ast_cdr_message_router();
  534. if (!router) {
  535. ast_log(LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n",
  536. ast_channel_name(chan));
  537. ao2_ref(message, -1);
  538. return -1;
  539. }
  540. stasis_message_router_publish_sync(router, message);
  541. ao2_ref(router, -1);
  542. ao2_ref(message, -1);
  543. return 0;
  544. }
  545. static int cdr_prop_write(struct ast_channel *chan, const char *cmd, char *parse,
  546. const char *value)
  547. {
  548. RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
  549. RAII_VAR(struct cdr_func_payload *, payload, NULL, ao2_cleanup);
  550. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  551. if (!chan) {
  552. ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
  553. return -1;
  554. }
  555. if (!router) {
  556. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n",
  557. ast_channel_name(chan));
  558. return -1;
  559. }
  560. if (!cdr_prop_write_message_type()) {
  561. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: message type not available\n",
  562. ast_channel_name(chan));
  563. return -1;
  564. }
  565. payload = ao2_alloc(sizeof(*payload), NULL);
  566. if (!payload) {
  567. return -1;
  568. }
  569. payload->chan = chan;
  570. payload->cmd = cmd;
  571. payload->arguments = parse;
  572. payload->value = value;
  573. message = stasis_message_create(cdr_prop_write_message_type(), payload);
  574. if (!message) {
  575. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
  576. ast_channel_name(chan));
  577. return -1;
  578. }
  579. stasis_message_router_publish_sync(router, message);
  580. return 0;
  581. }
  582. static struct ast_custom_function cdr_function = {
  583. .name = "CDR",
  584. .read = cdr_read,
  585. .write = cdr_write,
  586. };
  587. static struct ast_custom_function cdr_prop_function = {
  588. .name = "CDR_PROP",
  589. .read = NULL,
  590. .write = cdr_prop_write,
  591. };
  592. static int unload_module(void)
  593. {
  594. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  595. int res = 0;
  596. if (router) {
  597. stasis_message_router_remove(router, cdr_prop_write_message_type());
  598. stasis_message_router_remove(router, cdr_write_message_type());
  599. stasis_message_router_remove(router, cdr_read_message_type());
  600. }
  601. STASIS_MESSAGE_TYPE_CLEANUP(cdr_read_message_type);
  602. STASIS_MESSAGE_TYPE_CLEANUP(cdr_write_message_type);
  603. STASIS_MESSAGE_TYPE_CLEANUP(cdr_prop_write_message_type);
  604. res |= ast_custom_function_unregister(&cdr_function);
  605. res |= ast_custom_function_unregister(&cdr_prop_function);
  606. return res;
  607. }
  608. static int load_module(void)
  609. {
  610. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  611. int res = 0;
  612. if (!router) {
  613. return AST_MODULE_LOAD_DECLINE;
  614. }
  615. res |= STASIS_MESSAGE_TYPE_INIT(cdr_read_message_type);
  616. res |= STASIS_MESSAGE_TYPE_INIT(cdr_write_message_type);
  617. res |= STASIS_MESSAGE_TYPE_INIT(cdr_prop_write_message_type);
  618. res |= ast_custom_function_register(&cdr_function);
  619. res |= ast_custom_function_register(&cdr_prop_function);
  620. res |= stasis_message_router_add(router, cdr_prop_write_message_type(),
  621. cdr_prop_write_callback, NULL);
  622. res |= stasis_message_router_add(router, cdr_write_message_type(),
  623. cdr_write_callback, NULL);
  624. res |= stasis_message_router_add(router, cdr_read_message_type(),
  625. cdr_read_callback, NULL);
  626. if (res) {
  627. unload_module();
  628. return AST_MODULE_LOAD_DECLINE;
  629. }
  630. return AST_MODULE_LOAD_SUCCESS;
  631. }
  632. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Detail Record (CDR) dialplan functions");