res_pjsip_config_wizard.c 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2014, Fairview 5 Engineering, LLC
  5. *
  6. * George Joseph <george.joseph@fairview5.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. /*! \file
  19. *
  20. * \brief PJSIP Configuration Wizard
  21. *
  22. * \author George Joseph <george.joseph@fairview5.com>
  23. */
  24. /*! \li \ref res_pjsip_config_wizard.c uses the configuration file \ref pjsip_wizard.conf
  25. */
  26. /*!
  27. * \page pjsip_wizard.conf pjsip_wizard.conf
  28. * \verbinclude pjsip_wizard.conf.sample
  29. */
  30. /*** MODULEINFO
  31. <depend>pjproject</depend>
  32. <depend>res_pjsip</depend>
  33. <support_level>core</support_level>
  34. ***/
  35. #include "asterisk.h"
  36. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  37. #include <regex.h>
  38. #include <pjsip.h>
  39. #include "asterisk/astobj2.h"
  40. #include "asterisk/res_pjsip.h"
  41. #include "asterisk/module.h"
  42. #include "asterisk/pbx.h"
  43. #include "asterisk/sorcery.h"
  44. #include "asterisk/vector.h"
  45. /*** DOCUMENTATION
  46. <configInfo name="res_pjsip_config_wizard" language="en_US">
  47. <synopsis>Module that privides simple configuration wizard capabilities.</synopsis>
  48. <description><para>
  49. <emphasis>PJSIP Configuration Wizard</emphasis>
  50. </para>
  51. <para>This module allows creation of common PJSIP configuration scenarios
  52. without having to specify individual endpoint, aor, auth, identify and registration objects.
  53. </para>
  54. <para> </para>
  55. <para>For example, the following configuration snippet would create the
  56. endpoint, aor, contact, auth and phoneprov objects necessary for a phone to
  57. get phone provisioning information, register, and make and receive calls.
  58. A hint is also created in the default context for extension 1000.</para>
  59. <para> </para>
  60. <para>[myphone]</para>
  61. <para>type = wizard</para>
  62. <para>sends_auth = no</para>
  63. <para>accepts_auth = yes</para>
  64. <para>sends_registrations = no</para>
  65. <para>accepts_registrations = yes</para>
  66. <para>has_phoneprov = yes</para>
  67. <para>transport = ipv4</para>
  68. <para>has_hint = yes</para>
  69. <para>hint_exten = 1000</para>
  70. <para>inbound_auth/username = testname</para>
  71. <para>inbound_auth/password = test password</para>
  72. <para>endpoint/allow = ulaw</para>
  73. <para>endpoint/context = default</para>
  74. <para>phoneprov/MAC = 001122aa4455</para>
  75. <para>phoneprov/PROFILE = profile1</para>
  76. <para> </para>
  77. <para>The first 8 items are specific to the wizard. The rest of the items
  78. are passed verbatim to the underlying objects.</para>
  79. <para> </para>
  80. <para>The following configuration snippet would create the
  81. endpoint, aor, contact, auth, identify and registration objects necessary for a trunk
  82. to another pbx or ITSP that requires registration.</para>
  83. <para> </para>
  84. <para>[mytrunk]</para>
  85. <para>type = wizard</para>
  86. <para>sends_auth = yes</para>
  87. <para>accepts_auth = no</para>
  88. <para>sends_registrations = yes</para>
  89. <para>accepts_registrations = no</para>
  90. <para>transport = ipv4</para>
  91. <para>remote_hosts = sip1.myitsp.com:5060,sip2.myitsp.com:5060</para>
  92. <para>outbound_auth/username = testname</para>
  93. <para>outbound_auth/password = test password</para>
  94. <para>endpoint/allow = ulaw</para>
  95. <para>endpoint/context = default</para>
  96. <para> </para>
  97. <para>Of course, any of the items in either example could be placed into
  98. templates and shared among wizard objects.</para>
  99. <para> </para>
  100. <para>For more information, visit:</para>
  101. <para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
  102. </description>
  103. <configFile name="pjsip_wizard.conf">
  104. <configObject name="wizard">
  105. <synopsis>Provides config wizard.</synopsis>
  106. <description>
  107. <para>For more information, visit:</para>
  108. <para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
  109. </description>
  110. <configOption name="type">
  111. <synopsis>Must be 'wizard'.</synopsis>
  112. </configOption>
  113. <configOption name="transport">
  114. <synopsis>The name of a transport to use for this object.</synopsis>
  115. <description><para>If not specified,
  116. the default will be used.</para></description>
  117. </configOption>
  118. <configOption name="remote_hosts">
  119. <synopsis>List of remote hosts.</synopsis>
  120. <description><para>A comma-separated list of remote hosts in the form of
  121. <replaceable>host</replaceable>[:<replaceable>port</replaceable>].
  122. If set, an aor static contact and an identify match will be created for each
  123. entry in the list. If send_registrations is also set, a registration will
  124. also be created for each.</para></description>
  125. </configOption>
  126. <configOption name="sends_auth" default="no">
  127. <synopsis>Send outbound authentication to remote hosts.</synopsis>
  128. <description><para>At least outbound_auth/username is required.</para></description>
  129. </configOption>
  130. <configOption name="accepts_auth" default="no">
  131. <synopsis>Accept incoming authentication from remote hosts.</synopsis>
  132. <description><para>At least inbound_auth/username is required.</para></description>
  133. </configOption>
  134. <configOption name="sends_registrations" default="no">
  135. <synopsis>Send outbound registrations to remote hosts.</synopsis>
  136. <description><para>remote_hosts is required and a registration object will
  137. be created for each host in the remote _hosts string. If authentication is required,
  138. sends_auth and an outbound_auth/username must also be supplied.</para></description>
  139. </configOption>
  140. <configOption name="accepts_registrations" default="no">
  141. <synopsis>Accept inbound registration from remote hosts.</synopsis>
  142. <description><para>An AOR with dynamic contacts will be created. If
  143. the number of contacts nneds to be limited, set aor/max_contacts.</para></description>
  144. </configOption>
  145. <configOption name="has_phoneprov" default="no">
  146. <synopsis>Create a phoneprov object for this endpoint.</synopsis>
  147. <description><para>A phoneprov object will be created. phoneprov/MAC
  148. must be specified.</para></description>
  149. </configOption>
  150. <configOption name="server_uri_pattern" default="sip:${REMOTE_HOST}">
  151. <synopsis>A pattern to use for constructing outbound registration server_uris.</synopsis>
  152. <description><para>
  153. The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
  154. appropriate remote_host for each registration.</para></description>
  155. </configOption>
  156. <configOption name="client_uri_pattern" default="sip:${USERNAME}@${REMOTE_HOST}">
  157. <synopsis>A pattern to use for constructing outbound registration client_uris.</synopsis>
  158. <description><para>
  159. The literals <literal>${REMOTE_HOST}</literal> and <literal>${USERNAME}</literal>
  160. will be substituted with the appropriate remote_host and outbound_auth/username.</para></description>
  161. </configOption>
  162. <configOption name="contact_pattern" default="sip:${REMOTE_HOST}">
  163. <synopsis>A pattern to use for constructing outbound contact uris.</synopsis>
  164. <description><para>
  165. The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
  166. appropriate remote_host for each contact.</para></description>
  167. </configOption>
  168. <configOption name="has_hint" default="no">
  169. <synopsis>Create hint and optionally a default application.</synopsis>
  170. <description><para>Create hint and optionally a default application.</para></description>
  171. </configOption>
  172. <configOption name="hint_context" default="endpoint/context or 'default'">
  173. <synopsis>The context in which to place hints.</synopsis>
  174. <description>
  175. <para>Ignored if <literal>hint_exten</literal> is not specified otherwise specifies the
  176. context into which the dialplan hints will be placed. If not specified,
  177. defaults to the endpoint's context or <literal>default</literal> if that isn't
  178. found.
  179. </para></description>
  180. </configOption>
  181. <configOption name="hint_exten">
  182. <synopsis>Extension to map a PJSIP hint to.</synopsis>
  183. <description>
  184. <para>Will create the following entry in <literal>hint_context</literal>:</para>
  185. <para> <literal>exten =&gt; &lt;hint_exten&gt;,hint,PJSIP/&lt;wizard_id&gt;</literal></para>
  186. <para> </para>
  187. <para>Normal dialplan precedence rules apply so if there's already a hint for
  188. this extension in <literal>hint_context</literal>, this one will be ignored.
  189. For more information, visit: </para>
  190. <para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
  191. </description>
  192. </configOption>
  193. <configOption name="hint_application">
  194. <synopsis>Application to call when 'hint_exten' is dialed.</synopsis>
  195. <description>
  196. <para>Ignored if <literal>hint_exten</literal> isn't specified otherwise
  197. will create the following priority 1 extension in <literal>hint_context</literal>:</para>
  198. <para> <literal>exten =&gt; &lt;hint_exten&gt;,1,&lt;hint_application&gt;</literal></para>
  199. <para> </para>
  200. <para>You can specify any valid extensions.conf application expression.</para>
  201. <para>Examples: </para>
  202. <para> <literal>Dial(${HINT})</literal></para>
  203. <para> <literal>Gosub(stdexten,${EXTEN},1(${HINT}))</literal></para>
  204. <para> </para>
  205. <para>Any extensions.conf style variables specified are passed directly to the
  206. dialplan.</para>
  207. <para> </para>
  208. <para>Normal dialplan precedence rules apply so if there's already a priority 1
  209. application for this specific extension in <literal>hint_context</literal>,
  210. this one will be ignored. For more information, visit: </para>
  211. <para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
  212. </description>
  213. </configOption>
  214. <configOption name="endpoint&#47;*">
  215. <synopsis>Variables to be passed directly to the endpoint.</synopsis>
  216. </configOption>
  217. <configOption name="aor&#47;*">
  218. <synopsis>Variables to be passed directly to the aor.</synopsis>
  219. <description><para>If an aor/contact is explicitly defined then remote_hosts
  220. will not be used to create contacts automatically.</para></description>
  221. </configOption>
  222. <configOption name="inbound_auth&#47;*">
  223. <synopsis>Variables to be passed directly to the inbound auth.</synopsis>
  224. </configOption>
  225. <configOption name="outbound_auth&#47;*">
  226. <synopsis>Variables to be passed directly to the outbound auth.</synopsis>
  227. </configOption>
  228. <configOption name="identify&#47;*">
  229. <synopsis>Variables to be passed directly to the identify.</synopsis>
  230. <description><para>If an identify/match is explicitly defined then remote_hosts
  231. will not be used to create matches automatically.</para></description>
  232. </configOption>
  233. <configOption name="registration&#47;*">
  234. <synopsis>Variables to be passed directly to the outbound registrations.</synopsis>
  235. </configOption>
  236. <configOption name="phoneprov&#47;*">
  237. <synopsis>Variables to be passed directly to the phoneprov object.</synopsis>
  238. <description><para>
  239. To activate phoneprov, at least phoneprov/MAC must be set.</para></description>
  240. </configOption>
  241. </configObject>
  242. </configFile>
  243. </configInfo>
  244. ***/
  245. /*! \brief Defines the maximum number of characters that can be added to a wizard id. */
  246. #define MAX_ID_SUFFIX 20
  247. #define BASE_REGISTRAR "res_pjsip_config_wizard"
  248. /*! \brief A generic char * vector definition. */
  249. AST_VECTOR(string_vector, char *);
  250. /*! \brief Keeps track of the sorcery wizard and last config for each object type */
  251. struct object_type_wizard {
  252. struct ast_sorcery *sorcery;
  253. struct ast_sorcery_wizard *wizard;
  254. void *wizard_data;
  255. struct ast_config *last_config;
  256. char object_type[];
  257. };
  258. static AST_VECTOR(object_type_wizards, struct object_type_wizard *) object_type_wizards;
  259. /*! \brief Callbacks for vector deletes */
  260. #define NOT_EQUALS(a, b) (a != b)
  261. #define OTW_DELETE_CB(otw) ({ \
  262. ast_config_destroy(otw->last_config); \
  263. ast_free(otw); \
  264. })
  265. const static char *object_types[] = {"phoneprov", "registration", "identify", "endpoint", "aor", "auth", NULL};
  266. static int is_one_of(const char *needle, const char *haystack[])
  267. {
  268. int i;
  269. for (i = 0; haystack[i]; i++) {
  270. if (!strcmp(needle, haystack[i])) {
  271. return 1;
  272. }
  273. }
  274. return 0;
  275. }
  276. /*! \brief Finds the otw for the object type */
  277. static struct object_type_wizard *find_wizard(const char *object_type)
  278. {
  279. int idx;
  280. for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
  281. struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
  282. if (!strcmp(otw->object_type, object_type)) {
  283. return otw;
  284. }
  285. }
  286. return NULL;
  287. }
  288. /*! \brief Creates a sorcery object and applies a variable list */
  289. static void *create_object(const struct ast_sorcery *sorcery,
  290. const char *id, const char *type, struct ast_variable *vars)
  291. {
  292. struct ast_sorcery_object *obj = ast_sorcery_alloc(sorcery, type, id);
  293. if (!obj) {
  294. ast_log(LOG_ERROR, "Unable to allocate an object of type '%s' with id '%s'.\n", type, id);
  295. return NULL;
  296. }
  297. if (ast_sorcery_objectset_apply(sorcery, obj, vars)) {
  298. ast_log(LOG_ERROR, "Unable to apply object type '%s' with id '%s'. Check preceeding errors.\n", type, id);
  299. ao2_ref(obj, -1);
  300. return NULL;
  301. }
  302. return obj;
  303. }
  304. /*! \brief Finds a variable in a list and tests it */
  305. static int is_variable_true(struct ast_variable *vars, const char *name)
  306. {
  307. return ast_true(ast_variable_find_in_list(vars, name));
  308. }
  309. /*! \brief Appends a variable to the end of an existing list */
  310. static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
  311. {
  312. struct ast_variable *new = ast_variable_new(name, value, "");
  313. if (!new) {
  314. ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name);
  315. return -1;
  316. }
  317. ast_variable_list_append(existing, new);
  318. return 0;
  319. }
  320. /*! \brief Appends a variable to the end of an existing list. On failure, cause the calling
  321. * function to return -1 */
  322. #define variable_list_append_return(existing, name, value) ({ \
  323. struct ast_variable *new = ast_variable_new(name, value, ""); \
  324. if (!new) { \
  325. ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name); \
  326. return -1; \
  327. } \
  328. ast_variable_list_append(existing, new); \
  329. })
  330. /*! \brief We need to strip off the prefix from the name of each variable
  331. * so they're suitable for objectset_apply.
  332. * I.E. will transform outbound_auth/username to username.
  333. */
  334. static struct ast_variable *get_object_variables(struct ast_variable *vars, char *prefix)
  335. {
  336. struct ast_variable *return_vars = NULL;
  337. struct ast_variable *v = vars;
  338. int plen = strlen(prefix);
  339. for(; v; v = v->next) {
  340. if (ast_begins_with(v->name, prefix) && strlen(v->name) > plen) {
  341. if (variable_list_append(&return_vars, v->name + plen, v->value)) {
  342. ast_variables_destroy(return_vars);
  343. return NULL;
  344. }
  345. }
  346. }
  347. return return_vars;
  348. }
  349. /* Don't call while holding context locks. */
  350. static int delete_extens(const char *context, const char *exten)
  351. {
  352. struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
  353. if (pbx_find_extension(NULL, NULL, &find_info, context, exten, PRIORITY_HINT, NULL, NULL, E_MATCH)) {
  354. ast_context_remove_extension(context, exten, PRIORITY_HINT, BASE_REGISTRAR);
  355. }
  356. if (pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH)) {
  357. ast_context_remove_extension(context, exten, 1, BASE_REGISTRAR);
  358. }
  359. return 0;
  360. }
  361. static int add_extension(struct ast_context *context, const char *exten,
  362. int priority, const char *application)
  363. {
  364. struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
  365. struct ast_exten *existing_exten;
  366. char *data = NULL;
  367. char *app = NULL;
  368. void *free_ptr = NULL;
  369. char *paren;
  370. const char *context_name;
  371. if (!context || ast_strlen_zero(exten) || ast_strlen_zero(application)) {
  372. return -1;
  373. }
  374. /* The incoming application has to be split into the app name and the
  375. * arguments (data). The app name can be any storage type as add_extension
  376. * copies it into its own buffer. Data however, needs to be dynamically
  377. * allocated and a free function provided.
  378. */
  379. paren = strchr(application, '(');
  380. if (!paren) {
  381. app = (char *)application;
  382. } else {
  383. app = ast_strdupa(application);
  384. app[paren - application] = '\0';
  385. data = ast_strdup(paren + 1);
  386. if (!data) {
  387. return -1;
  388. }
  389. data[strlen(data) - 1] = '\0';
  390. free_ptr = ast_free_ptr;
  391. if (ast_strlen_zero(app) || ast_strlen_zero(data)) {
  392. ast_free(data);
  393. return -1;
  394. }
  395. }
  396. /* Don't disturb existing, exact-match, entries. */
  397. context_name = ast_get_context_name(context);
  398. if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, context_name, exten,
  399. priority, NULL, NULL, E_MATCH))) {
  400. const char *existing_app = ast_get_extension_app(existing_exten);
  401. const char *existing_data = ast_get_extension_app_data(existing_exten);
  402. if (!strcmp(existing_app, app)
  403. && !strcmp(existing_data ? existing_data : "", data ? data : "")) {
  404. ast_free(data);
  405. return 0;
  406. }
  407. ast_context_remove_extension2(context, exten, priority, BASE_REGISTRAR, 1);
  408. }
  409. if (ast_add_extension2_nolock(context, 0, exten, priority, NULL, NULL,
  410. app, data, free_ptr, BASE_REGISTRAR)) {
  411. ast_free(data);
  412. return -1;
  413. }
  414. return 0;
  415. }
  416. static int add_hints(const char *context, const char *exten, const char *application, const char *id)
  417. {
  418. struct ast_context *hint_context;
  419. char *hint_device;
  420. hint_device = ast_alloca(strlen("PJSIP/") + strlen(id) + 1);
  421. sprintf(hint_device, "PJSIP/%s", id);
  422. /* We need the contexts list locked to safely be able to both read and lock the specific context within */
  423. if (ast_wrlock_contexts()) {
  424. ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
  425. return -1;
  426. }
  427. if (!(hint_context = ast_context_find_or_create(NULL, NULL, context, BASE_REGISTRAR))) {
  428. ast_log(LOG_ERROR, "Unable to find or create hint context '%s'\n", context);
  429. if (ast_unlock_contexts()) {
  430. ast_assert(0);
  431. }
  432. return -1;
  433. }
  434. /* Transfer the all-contexts lock to the specific context */
  435. if (ast_wrlock_context(hint_context)) {
  436. ast_unlock_contexts();
  437. ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
  438. return -1;
  439. }
  440. ast_unlock_contexts();
  441. if (add_extension(hint_context, exten, PRIORITY_HINT, hint_device)) {
  442. ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
  443. exten, context);
  444. }
  445. if (!ast_strlen_zero(application)) {
  446. if (add_extension(hint_context, exten, 1, application)) {
  447. ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
  448. exten, context);
  449. }
  450. } else {
  451. ast_context_remove_extension2(hint_context, exten, 1, BASE_REGISTRAR, 1);
  452. }
  453. ast_unlock_context(hint_context);
  454. return 0;
  455. }
  456. static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  457. struct ast_category *wiz, char *direction)
  458. {
  459. struct ast_variable *wizvars = ast_category_first(wiz);
  460. struct ast_sorcery_object *obj = NULL;
  461. const char *id = ast_category_get_name(wiz);
  462. char new_id[strlen(id) + MAX_ID_SUFFIX];
  463. char prefix[strlen(direction) + strlen("_auth/") + 1];
  464. char *test_variable = NULL;
  465. RAII_VAR(struct ast_variable *, vars, NULL, ast_variables_destroy);
  466. snprintf(prefix, sizeof(prefix), "%s_auth/", direction);
  467. vars = get_object_variables(wizvars, prefix);
  468. if (!strcmp(direction, "outbound")) {
  469. snprintf(new_id, sizeof(new_id), "%s-oauth", id);
  470. test_variable = "sends_auth";
  471. } else {
  472. snprintf(new_id, sizeof(new_id), "%s-iauth", id);
  473. test_variable = "accepts_auth";
  474. }
  475. if (is_variable_true(wizvars, test_variable)) {
  476. if (!ast_variable_find_in_list(vars, "username")) {
  477. ast_log(LOG_ERROR,
  478. "Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
  479. return -1;
  480. }
  481. } else {
  482. /* Delete auth if sends or accepts is now false. */
  483. obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "auth", new_id);
  484. if (obj) {
  485. otw->wizard->delete(sorcery, otw->wizard_data, obj);
  486. ao2_ref(obj, -1);
  487. }
  488. return 0;
  489. }
  490. variable_list_append_return(&vars, "@pjsip_wizard", id);
  491. /* If the user set auth_type, don't override it. */
  492. if (!ast_variable_find_in_list(vars, "auth_type")) {
  493. variable_list_append_return(&vars, "auth_type", "userpass");
  494. }
  495. obj = create_object(sorcery, new_id, "auth", vars);
  496. if (!obj) {
  497. return -1;
  498. }
  499. if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
  500. otw->wizard->create(sorcery, otw->wizard_data, obj);
  501. }
  502. ao2_ref(obj, -1);
  503. return 0;
  504. }
  505. static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  506. struct ast_category *wiz)
  507. {
  508. int rc;
  509. if ((rc = handle_auth(sorcery, otw, wiz, "outbound"))) {
  510. return rc;
  511. }
  512. return handle_auth(sorcery, otw, wiz, "inbound");
  513. }
  514. static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  515. struct ast_category *wiz, struct string_vector *remote_hosts_vector)
  516. {
  517. struct ast_variable *wizvars = ast_category_first(wiz);
  518. struct ast_sorcery_object *obj = NULL;
  519. const char *id = ast_category_get_name(wiz);
  520. const char *contact_pattern;
  521. int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
  522. RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "aor/"), ast_variables_destroy);
  523. variable_list_append(&vars, "@pjsip_wizard", id);
  524. /* If the user explicitly specified an aor/contact, don't use remote hosts. */
  525. if (!ast_variable_find_in_list(vars, "contact")) {
  526. if (!(contact_pattern = ast_variable_find_in_list(wizvars, "contact_pattern"))) {
  527. contact_pattern = "sip:${REMOTE_HOST}";
  528. }
  529. if (host_count > 0 && !ast_strlen_zero(contact_pattern)) {
  530. int host_counter;
  531. /* ast_str_substitute_variables operate on a varshead list so we have
  532. * to create one to hold the REPORT_HOST substitution, do the substitution,
  533. * then append the result to the ast_variable list.
  534. */
  535. for (host_counter = 0; host_counter < host_count; host_counter++) {
  536. RAII_VAR(struct ast_str *, new_str, ast_str_create(64), ast_free);
  537. RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
  538. struct ast_var_t *var = ast_var_assign("REMOTE_HOST",
  539. AST_VECTOR_GET(remote_hosts_vector, host_counter));
  540. AST_VAR_LIST_INSERT_TAIL(subst_vars, var);
  541. ast_str_substitute_variables_varshead(&new_str, 0, subst_vars,
  542. contact_pattern);
  543. variable_list_append_return(&vars, "contact", ast_str_buffer(new_str));
  544. }
  545. }
  546. }
  547. obj = create_object(sorcery, id, "aor", vars);
  548. if (!obj) {
  549. return -1;
  550. }
  551. if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
  552. otw->wizard->create(sorcery, otw->wizard_data, obj);
  553. }
  554. ao2_ref(obj, -1);
  555. return 0;
  556. }
  557. static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  558. struct ast_category *wiz)
  559. {
  560. struct ast_variable *wizvars = ast_category_first(wiz);
  561. struct ast_sorcery_object *obj = NULL;
  562. const char *id = ast_category_get_name(wiz);
  563. const char *transport = ast_variable_find_in_list(wizvars, "transport");
  564. const char *hint_context = hint_context = ast_variable_find_in_list(wizvars, "hint_context");
  565. const char *hint_exten = ast_variable_find_in_list(wizvars, "hint_exten");
  566. const char *hint_application= ast_variable_find_in_list(wizvars, "hint_application");
  567. char new_id[strlen(id) + MAX_ID_SUFFIX];
  568. RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
  569. variable_list_append_return(&vars, "@pjsip_wizard", id);
  570. variable_list_append_return(&vars, "aors", id);
  571. if (ast_strlen_zero(hint_context)) {
  572. hint_context = ast_variable_find_in_list(vars, "context");
  573. }
  574. if (ast_strlen_zero(hint_context)) {
  575. hint_context = "default";
  576. }
  577. if (!ast_strlen_zero(hint_exten)) {
  578. /* These are added so we can find and delete the hints when the endpoint gets deleted */
  579. variable_list_append_return(&vars, "@hint_context", hint_context);
  580. variable_list_append_return(&vars, "@hint_exten", hint_exten);
  581. }
  582. if (!ast_strlen_zero(transport)) {
  583. variable_list_append_return(&vars, "transport", transport);
  584. }
  585. if (is_variable_true(wizvars, "sends_auth")) {
  586. snprintf(new_id, sizeof(new_id), "%s-oauth", id);
  587. variable_list_append_return(&vars, "outbound_auth", new_id);
  588. }
  589. if (is_variable_true(wizvars, "accepts_auth")) {
  590. snprintf(new_id, sizeof(new_id), "%s-iauth", id);
  591. variable_list_append_return(&vars, "auth", new_id);
  592. }
  593. obj = create_object(sorcery, id, "endpoint", vars);
  594. if (!obj) {
  595. return -1;
  596. }
  597. if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
  598. otw->wizard->create(sorcery, otw->wizard_data, obj);
  599. }
  600. ao2_ref(obj, -1);
  601. if (!ast_strlen_zero(hint_exten)) {
  602. if (is_variable_true(wizvars, "has_hint")) {
  603. add_hints(hint_context, hint_exten, hint_application, id);
  604. } else {
  605. delete_extens(hint_context, hint_exten);
  606. }
  607. }
  608. return 0;
  609. }
  610. static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  611. struct ast_category *wiz, struct string_vector *remote_hosts_vector)
  612. {
  613. struct ast_variable *wizvars = ast_category_first(wiz);
  614. struct ast_sorcery_object *obj = NULL;
  615. const char *id = ast_category_get_name(wiz);
  616. char new_id[strlen(id) + MAX_ID_SUFFIX];
  617. int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
  618. int host_counter;
  619. RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "identify/"), ast_variables_destroy);
  620. snprintf(new_id, sizeof(new_id), "%s-identify", id);
  621. /* If accepting registrations, we don't need an identify. */
  622. if (is_variable_true(wizvars, "accepts_registrations")) {
  623. /* If one exists, delete it. */
  624. obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "identify", new_id);
  625. if (obj) {
  626. otw->wizard->delete(sorcery, otw->wizard_data, obj);
  627. ao2_ref(obj, -1);
  628. }
  629. return 0;
  630. }
  631. if (!host_count) {
  632. ast_log(LOG_ERROR,
  633. "Wizard '%s' must have 'remote_hosts' if it doesn't accept registrations.\n", id);
  634. return -1;
  635. }
  636. variable_list_append_return(&vars, "endpoint", id);
  637. variable_list_append_return(&vars, "@pjsip_wizard", id);
  638. if (!ast_variable_find_in_list(vars, "match")) {
  639. for (host_counter = 0; host_counter < host_count; host_counter++) {
  640. char *rhost = AST_VECTOR_GET(remote_hosts_vector, host_counter);
  641. char host[strlen(rhost) + 1];
  642. char *colon;
  643. /* If there's a :port specified, we have to remove it. */
  644. strcpy(host, rhost); /* Safe */
  645. colon = strchr(host, ':');
  646. if (colon) {
  647. *colon = '\0';
  648. }
  649. variable_list_append_return(&vars, "match", host);
  650. }
  651. }
  652. obj = create_object(sorcery, new_id, "identify", vars);
  653. if (!obj) {
  654. return -1;
  655. }
  656. if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
  657. otw->wizard->create(sorcery, otw->wizard_data, obj);
  658. }
  659. ao2_ref(obj, -1);
  660. return 0;
  661. }
  662. static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  663. struct ast_category *wiz)
  664. {
  665. struct ast_variable *wizvars = ast_category_first(wiz);
  666. struct ast_sorcery_object *obj = NULL;
  667. const char *id = ast_category_get_name(wiz);
  668. char new_id[strlen(id) + MAX_ID_SUFFIX];
  669. RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "phoneprov/"), ast_variables_destroy);
  670. snprintf(new_id, sizeof(new_id), "%s-phoneprov", id);
  671. if (!is_variable_true(wizvars, "has_phoneprov")) {
  672. obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "phoneprov", new_id);
  673. if (obj) {
  674. otw->wizard->delete(sorcery, otw->wizard_data, obj);
  675. ao2_ref(obj, -1);
  676. }
  677. return 0;
  678. }
  679. if (!ast_variable_find_in_list(wizvars, "phoneprov/MAC")) {
  680. ast_log(LOG_ERROR,
  681. "Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
  682. return -1;
  683. }
  684. variable_list_append_return(&vars, "endpoint", id);
  685. variable_list_append_return(&vars, "@pjsip_wizard", id);
  686. obj = create_object(sorcery, new_id, "phoneprov", vars);
  687. if (!obj) {
  688. return -1;
  689. }
  690. if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
  691. otw->wizard->create(sorcery, otw->wizard_data, obj);
  692. }
  693. ao2_ref(obj, -1);
  694. return 0;
  695. }
  696. static int delete_existing_cb(void *obj, void *arg, int flags)
  697. {
  698. struct object_type_wizard *otw = arg;
  699. if (!strcmp(otw->object_type, "endpoint")) {
  700. const char *context = ast_sorcery_object_get_extended(obj, "hint_context");
  701. const char *exten = ast_sorcery_object_get_extended(obj, "hint_exten");
  702. if (!ast_strlen_zero(context) && !ast_strlen_zero(exten)) {
  703. delete_extens(context, exten);
  704. }
  705. }
  706. otw->wizard->delete(otw->sorcery, otw->wizard_data, obj);
  707. return CMP_MATCH;
  708. }
  709. static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  710. struct ast_category *wiz, struct string_vector *remote_hosts_vector)
  711. {
  712. struct ast_variable *search;
  713. struct ast_variable *wizvars = ast_category_first(wiz);
  714. const char *id = ast_category_get_name(wiz);
  715. const char *server_uri_pattern;
  716. const char *client_uri_pattern;
  717. const char *transport = ast_variable_find_in_list(wizvars, "transport");
  718. const char *username;
  719. char new_id[strlen(id) + MAX_ID_SUFFIX];
  720. int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
  721. int host_counter;
  722. RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "registration/"), ast_variables_destroy);
  723. RAII_VAR(struct ao2_container *, existing,
  724. ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
  725. if (!existing) {
  726. return -1;
  727. }
  728. /* Find any existing registrations. */
  729. search = ast_variable_new("@pjsip_wizard", id, "");
  730. if (!search) {
  731. return -1;
  732. }
  733. otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, "registration", existing, search);
  734. ast_variables_destroy(search);
  735. /* If not sending registrations, delete ALL existing registrations for this wizard. */
  736. if (!is_variable_true(wizvars, "sends_registrations")) {
  737. if (ao2_container_count(existing) > 0) {
  738. ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
  739. }
  740. return 0;
  741. }
  742. if (!host_count) {
  743. ast_log(LOG_ERROR, "Wizard '%s' must have 'remote_hosts' if it sends registrations.\n", id);
  744. return -1;
  745. }
  746. variable_list_append_return(&vars, "@pjsip_wizard", id);
  747. if (!(server_uri_pattern = ast_variable_find_in_list(wizvars, "server_uri_pattern"))) {
  748. server_uri_pattern = "sip:${REMOTE_HOST}";
  749. }
  750. if (!(client_uri_pattern = ast_variable_find_in_list(wizvars, "client_uri_pattern"))) {
  751. client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
  752. }
  753. if(is_variable_true(wizvars, "sends_auth")) {
  754. username = ast_variable_find_in_list(wizvars, "outbound_auth/username");
  755. } else {
  756. username = id;
  757. }
  758. /* Unlike aor and identify, we need to create a separate registration object
  759. * for each remote host.
  760. */
  761. for (host_counter = 0; host_counter < host_count; host_counter++) {
  762. struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
  763. AST_VECTOR_GET(remote_hosts_vector, host_counter));
  764. struct ast_var_t *un = ast_var_assign("USERNAME", username);
  765. struct ast_sorcery_object *obj;
  766. RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
  767. RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
  768. RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
  769. AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
  770. AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
  771. if (!ast_strlen_zero(server_uri_pattern)) {
  772. ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
  773. server_uri_pattern);
  774. variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
  775. }
  776. if (!ast_strlen_zero(client_uri_pattern)) {
  777. ast_str_reset(uri);
  778. ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
  779. client_uri_pattern);
  780. variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
  781. }
  782. if (is_variable_true(wizvars, "sends_auth")) {
  783. snprintf(new_id, sizeof(new_id), "%s-oauth", id);
  784. variable_list_append_return(&registration_vars, "outbound_auth", new_id);
  785. }
  786. if (!ast_strlen_zero(transport)) {
  787. variable_list_append_return(&registration_vars, "transport", transport);
  788. }
  789. snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
  790. obj = create_object(sorcery, new_id, "registration", registration_vars);
  791. if (!obj) {
  792. return -1;
  793. }
  794. if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
  795. otw->wizard->create(sorcery, otw->wizard_data, obj);
  796. }
  797. ao2_ref(obj, -1);
  798. /* Unlink it from the 'existing' container. Any left will be deleted from
  799. * sorcery. If it wasn't in the existing container, no harm.
  800. */
  801. ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_KEY, ast_sorcery_object_id_compare, new_id);
  802. }
  803. /* If there are any excess registrations, delete them. */
  804. if (ao2_container_count(existing) > 0) {
  805. ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
  806. }
  807. return 0;
  808. }
  809. static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
  810. struct ast_category *wiz)
  811. {
  812. struct ast_variable *wizvars = ast_category_first(wiz);
  813. struct string_vector remote_hosts_vector;
  814. const char *remote_hosts;
  815. int rc = -1;
  816. AST_VECTOR_INIT(&remote_hosts_vector, 16);
  817. remote_hosts = ast_variable_find_in_list(wizvars, "remote_hosts");
  818. if (!ast_strlen_zero(remote_hosts)) {
  819. char *host;
  820. char *hosts = ast_strdupa(remote_hosts);
  821. while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
  822. AST_VECTOR_APPEND(&remote_hosts_vector, ast_strdup(host));
  823. }
  824. }
  825. ast_debug(4, "%s handler starting.\n", otw->object_type);
  826. if (!strcmp(otw->object_type, "auth")) {
  827. rc = handle_auths(sorcery, otw, wiz);
  828. } else if (!strcmp(otw->object_type, "aor")) {
  829. rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
  830. } else if (!strcmp(otw->object_type, "endpoint")) {
  831. rc = handle_endpoint(sorcery, otw, wiz);
  832. } else if (!strcmp(otw->object_type, "identify")) {
  833. rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
  834. } else if (!strcmp(otw->object_type, "phoneprov")) {
  835. rc = handle_phoneprov(sorcery, otw, wiz);
  836. } else if (!strcmp(otw->object_type, "registration")) {
  837. rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
  838. }
  839. AST_VECTOR_REMOVE_CMP_UNORDERED(&remote_hosts_vector, NULL, NOT_EQUALS, ast_free);
  840. AST_VECTOR_FREE(&remote_hosts_vector);
  841. ast_debug(4, "%s handler complete. rc: %d\n", otw->object_type, rc);
  842. return rc;
  843. }
  844. /*
  845. * Everything below are the sorcery observers.
  846. */
  847. static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
  848. static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery);
  849. static void object_type_loaded_observer(const char *name,
  850. const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
  851. static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
  852. const char *object_type, struct ast_sorcery_wizard *wizard,
  853. const char *wizard_args, void *wizard_data);
  854. static void object_type_registered_observer(const char *name,
  855. struct ast_sorcery *sorcery, const char *object_type);
  856. const static struct ast_sorcery_global_observer global_observer = {
  857. .instance_created = instance_created_observer,
  858. .instance_destroying = instance_destroying_observer,
  859. };
  860. struct ast_sorcery_instance_observer observer = {
  861. .wizard_mapped = wizard_mapped_observer,
  862. .object_type_registered = object_type_registered_observer,
  863. .object_type_loaded = object_type_loaded_observer,
  864. };
  865. /*! \brief Called after an object type is loaded/reloaded */
  866. static void object_type_loaded_observer(const char *name,
  867. const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
  868. {
  869. struct ast_category *category = NULL;
  870. struct object_type_wizard *otw = NULL;
  871. char *filename = "pjsip_wizard.conf";
  872. struct ast_flags flags = { 0 };
  873. struct ast_config *cfg;
  874. if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
  875. /* Not interested. */
  876. return;
  877. }
  878. otw = find_wizard(object_type);
  879. if (!otw) {
  880. ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
  881. return;
  882. }
  883. if (reloaded && otw->last_config) {
  884. flags.flags = CONFIG_FLAG_FILEUNCHANGED;
  885. }
  886. cfg = ast_config_load2(filename, object_type, flags);
  887. if (!cfg) {
  888. ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
  889. return;
  890. } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
  891. ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
  892. return;
  893. } else if (cfg == CONFIG_STATUS_FILEINVALID) {
  894. ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
  895. return;
  896. }
  897. while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
  898. const char *id = ast_category_get_name(category);
  899. struct ast_category *last_cat = NULL;
  900. struct ast_variable *change_set = NULL;
  901. if (otw->last_config) {
  902. last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
  903. ast_sorcery_changeset_create(ast_category_first(category), ast_category_first(last_cat), &change_set);
  904. if (last_cat) {
  905. ast_category_delete(otw->last_config, last_cat);
  906. }
  907. }
  908. if (!last_cat || change_set) {
  909. ast_variables_destroy(change_set);
  910. ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
  911. if (wizard_apply_handler(sorcery, otw, category)) {
  912. ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
  913. }
  914. }
  915. }
  916. if (!otw->last_config) {
  917. otw->last_config = cfg;
  918. return;
  919. }
  920. /* Only wizards that weren't in the new config are left in last_config now so we need to delete
  921. * all objects belonging to them.
  922. */
  923. category = NULL;
  924. while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
  925. const char *id = ast_category_get_name(category);
  926. struct ast_variable *search;
  927. RAII_VAR(struct ao2_container *, existing,
  928. ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
  929. if (!existing) {
  930. ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
  931. break;
  932. }
  933. search = ast_variable_new("@pjsip_wizard", id, "");
  934. if (!search) {
  935. ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
  936. break;
  937. }
  938. otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
  939. ast_variables_destroy(search);
  940. if (ao2_container_count(existing) > 0) {
  941. ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
  942. reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
  943. ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
  944. delete_existing_cb, otw);
  945. }
  946. }
  947. ast_config_destroy(otw->last_config);
  948. otw->last_config = cfg;
  949. }
  950. /*! \brief When each wizard is mapped, save it off to the vector. */
  951. static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
  952. const char *object_type, struct ast_sorcery_wizard *wizard,
  953. const char *wizard_args, void *wizard_data)
  954. {
  955. struct object_type_wizard *otw;
  956. if (!is_one_of(object_type, object_types)) {
  957. /* Not interested. */
  958. return;
  959. }
  960. /* We're only interested in memory wizards with the pjsip_wizard tag. */
  961. if (wizard_args && !strcmp(wizard_args, "pjsip_wizard")) {
  962. otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
  963. otw->sorcery = sorcery;
  964. otw->wizard = wizard;
  965. otw->wizard_data = wizard_data;
  966. otw->last_config = NULL;
  967. strcpy(otw->object_type, object_type); /* Safe */
  968. AST_VECTOR_APPEND(&object_type_wizards, otw);
  969. ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
  970. }
  971. }
  972. /*! \brief When each object type is registered, map a memory wizard to it. */
  973. static void object_type_registered_observer(const char *name,
  974. struct ast_sorcery *sorcery, const char *object_type)
  975. {
  976. if (is_one_of(object_type, object_types)) {
  977. ast_sorcery_apply_wizard_mapping(sorcery, object_type, "memory", "pjsip_wizard", 0);
  978. }
  979. }
  980. /*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
  981. * Also, bump the module's ref count so it can't be unloaded before the sorcery instance is
  982. * destroyed.
  983. */
  984. static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
  985. {
  986. if (strcmp(name, "res_pjsip")) {
  987. return;
  988. }
  989. ast_module_ref(ast_module_info->self);
  990. ast_sorcery_instance_observer_add(sorcery, &observer);
  991. }
  992. /*! \brief When the res_pjsip instance is destroyed, remove the observer
  993. * and unref the module. This should then allow this module to unload cleanly.
  994. */
  995. static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
  996. {
  997. if (strcmp(name, "res_pjsip")) {
  998. return;
  999. }
  1000. ast_sorcery_instance_observer_remove(sorcery, &observer);
  1001. ast_module_unref(ast_module_info->self);
  1002. }
  1003. static int load_module(void)
  1004. {
  1005. AST_VECTOR_INIT(&object_type_wizards, 12);
  1006. ast_sorcery_global_observer_add(&global_observer);
  1007. return AST_MODULE_LOAD_SUCCESS;
  1008. }
  1009. static int unload_module(void)
  1010. {
  1011. ast_sorcery_global_observer_remove(&global_observer);
  1012. AST_VECTOR_REMOVE_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
  1013. AST_VECTOR_FREE(&object_type_wizards);
  1014. return 0;
  1015. }
  1016. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Config Wizard",
  1017. .support_level = AST_MODULE_SUPPORT_CORE,
  1018. .load = load_module,
  1019. .unload = unload_module,
  1020. .load_pri = AST_MODPRI_REALTIME_DRIVER,
  1021. );