res_sorcery_astdb.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Joshua Colp <jcolp@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. /*!
  19. * \file
  20. *
  21. * \brief Sorcery Astdb Object Wizard
  22. *
  23. * \author Joshua Colp <jcolp@digium.com>
  24. */
  25. /*** MODULEINFO
  26. <support_level>core</support_level>
  27. ***/
  28. #include "asterisk.h"
  29. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  30. #include <regex.h>
  31. #include "asterisk/module.h"
  32. #include "asterisk/sorcery.h"
  33. #include "asterisk/astdb.h"
  34. #include "asterisk/json.h"
  35. static void *sorcery_astdb_open(const char *data);
  36. static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, void *object);
  37. static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id);
  38. static void *sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields);
  39. static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,
  40. const struct ast_variable *fields);
  41. static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);
  42. static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len);
  43. static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object);
  44. static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object);
  45. static void sorcery_astdb_close(void *data);
  46. static struct ast_sorcery_wizard astdb_object_wizard = {
  47. .name = "astdb",
  48. .open = sorcery_astdb_open,
  49. .create = sorcery_astdb_create,
  50. .retrieve_id = sorcery_astdb_retrieve_id,
  51. .retrieve_fields = sorcery_astdb_retrieve_fields,
  52. .retrieve_multiple = sorcery_astdb_retrieve_multiple,
  53. .retrieve_regex = sorcery_astdb_retrieve_regex,
  54. .retrieve_prefix = sorcery_astdb_retrieve_prefix,
  55. .update = sorcery_astdb_update,
  56. .delete = sorcery_astdb_delete,
  57. .close = sorcery_astdb_close,
  58. };
  59. static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, void *object)
  60. {
  61. RAII_VAR(struct ast_json *, objset, ast_sorcery_objectset_json_create(sorcery, object), ast_json_unref);
  62. RAII_VAR(char *, value, NULL, ast_json_free);
  63. const char *prefix = data;
  64. char family[strlen(prefix) + strlen(ast_sorcery_object_get_type(object)) + 2];
  65. if (!objset || !(value = ast_json_dump_string(objset))) {
  66. return -1;
  67. }
  68. snprintf(family, sizeof(family), "%s/%s", prefix, ast_sorcery_object_get_type(object));
  69. return ast_db_put(family, ast_sorcery_object_get_id(object), value);
  70. }
  71. /*! \brief Internal helper function which returns a filtered objectset.
  72. *
  73. * The following are filtered out of the objectset:
  74. * \li Fields that are not registered with sorcery.
  75. *
  76. * \param objectset Objectset to filter.
  77. * \param sorcery The sorcery instance that is requesting an objectset.
  78. * \param type The object type
  79. *
  80. * \return The filtered objectset
  81. */
  82. static struct ast_variable *sorcery_astdb_filter_objectset(struct ast_variable *objectset, const struct ast_sorcery *sorcery,
  83. const char *type)
  84. {
  85. struct ast_variable *previous = NULL, *field = objectset;
  86. struct ast_sorcery_object_type *object_type;
  87. object_type = ast_sorcery_get_object_type(sorcery, type);
  88. if (!object_type) {
  89. ast_log(LOG_WARNING, "Unknown sorcery object type %s. Expect errors\n", type);
  90. return objectset;
  91. }
  92. while (field) {
  93. struct ast_variable *removed;
  94. if (ast_sorcery_is_object_field_registered(object_type, field->name)) {
  95. previous = field;
  96. field = field->next;
  97. continue;
  98. }
  99. ast_debug(1, "Filtering out astdb field '%s' from retrieval\n", field->name);
  100. if (previous) {
  101. previous->next = field->next;
  102. } else {
  103. objectset = field->next;
  104. }
  105. removed = field;
  106. field = field->next;
  107. removed->next = NULL;
  108. ast_variables_destroy(removed);
  109. }
  110. ao2_cleanup(object_type);
  111. return objectset;
  112. }
  113. /*! \brief Internal helper function which retrieves an object, or multiple objects, using fields for criteria */
  114. static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields, struct ao2_container *objects)
  115. {
  116. const char *prefix = data;
  117. char family[strlen(prefix) + strlen(type) + 2];
  118. RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
  119. struct ast_db_entry *entry;
  120. snprintf(family, sizeof(family), "%s/%s", prefix, type);
  121. if (!(entries = ast_db_gettree(family, NULL))) {
  122. return NULL;
  123. }
  124. for (entry = entries; entry; entry = entry->next) {
  125. const char *key = entry->key + strlen(family) + 2;
  126. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  127. struct ast_json_error error;
  128. RAII_VAR(struct ast_variable *, existing, NULL, ast_variables_destroy);
  129. void *object = NULL;
  130. if (!(json = ast_json_load_string(entry->data, &error))) {
  131. return NULL;
  132. }
  133. if (ast_json_to_ast_variables(json, &existing) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) {
  134. return NULL;
  135. }
  136. existing = sorcery_astdb_filter_objectset(existing, sorcery, type);
  137. if (fields && !ast_variable_lists_match(existing, fields, 0)) {
  138. continue;
  139. }
  140. if (!(object = ast_sorcery_alloc(sorcery, type, key)) ||
  141. ast_sorcery_objectset_apply(sorcery, object, existing)) {
  142. ao2_cleanup(object);
  143. return NULL;
  144. }
  145. if (!objects) {
  146. return object;
  147. }
  148. ao2_link(objects, object);
  149. ao2_cleanup(object);
  150. }
  151. return NULL;
  152. }
  153. static void *sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields)
  154. {
  155. return sorcery_astdb_retrieve_fields_common(sorcery, data, type, fields, NULL);
  156. }
  157. static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
  158. {
  159. const char *prefix = data;
  160. char family[strlen(prefix) + strlen(type) + 2];
  161. RAII_VAR(char *, value, NULL, ast_free_ptr);
  162. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  163. struct ast_json_error error;
  164. RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
  165. void *object = NULL;
  166. snprintf(family, sizeof(family), "%s/%s", prefix, type);
  167. if (ast_db_get_allocated(family, id, &value)
  168. || !(json = ast_json_load_string(value, &error))
  169. || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
  170. || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
  171. || !(object = ast_sorcery_alloc(sorcery, type, id))
  172. || ast_sorcery_objectset_apply(sorcery, object, objset)) {
  173. ast_debug(3, "Failed to retrieve object '%s' from astdb\n", id);
  174. ao2_cleanup(object);
  175. return NULL;
  176. }
  177. return object;
  178. }
  179. static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
  180. {
  181. sorcery_astdb_retrieve_fields_common(sorcery, data, type, fields, objects);
  182. }
  183. /*!
  184. * \internal
  185. * \brief Convert regex prefix pattern to astDB prefix pattern if possible.
  186. *
  187. * \param tree astDB prefix pattern buffer to fill.
  188. * \param regex Extended regular expression with the start anchor character '^'.
  189. *
  190. * \note Since this is a helper function, the tree buffer is
  191. * assumed to always be large enough.
  192. *
  193. * \retval 0 on success.
  194. * \retval -1 on error. regex is invalid.
  195. */
  196. static int make_astdb_prefix_pattern(char *tree, const char *regex)
  197. {
  198. const char *src;
  199. char *dst;
  200. for (dst = tree, src = regex + 1; *src; ++src) {
  201. if (*src == '\\') {
  202. /* Escaped regex char. */
  203. ++src;
  204. if (!*src) {
  205. /* Invalid regex. The caller escaped the string terminator. */
  206. return -1;
  207. }
  208. } else if (*src == '$') {
  209. if (!src[1]) {
  210. /* Remove the tail anchor character. */
  211. *dst = '\0';
  212. return 0;
  213. }
  214. } else if (strchr(".?*+{[(|", *src)) {
  215. /*
  216. * The regex is not a simple prefix pattern.
  217. *
  218. * XXX With more logic, it is possible to simply
  219. * use the current prefix pattern. The last character
  220. * needs to be removed if possible when the current regex
  221. * token is "?*{". Also the rest of the regex pattern
  222. * would need to be checked for subgroup/alternation.
  223. * Subgroup/alternation is too complex for a simple prefix
  224. * match.
  225. */
  226. dst = tree;
  227. break;
  228. }
  229. *dst++ = *src;
  230. }
  231. if (dst != tree) {
  232. *dst++ = '%';
  233. }
  234. *dst = '\0';
  235. return 0;
  236. }
  237. static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex)
  238. {
  239. const char *prefix = data;
  240. char family[strlen(prefix) + strlen(type) + 2];
  241. char tree[strlen(regex) + 1];
  242. RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
  243. regex_t expression;
  244. struct ast_db_entry *entry;
  245. snprintf(family, sizeof(family), "%s/%s", prefix, type);
  246. if (regex[0] == '^') {
  247. /*
  248. * For performance reasons, try to create an astDB prefix
  249. * pattern from the regex to reduce the number of entries
  250. * retrieved from astDB for regex to then match.
  251. */
  252. if (make_astdb_prefix_pattern(tree, regex)) {
  253. return;
  254. }
  255. } else {
  256. tree[0] = '\0';
  257. }
  258. if (!(entries = ast_db_gettree(family, tree))
  259. || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
  260. return;
  261. }
  262. for (entry = entries; entry; entry = entry->next) {
  263. /* The key in the entry includes the family, so we need to strip it out for regex purposes */
  264. const char *key = entry->key + strlen(family) + 2;
  265. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  266. struct ast_json_error error;
  267. RAII_VAR(void *, object, NULL, ao2_cleanup);
  268. RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
  269. if (regexec(&expression, key, 0, NULL, 0)) {
  270. continue;
  271. } else if (!(json = ast_json_load_string(entry->data, &error))
  272. || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
  273. || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
  274. || !(object = ast_sorcery_alloc(sorcery, type, key))
  275. || ast_sorcery_objectset_apply(sorcery, object, objset)) {
  276. regfree(&expression);
  277. return;
  278. }
  279. ao2_link(objects, object);
  280. }
  281. regfree(&expression);
  282. }
  283. static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len)
  284. {
  285. const char *family_prefix = data;
  286. size_t family_len = strlen(family_prefix) + strlen(type) + 1; /* +1 for slash delimiter */
  287. char family[family_len + 1];
  288. char tree[prefix_len + 1];
  289. RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
  290. struct ast_db_entry *entry;
  291. snprintf(tree, sizeof(tree), "%.*s", (int) prefix_len, prefix);
  292. snprintf(family, sizeof(family), "%s/%s", family_prefix, type);
  293. if (!(entries = ast_db_gettree_by_prefix(family, tree))) {
  294. return;
  295. }
  296. for (entry = entries; entry; entry = entry->next) {
  297. /* The key in the entry includes the family, so we need to strip it out */
  298. const char *key = entry->key + family_len + 2;
  299. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  300. struct ast_json_error error;
  301. RAII_VAR(void *, object, NULL, ao2_cleanup);
  302. RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
  303. if (!(json = ast_json_load_string(entry->data, &error))
  304. || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
  305. || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
  306. || !(object = ast_sorcery_alloc(sorcery, type, key))
  307. || ast_sorcery_objectset_apply(sorcery, object, objset)) {
  308. return;
  309. }
  310. ao2_link(objects, object);
  311. }
  312. }
  313. static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object)
  314. {
  315. const char *prefix = data;
  316. char family[strlen(prefix) + strlen(ast_sorcery_object_get_type(object)) + 2], value[2];
  317. snprintf(family, sizeof(family), "%s/%s", prefix, ast_sorcery_object_get_type(object));
  318. /* It is okay for the value to be truncated, we are only checking that it exists */
  319. if (ast_db_get(family, ast_sorcery_object_get_id(object), value, sizeof(value))) {
  320. return -1;
  321. }
  322. /* The only difference between update and create is that for update the object must already exist */
  323. return sorcery_astdb_create(sorcery, data, object);
  324. }
  325. static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object)
  326. {
  327. const char *prefix = data;
  328. char family[strlen(prefix) + strlen(ast_sorcery_object_get_type(object)) + 2];
  329. char value[2];
  330. snprintf(family, sizeof(family), "%s/%s", prefix, ast_sorcery_object_get_type(object));
  331. if (ast_db_get(family, ast_sorcery_object_get_id(object), value, sizeof(value))) {
  332. return -1;
  333. }
  334. return ast_db_del(family, ast_sorcery_object_get_id(object));
  335. }
  336. static void *sorcery_astdb_open(const char *data)
  337. {
  338. /* We require a prefix for family string generation, or else stuff could mix together */
  339. if (ast_strlen_zero(data)) {
  340. return NULL;
  341. }
  342. return ast_strdup(data);
  343. }
  344. static void sorcery_astdb_close(void *data)
  345. {
  346. ast_free(data);
  347. }
  348. static int load_module(void)
  349. {
  350. if (ast_sorcery_wizard_register(&astdb_object_wizard)) {
  351. return AST_MODULE_LOAD_DECLINE;
  352. }
  353. return AST_MODULE_LOAD_SUCCESS;
  354. }
  355. static int unload_module(void)
  356. {
  357. ast_sorcery_wizard_unregister(&astdb_object_wizard);
  358. return 0;
  359. }
  360. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Sorcery Astdb Object Wizard",
  361. .support_level = AST_MODULE_SUPPORT_CORE,
  362. .load = load_module,
  363. .unload = unload_module,
  364. .load_pri = AST_MODPRI_REALTIME_DRIVER,
  365. );