Merge "dns_core: Allow zero-length DNS responses."
[asterisk/asterisk.git] / res / ari / resource_asterisk.c
1 /* -*- C -*-
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@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 Implementation for ARI stubs.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_REGISTER_FILE()
33
34 #include "asterisk/ast_version.h"
35 #include "asterisk/buildinfo.h"
36 #include "asterisk/module.h"
37 #include "asterisk/paths.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/sorcery.h"
40 #include "resource_asterisk.h"
41
42 static void return_sorcery_object(struct ast_sorcery *sorcery, void *sorcery_obj,
43         struct ast_ari_response *response)
44 {
45         RAII_VAR(struct ast_json *, return_set, NULL, ast_json_unref);
46         struct ast_variable *change_set;
47         struct ast_variable *it_change_set;
48
49         return_set = ast_json_array_create();
50         if (!return_set) {
51                 ast_ari_response_alloc_failed(response);
52                 return;
53         }
54
55         /* Note that we can't use the sorcery JSON change set directly,
56          * as it will hand us back an Object (with fields), and we need
57          * a more generic representation of whatever the API call asked
58          * for, i.e., a list of tuples.
59          */
60         change_set = ast_sorcery_objectset_create(sorcery, sorcery_obj);
61         if (!change_set) {
62                 ast_ari_response_alloc_failed(response);
63                 return;
64         }
65
66         for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) {
67                 struct ast_json *tuple;
68
69                 tuple = ast_json_pack("{s: s, s: s}",
70                         "attribute", it_change_set->name,
71                         "value", it_change_set->value);
72                 if (!tuple) {
73                         ast_variables_destroy(change_set);
74                         ast_ari_response_alloc_failed(response);
75                         return;
76                 }
77
78                 if (ast_json_array_append(return_set, tuple)) {
79                         ast_json_unref(tuple);
80                         ast_variables_destroy(change_set);
81                         ast_ari_response_alloc_failed(response);
82                         return;
83                 }
84         }
85         ast_variables_destroy(change_set);
86
87         ast_ari_response_ok(response, ast_json_ref(return_set));
88 }
89
90 void ast_ari_asterisk_get_object(struct ast_variable *headers,
91         struct ast_ari_asterisk_get_object_args *args,
92         struct ast_ari_response *response)
93 {
94         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
95         RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
96         RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
97
98
99         sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
100         if (!sorcery) {
101                 ast_ari_response_error(
102                         response, 404, "Not Found",
103                         "configClass '%s' not found",
104                         args->config_class);
105                 return;
106         }
107
108         object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
109         if (!object_type) {
110                 ast_ari_response_error(
111                         response, 404, "Not Found",
112                         "objectType '%s' not found",
113                         args->object_type);
114                 return;
115         }
116
117         sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
118         if (!sorcery_obj) {
119                 ast_ari_response_error(
120                         response, 404, "Not Found",
121                         "Object with id '%s' not found",
122                         args->id);
123                 return;
124         }
125
126         return_sorcery_object(sorcery, sorcery_obj, response);
127 }
128
129 void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response)
130 {
131         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
132         RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
133         RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
134         struct ast_json *fields;
135         struct ast_variable *update_set = NULL;
136         int created = 0;
137
138         sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
139         if (!sorcery) {
140                 ast_ari_response_error(
141                         response, 404, "Not Found",
142                         "configClass '%s' not found",
143                         args->config_class);
144                 return;
145         }
146
147         object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
148         if (!object_type) {
149                 ast_ari_response_error(
150                         response, 404, "Not Found",
151                         "objectType '%s' not found",
152                         args->object_type);
153                 return;
154         }
155
156         sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
157         if (!sorcery_obj) {
158                 ast_debug(5, "Sorcery object '%s' does not exist; creating it\n", args->id);
159                 sorcery_obj = ast_sorcery_alloc(sorcery, args->object_type, args->id);
160                 if (!sorcery_obj) {
161                         ast_ari_response_alloc_failed(response);
162                         return;
163                 }
164
165                 created = 1;
166         } else {
167                 void *copy;
168
169                 copy = ast_sorcery_copy(sorcery, sorcery_obj);
170                 if (!copy) {
171                         ast_ari_response_alloc_failed(response);
172                         return;
173                 }
174
175                 ao2_ref(sorcery_obj, -1);
176                 sorcery_obj = copy;
177         }
178
179         fields = ast_json_object_get(args->fields, "fields");
180         if (!fields && !created) {
181                 /* Whoops. We need data. */
182                 ast_ari_response_error(
183                         response, 400, "Bad request",
184                         "Fields must be provided to update object '%s'",
185                         args->id);
186                 return;
187         } else if (fields) {
188                 size_t i;
189
190                 for (i = 0; i < ast_json_array_size(fields); i++) {
191                         struct ast_variable *new_var;
192                         struct ast_json *json_value = ast_json_array_get(fields, i);
193
194                         if (!json_value) {
195                                 continue;
196                         }
197
198                         new_var = ast_variable_new(
199                                 ast_json_string_get(ast_json_object_get(json_value, "attribute")),
200                                 ast_json_string_get(ast_json_object_get(json_value, "value")),
201                                 "");
202                         if (!new_var) {
203                                 ast_variables_destroy(update_set);
204                                 ast_ari_response_alloc_failed(response);
205                                 return;
206                         }
207                         ast_variable_list_append(&update_set, new_var);
208                 }
209         }
210
211         /* APPLY! Note that a NULL update_set is fine (and necessary), as it
212          * will force validation on a newly created object.
213          */
214         if (ast_sorcery_objectset_apply(sorcery, sorcery_obj, update_set)) {
215                 ast_variables_destroy(update_set);
216                 ast_ari_response_error(
217                         response, 400, "Bad request",
218                         "%s of object '%s' failed field value validation",
219                         created ? "Creation" : "Update",
220                         args->id);
221                 return;
222         }
223
224         ast_variables_destroy(update_set);
225
226         if (created) {
227                 if (ast_sorcery_create(sorcery, sorcery_obj)) {
228                         ast_ari_response_error(
229                                 response, 403, "Forbidden",
230                                 "Cannot create sorcery objects of type '%s'",
231                                 args->object_type);
232                         return;
233                 }
234         } else {
235                 if (ast_sorcery_update(sorcery, sorcery_obj)) {
236                         ast_ari_response_error(
237                                 response, 403, "Forbidden",
238                                 "Cannot update sorcery objects of type '%s'",
239                                 args->object_type);
240                         return;
241                 }
242         }
243
244         return_sorcery_object(sorcery, sorcery_obj, response);
245 }
246
247
248 void ast_ari_asterisk_delete_object(struct ast_variable *headers,
249         struct ast_ari_asterisk_delete_object_args *args,
250         struct ast_ari_response *response)
251 {
252         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
253         RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
254         RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
255
256         sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
257         if (!sorcery) {
258                 ast_ari_response_error(
259                         response, 404, "Not Found",
260                         "configClass '%s' not found",
261                         args->config_class);
262                 return;
263         }
264
265         object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
266         if (!object_type) {
267                 ast_ari_response_error(
268                         response, 404, "Not Found",
269                         "objectType '%s' not found",
270                         args->object_type);
271                 return;
272         }
273
274         sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
275         if (!sorcery_obj) {
276                 ast_ari_response_error(
277                         response, 404, "Not Found",
278                         "Object with id '%s' not found",
279                         args->id);
280                 return;
281         }
282
283         if (ast_sorcery_delete(sorcery, sorcery_obj)) {
284                 ast_ari_response_error(
285                         response, 403, "Forbidden",
286                         "Could not delete object with id '%s'",
287                         args->id);
288                 return;
289         }
290
291         ast_ari_response_no_content(response);
292 }
293
294
295 void ast_ari_asterisk_get_info(struct ast_variable *headers,
296         struct ast_ari_asterisk_get_info_args *args,
297         struct ast_ari_response *response)
298 {
299         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
300         int show_all = args->only_count == 0;
301         int show_build = show_all;
302         int show_system = show_all;
303         int show_config = show_all;
304         int show_status = show_all;
305         size_t i;
306         int res = 0;
307
308         for (i = 0; i < args->only_count; ++i) {
309                 if (strcasecmp("build", args->only[i]) == 0) {
310                         show_build = 1;
311                 } else if (strcasecmp("system", args->only[i]) == 0) {
312                         show_system = 1;
313                 } else if (strcasecmp("config", args->only[i]) == 0) {
314                         show_config = 1;
315                 } else if (strcasecmp("status", args->only[i]) == 0) {
316                         show_status = 1;
317                 } else {
318                         ast_log(LOG_WARNING, "Unrecognized info section '%s'\n",
319                                 args->only[i]);
320                 }
321         }
322
323         json = ast_json_object_create();
324
325         if (show_build) {
326                 res |= ast_json_object_set(json, "build",
327                         ast_json_pack(
328                                 "{ s: s, s: s, s: s,"
329                                 "  s: s, s: s, s: s }",
330
331                                 "os", ast_build_os,
332                                 "kernel", ast_build_kernel,
333                                 "machine", ast_build_machine,
334
335                                 "options", AST_BUILDOPTS,
336                                 "date", ast_build_date,
337                                 "user", ast_build_user));
338         }
339
340         if (show_system) {
341                 char eid_str[128];
342
343                 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
344
345                 res |= ast_json_object_set(json, "system",
346                         ast_json_pack("{ s: s, s: s }",
347                                 "version", ast_get_version(),
348                                 "entity_id", eid_str));
349         }
350
351         if (show_config) {
352                 struct ast_json *config = ast_json_pack(
353                         "{ s: s, s: s,"
354                         " s: { s: s, s: s } }",
355
356                         "name", ast_config_AST_SYSTEM_NAME,
357                         "default_language", ast_defaultlanguage,
358
359                         "setid",
360                         "user", ast_config_AST_RUN_USER,
361                         "group", ast_config_AST_RUN_GROUP);
362
363                 res |= ast_json_object_set(json, "config", config);
364
365                 if (ast_option_maxcalls) {
366                         res |= ast_json_object_set(config, "max_channels",
367                                 ast_json_integer_create(ast_option_maxcalls));
368                 }
369
370                 if (ast_option_maxfiles) {
371                         res |= ast_json_object_set(config, "max_open_files",
372                                 ast_json_integer_create(ast_option_maxfiles));
373                 }
374
375                 if (ast_option_maxload) {
376                         res |= ast_json_object_set(config, "max_load",
377                                 ast_json_real_create(ast_option_maxload));
378                 }
379         }
380
381         if (show_status) {
382                 res |= ast_json_object_set(json, "status",
383                         ast_json_pack("{ s: o, s: o }",
384                                 "startup_time",
385                                 ast_json_timeval(ast_startuptime, NULL),
386                                 "last_reload_time",
387                                 ast_json_timeval(ast_lastreloadtime, NULL)));
388         }
389
390         if (res != 0) {
391                 ast_ari_response_alloc_failed(response);
392                 return;
393         }
394
395         ast_ari_response_ok(response, ast_json_ref(json));
396 }
397
398 /*!
399  * \brief Process module information and append to a json array
400  * \param module Resource name
401  * \param description Resource description
402  * \param usecnt Resource use count
403  * \param status Resource running status
404  * \param like
405  * \param support_level Resource support level
406  * \param module_data_list Resource array
407  *
408  * \retval 0 if no resource exists
409  * \retval 1 if resource exists
410  */
411 static int process_module_list(const char *module, const char *description, int usecnt,
412                                const char *status, const char *like,
413                                enum ast_module_support_level support_level, void *module_data_list)
414 {
415         struct ast_json *module_info;
416
417         module_info = ast_json_pack("{s: s, s: s, s: i, s: s, s: s}",
418                               "name", module,
419                               "description", description,
420                               "use_count", usecnt,
421                               "status", status,
422                               "support_level", ast_module_support_level_to_string(support_level));
423         if (!module_info) {
424                 return 0;
425         }
426         ast_json_array_append(module_data_list, module_info);
427         return 1;
428 }
429
430 void ast_ari_asterisk_list_modules(struct ast_variable *headers,
431         struct ast_ari_asterisk_list_modules_args *args,
432         struct ast_ari_response *response)
433 {
434         struct ast_json *json;
435
436         json = ast_json_array_create();
437         ast_update_module_list_data(&process_module_list, NULL, json);
438
439         ast_ari_response_ok(response, json);
440 }
441
442 /*!
443  * \brief Identify module by name and process resource information
444  * \param module Resource name
445  * \param description Resource description
446  * \param usecnt Resource use count
447  * \param status Resource running status
448  * \param like
449  * \param support_level Resource support level
450  * \param data JSON body for resource
451  * \param condition Name to match resource to
452  *
453  * \retval 0 if no resource exists
454  * \retval 1 if resource exists
455  */
456 static int identify_module(const char *module, const char *description, int usecnt,
457                            const char *status, const char *like,
458                            enum ast_module_support_level support_level, void *data,
459                            const char *condition)
460 {
461         int json_obj_set = 0;
462
463         if (strcmp(condition, module) != 0) {
464                 return 0;
465         }
466
467         json_obj_set += ast_json_object_set(data, "name", ast_json_string_create(module));
468         json_obj_set += ast_json_object_set(data, "description", ast_json_string_create(description));
469         json_obj_set += ast_json_object_set(data, "use_count", ast_json_integer_create(usecnt));
470         json_obj_set += ast_json_object_set(data, "status", ast_json_string_create(status));
471         json_obj_set += ast_json_object_set(data, "support_level", ast_json_string_create(
472                                             ast_module_support_level_to_string(support_level)));
473
474         if (json_obj_set != 0) {
475                 return 0;
476         }
477
478         return 1;
479 }
480
481 void ast_ari_asterisk_get_module(struct ast_variable *headers,
482         struct ast_ari_asterisk_get_module_args *args,
483         struct ast_ari_response *response)
484 {
485         struct ast_json *json;
486         int module_retrieved = 0;
487
488         ast_assert(response != NULL);
489
490         if (!ast_module_check(args->module_name)) {
491                 ast_ari_response_error(
492                         response, 404, "Not Found",
493                         "Module could not be found in running modules");
494                 return;
495         }
496
497         json = ast_json_object_create();
498         if (!json) {
499                 ast_ari_response_alloc_failed(response);
500                 return;
501         }
502
503         module_retrieved = ast_update_module_list_condition(&identify_module, NULL, json,
504                                                             args->module_name);
505         if (!module_retrieved) {
506                 ast_ari_response_error(
507                         response, 409, "Conflict",
508                         "Module information could not be retrieved");
509                 return;
510         }
511
512         ast_ari_response_ok(response, json);
513 }
514
515 void ast_ari_asterisk_load_module(struct ast_variable *headers,
516         struct ast_ari_asterisk_load_module_args *args,
517         struct ast_ari_response *response)
518 {
519         enum ast_module_load_result load_result;
520
521         ast_assert(response != NULL);
522
523         if (ast_module_check(args->module_name)) {
524                 ast_ari_response_error(
525                         response, 409, "Conflict",
526                         "Module is already loaded");
527                 return;
528         }
529
530         load_result = ast_load_resource(args->module_name);
531
532         if (load_result == AST_MODULE_LOAD_DECLINE) {
533                 ast_ari_response_error(
534                         response, 409, "Conflict",
535                         "Module load declined");
536                 return;
537         } else if (load_result == AST_MODULE_LOAD_SKIP) {
538                 ast_ari_response_error(
539                         response, 409, "Conflict",
540                         "Module was skipped");
541                 return;
542         } else if (load_result == AST_MODULE_LOAD_FAILURE) {
543                 ast_ari_response_error(
544                         response, 409, "Conflict",
545                         "Module could not be loaded properly");
546                 return;
547         }
548
549         ast_ari_response_no_content(response);
550 }
551
552 void ast_ari_asterisk_unload_module(struct ast_variable *headers,
553         struct ast_ari_asterisk_unload_module_args *args,
554         struct ast_ari_response *response)
555 {
556         int unload_result;
557         enum ast_module_unload_mode unload_mode = AST_FORCE_SOFT;
558
559         ast_assert(response != NULL);
560
561         if (!ast_module_check(args->module_name)) {
562                 ast_ari_response_error(
563                         response, 404, "Not Found",
564                         "Module not found in running modules");
565                 return;
566         }
567
568         unload_result = ast_unload_resource(args->module_name, unload_mode);
569
570         if (unload_result != 0) {
571                 ast_ari_response_error(
572                         response, 409, "Conflict",
573                         "Module could not be unloaded");
574                 return;
575         }
576
577         ast_ari_response_no_content(response);
578 }
579
580 void ast_ari_asterisk_reload_module(struct ast_variable *headers,
581         struct ast_ari_asterisk_reload_module_args *args,
582         struct ast_ari_response *response)
583 {
584         enum ast_module_reload_result reload_result;
585
586         ast_assert(response != NULL);
587
588         if (!ast_module_check(args->module_name)) {
589                 ast_ari_response_error(
590                         response, 404, "Not Found",
591                         "Module not found in running modules");
592                 return;
593         }
594
595         reload_result = ast_module_reload(args->module_name);
596
597         if (reload_result == AST_MODULE_RELOAD_NOT_FOUND) {
598                 ast_ari_response_error(
599                         response, 404, "Not Found",
600                         "Module could not be found");
601                 return;
602         } else if (reload_result == AST_MODULE_RELOAD_ERROR) {
603                 ast_ari_response_error(
604                         response, 409, "Conflict",
605                         "An unknown error occurred while reloading the module");
606                 return;
607         } else if (reload_result == AST_MODULE_RELOAD_IN_PROGRESS) {
608                 ast_ari_response_error(
609                         response, 409, "Conflict",
610                         "Another reload is currently in progress");
611                 return;
612         } else if (reload_result == AST_MODULE_RELOAD_UNINITIALIZED) {
613                 ast_ari_response_error(
614                         response, 409, "Conflict",
615                         "Module has not been initialized");
616                 return;
617         } else if (reload_result == AST_MODULE_RELOAD_NOT_IMPLEMENTED) {
618                 ast_ari_response_error(
619                         response, 409, "Conflict",
620                         "Module does not support reloading");
621                 return;
622         } else if (reload_result == AST_MODULE_RELOAD_QUEUED) {
623                 ast_ari_response_accepted(response);
624                 return;
625         }
626
627         ast_ari_response_no_content(response);
628 }
629
630 void ast_ari_asterisk_get_global_var(struct ast_variable *headers,
631         struct ast_ari_asterisk_get_global_var_args *args,
632         struct ast_ari_response *response)
633 {
634         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
635         RAII_VAR(struct ast_str *, tmp, NULL, ast_free);
636
637         const char *value;
638
639         ast_assert(response != NULL);
640
641         if (ast_strlen_zero(args->variable)) {
642                 ast_ari_response_error(
643                         response, 400, "Bad Request",
644                         "Variable name is required");
645                 return;
646         }
647
648         tmp = ast_str_create(32);
649         if (!tmp) {
650                 ast_ari_response_alloc_failed(response);
651                 return;
652         }
653
654         value = ast_str_retrieve_variable(&tmp, 0, NULL, NULL, args->variable);
655
656         if (!(json = ast_json_pack("{s: s}", "value", S_OR(value, "")))) {
657                 ast_ari_response_alloc_failed(response);
658                 return;
659         }
660
661         ast_ari_response_ok(response, ast_json_ref(json));
662 }
663
664 void ast_ari_asterisk_set_global_var(struct ast_variable *headers,
665         struct ast_ari_asterisk_set_global_var_args *args,
666         struct ast_ari_response *response)
667 {
668         ast_assert(response != NULL);
669
670         if (ast_strlen_zero(args->variable)) {
671                 ast_ari_response_error(
672                         response, 400, "Bad Request",
673                         "Variable name is required");
674                 return;
675         }
676
677         pbx_builtin_setvar_helper(NULL, args->variable, args->value);
678
679         ast_ari_response_no_content(response);
680 }