func_cdr.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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>Disable CDRs for this channel.</para>
  178. <para>Write-Only</para>
  179. </enum>
  180. </enumlist>
  181. </parameter>
  182. </syntax>
  183. <description>
  184. <para>This function sets a property on a channel's CDR. Properties
  185. alter the behavior of how the CDR operates for that channel.</para>
  186. </description>
  187. </function>
  188. ***/
  189. enum cdr_option_flags {
  190. OPT_UNPARSED = (1 << 1),
  191. OPT_FLOAT = (1 << 2),
  192. };
  193. AST_APP_OPTIONS(cdr_func_options, {
  194. AST_APP_OPTION('f', OPT_FLOAT),
  195. AST_APP_OPTION('u', OPT_UNPARSED),
  196. });
  197. struct cdr_func_payload {
  198. struct ast_channel *chan;
  199. const char *cmd;
  200. const char *arguments;
  201. const char *value;
  202. void *data;
  203. };
  204. struct cdr_func_data {
  205. char *buf;
  206. size_t len;
  207. };
  208. STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_read_message_type);
  209. STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_write_message_type);
  210. STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_prop_write_message_type);
  211. static void cdr_read_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
  212. {
  213. struct cdr_func_payload *payload = stasis_message_data(message);
  214. struct cdr_func_data *output;
  215. char *info;
  216. char *value = NULL;
  217. struct ast_flags flags = { 0 };
  218. char tempbuf[512];
  219. AST_DECLARE_APP_ARGS(args,
  220. AST_APP_ARG(variable);
  221. AST_APP_ARG(options);
  222. );
  223. if (cdr_read_message_type() != stasis_message_type(message)) {
  224. return;
  225. }
  226. ast_assert(payload != NULL);
  227. output = payload->data;
  228. ast_assert(output != NULL);
  229. if (ast_strlen_zero(payload->arguments)) {
  230. ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable[,option]))\n)",
  231. payload->cmd, payload->cmd);
  232. return;
  233. }
  234. info = ast_strdupa(payload->arguments);
  235. AST_STANDARD_APP_ARGS(args, info);
  236. if (!ast_strlen_zero(args.options)) {
  237. ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
  238. }
  239. if (ast_strlen_zero(ast_channel_name(payload->chan))) {
  240. /* Format request on a dummy channel */
  241. ast_cdr_format_var(ast_channel_cdr(payload->chan), args.variable, &value, tempbuf, sizeof(tempbuf), 0);
  242. if (ast_strlen_zero(value)) {
  243. return;
  244. }
  245. ast_copy_string(tempbuf, value, sizeof(tempbuf));
  246. ast_set_flag(&flags, OPT_UNPARSED);
  247. } else if (ast_cdr_getvar(ast_channel_name(payload->chan), args.variable, tempbuf, sizeof(tempbuf))) {
  248. return;
  249. }
  250. if (ast_test_flag(&flags, OPT_FLOAT)
  251. && (!strcasecmp("billsec", args.variable) || !strcasecmp("duration", args.variable))) {
  252. long ms;
  253. double dtime;
  254. if (sscanf(tempbuf, "%30ld", &ms) != 1) {
  255. ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
  256. args.variable, tempbuf, ast_channel_name(payload->chan));
  257. return;
  258. }
  259. dtime = (double)(ms / 1000.0);
  260. snprintf(tempbuf, sizeof(tempbuf), "%lf", dtime);
  261. } else if (!ast_test_flag(&flags, OPT_UNPARSED)) {
  262. if (!strcasecmp("start", args.variable)
  263. || !strcasecmp("end", args.variable)
  264. || !strcasecmp("answer", args.variable)) {
  265. struct timeval fmt_time;
  266. struct ast_tm tm;
  267. /* tv_usec is suseconds_t, which could be int or long */
  268. long int tv_usec;
  269. if (sscanf(tempbuf, "%ld.%ld", &fmt_time.tv_sec, &tv_usec) != 2) {
  270. ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
  271. args.variable, tempbuf, ast_channel_name(payload->chan));
  272. return;
  273. }
  274. if (fmt_time.tv_sec) {
  275. fmt_time.tv_usec = tv_usec;
  276. ast_localtime(&fmt_time, &tm, NULL);
  277. ast_strftime(tempbuf, sizeof(tempbuf), "%Y-%m-%d %T", &tm);
  278. } else {
  279. tempbuf[0] = '\0';
  280. }
  281. } else if (!strcasecmp("disposition", args.variable)) {
  282. int disposition;
  283. if (sscanf(tempbuf, "%8d", &disposition) != 1) {
  284. ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
  285. args.variable, tempbuf, ast_channel_name(payload->chan));
  286. return;
  287. }
  288. snprintf(tempbuf, sizeof(tempbuf), "%s", ast_cdr_disp2str(disposition));
  289. } else if (!strcasecmp("amaflags", args.variable)) {
  290. int amaflags;
  291. if (sscanf(tempbuf, "%8d", &amaflags) != 1) {
  292. ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
  293. args.variable, tempbuf, ast_channel_name(payload->chan));
  294. return;
  295. }
  296. snprintf(tempbuf, sizeof(tempbuf), "%s", ast_channel_amaflags2string(amaflags));
  297. }
  298. }
  299. ast_copy_string(output->buf, tempbuf, output->len);
  300. }
  301. static void cdr_write_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
  302. {
  303. struct cdr_func_payload *payload = stasis_message_data(message);
  304. struct ast_flags flags = { 0 };
  305. AST_DECLARE_APP_ARGS(args,
  306. AST_APP_ARG(variable);
  307. AST_APP_ARG(options);
  308. );
  309. char *parse;
  310. if (cdr_write_message_type() != stasis_message_type(message)) {
  311. return;
  312. }
  313. if (!payload) {
  314. return;
  315. }
  316. if (ast_strlen_zero(payload->arguments)) {
  317. ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable)=value)\n)",
  318. payload->cmd, payload->cmd);
  319. return;
  320. }
  321. if (ast_strlen_zero(payload->value)) {
  322. ast_log(AST_LOG_WARNING, "%s requires a value (%s(variable)=value)\n)",
  323. payload->cmd, payload->cmd);
  324. return;
  325. }
  326. parse = ast_strdupa(payload->arguments);
  327. AST_STANDARD_APP_ARGS(args, parse);
  328. if (!ast_strlen_zero(args.options)) {
  329. ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
  330. }
  331. if (!strcasecmp(args.variable, "accountcode")) {
  332. ast_log(AST_LOG_WARNING, "Using the CDR function to set 'accountcode' is deprecated. Please use the CHANNEL function instead.\n");
  333. ast_channel_lock(payload->chan);
  334. ast_channel_accountcode_set(payload->chan, payload->value);
  335. ast_channel_unlock(payload->chan);
  336. } else if (!strcasecmp(args.variable, "peeraccount")) {
  337. ast_log(AST_LOG_WARNING, "The 'peeraccount' setting is not supported. Please set the 'accountcode' on the appropriate channel using the CHANNEL function.\n");
  338. } else if (!strcasecmp(args.variable, "userfield")) {
  339. ast_cdr_setuserfield(ast_channel_name(payload->chan), payload->value);
  340. } else if (!strcasecmp(args.variable, "amaflags")) {
  341. ast_log(AST_LOG_WARNING, "Using the CDR function to set 'amaflags' is deprecated. Please use the CHANNEL function instead.\n");
  342. if (isdigit(*payload->value)) {
  343. int amaflags;
  344. sscanf(payload->value, "%30d", &amaflags);
  345. ast_channel_lock(payload->chan);
  346. ast_channel_amaflags_set(payload->chan, amaflags);
  347. ast_channel_unlock(payload->chan);
  348. } else {
  349. ast_channel_lock(payload->chan);
  350. ast_channel_amaflags_set(payload->chan, ast_channel_string2amaflag(payload->value));
  351. ast_channel_unlock(payload->chan);
  352. }
  353. } else {
  354. ast_cdr_setvar(ast_channel_name(payload->chan), args.variable, payload->value);
  355. }
  356. return;
  357. }
  358. static void cdr_prop_write_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
  359. {
  360. struct cdr_func_payload *payload = stasis_message_data(message);
  361. enum ast_cdr_options option;
  362. char *parse;
  363. AST_DECLARE_APP_ARGS(args,
  364. AST_APP_ARG(variable);
  365. AST_APP_ARG(options);
  366. );
  367. if (cdr_prop_write_message_type() != stasis_message_type(message)) {
  368. return;
  369. }
  370. if (!payload) {
  371. return;
  372. }
  373. if (ast_strlen_zero(payload->arguments)) {
  374. ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable)=value)\n)",
  375. payload->cmd, payload->cmd);
  376. return;
  377. }
  378. if (ast_strlen_zero(payload->value)) {
  379. ast_log(AST_LOG_WARNING, "%s requires a value (%s(variable)=value)\n)",
  380. payload->cmd, payload->cmd);
  381. return;
  382. }
  383. parse = ast_strdupa(payload->arguments);
  384. AST_STANDARD_APP_ARGS(args, parse);
  385. if (!strcasecmp("party_a", args.variable)) {
  386. option = AST_CDR_FLAG_PARTY_A;
  387. } else if (!strcasecmp("disable", args.variable)) {
  388. option = AST_CDR_FLAG_DISABLE_ALL;
  389. } else {
  390. ast_log(AST_LOG_WARNING, "Unknown option %s used with %s\n", args.variable, payload->cmd);
  391. return;
  392. }
  393. if (ast_true(payload->value)) {
  394. ast_cdr_set_property(ast_channel_name(payload->chan), option);
  395. } else {
  396. ast_cdr_clear_property(ast_channel_name(payload->chan), option);
  397. }
  398. }
  399. static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
  400. char *buf, size_t len)
  401. {
  402. RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
  403. RAII_VAR(struct cdr_func_payload *, payload, NULL, ao2_cleanup);
  404. struct cdr_func_data output = { 0, };
  405. if (!chan) {
  406. ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
  407. return -1;
  408. }
  409. if (!cdr_read_message_type()) {
  410. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: message type not available\n",
  411. ast_channel_name(chan));
  412. return -1;
  413. }
  414. payload = ao2_alloc(sizeof(*payload), NULL);
  415. if (!payload) {
  416. return -1;
  417. }
  418. payload->chan = chan;
  419. payload->cmd = cmd;
  420. payload->arguments = parse;
  421. payload->data = &output;
  422. buf[0] = '\0';/* Ensure the buffer is initialized. */
  423. output.buf = buf;
  424. output.len = len;
  425. message = stasis_message_create(cdr_read_message_type(), payload);
  426. if (!message) {
  427. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
  428. ast_channel_name(chan));
  429. return -1;
  430. }
  431. /* If this is a request on a dummy channel, we're doing post-processing on an
  432. * already dispatched CDR. Simply call the callback to calculate the value and
  433. * return, instead of posting to Stasis as we would for a running channel.
  434. */
  435. if (ast_strlen_zero(ast_channel_name(chan))) {
  436. cdr_read_callback(NULL, NULL, message);
  437. } else {
  438. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  439. if (!router) {
  440. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n",
  441. ast_channel_name(chan));
  442. return -1;
  443. }
  444. stasis_message_router_publish_sync(router, message);
  445. }
  446. return 0;
  447. }
  448. static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
  449. const char *value)
  450. {
  451. RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
  452. RAII_VAR(struct cdr_func_payload *, payload, NULL, ao2_cleanup);
  453. RAII_VAR(struct stasis_message_router *, router,
  454. ast_cdr_message_router(), ao2_cleanup);
  455. if (!chan) {
  456. ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
  457. return -1;
  458. }
  459. if (!router) {
  460. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n",
  461. ast_channel_name(chan));
  462. return -1;
  463. }
  464. if (!cdr_write_message_type()) {
  465. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: message type not available\n",
  466. ast_channel_name(chan));
  467. return -1;
  468. }
  469. payload = ao2_alloc(sizeof(*payload), NULL);
  470. if (!payload) {
  471. return -1;
  472. }
  473. payload->chan = chan;
  474. payload->cmd = cmd;
  475. payload->arguments = parse;
  476. payload->value = value;
  477. message = stasis_message_create(cdr_write_message_type(), payload);
  478. if (!message) {
  479. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
  480. ast_channel_name(chan));
  481. return -1;
  482. }
  483. stasis_message_router_publish_sync(router, message);
  484. return 0;
  485. }
  486. static int cdr_prop_write(struct ast_channel *chan, const char *cmd, char *parse,
  487. const char *value)
  488. {
  489. RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
  490. RAII_VAR(struct cdr_func_payload *, payload,
  491. ao2_alloc(sizeof(*payload), NULL), ao2_cleanup);
  492. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  493. if (!chan) {
  494. ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
  495. return -1;
  496. }
  497. if (!router) {
  498. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n",
  499. ast_channel_name(chan));
  500. return -1;
  501. }
  502. if (!cdr_write_message_type()) {
  503. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: message type not available\n",
  504. ast_channel_name(chan));
  505. return -1;
  506. }
  507. payload = ao2_alloc(sizeof(*payload), NULL);
  508. if (!payload) {
  509. return -1;
  510. }
  511. payload->chan = chan;
  512. payload->cmd = cmd;
  513. payload->arguments = parse;
  514. payload->value = value;
  515. message = stasis_message_create(cdr_prop_write_message_type(), payload);
  516. if (!message) {
  517. ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
  518. ast_channel_name(chan));
  519. return -1;
  520. }
  521. stasis_message_router_publish_sync(router, message);
  522. return 0;
  523. }
  524. static struct ast_custom_function cdr_function = {
  525. .name = "CDR",
  526. .read = cdr_read,
  527. .write = cdr_write,
  528. };
  529. static struct ast_custom_function cdr_prop_function = {
  530. .name = "CDR_PROP",
  531. .read = NULL,
  532. .write = cdr_prop_write,
  533. };
  534. static int unload_module(void)
  535. {
  536. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  537. int res = 0;
  538. if (router) {
  539. stasis_message_router_remove(router, cdr_prop_write_message_type());
  540. stasis_message_router_remove(router, cdr_write_message_type());
  541. stasis_message_router_remove(router, cdr_read_message_type());
  542. }
  543. STASIS_MESSAGE_TYPE_CLEANUP(cdr_read_message_type);
  544. STASIS_MESSAGE_TYPE_CLEANUP(cdr_write_message_type);
  545. STASIS_MESSAGE_TYPE_CLEANUP(cdr_prop_write_message_type);
  546. res |= ast_custom_function_unregister(&cdr_function);
  547. res |= ast_custom_function_unregister(&cdr_prop_function);
  548. return res;
  549. }
  550. static int load_module(void)
  551. {
  552. RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
  553. int res = 0;
  554. if (!router) {
  555. return AST_MODULE_LOAD_FAILURE;
  556. }
  557. res |= STASIS_MESSAGE_TYPE_INIT(cdr_read_message_type);
  558. res |= STASIS_MESSAGE_TYPE_INIT(cdr_write_message_type);
  559. res |= STASIS_MESSAGE_TYPE_INIT(cdr_prop_write_message_type);
  560. res |= ast_custom_function_register(&cdr_function);
  561. res |= ast_custom_function_register(&cdr_prop_function);
  562. res |= stasis_message_router_add(router, cdr_prop_write_message_type(),
  563. cdr_prop_write_callback, NULL);
  564. res |= stasis_message_router_add(router, cdr_write_message_type(),
  565. cdr_write_callback, NULL);
  566. res |= stasis_message_router_add(router, cdr_read_message_type(),
  567. cdr_read_callback, NULL);
  568. if (res) {
  569. return AST_MODULE_LOAD_FAILURE;
  570. }
  571. return AST_MODULE_LOAD_SUCCESS;
  572. }
  573. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Detail Record (CDR) dialplan functions");