Merge "rtp_engine/res_rtp_asterisk: Fix RTP struct reentrancy crashes."
[asterisk/asterisk.git] / res / res_ari_model.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 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 Swagger validators.
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 #include "ari/ari_model_validators.h"
33 #include "asterisk/logger.h"
34 #include "asterisk/module.h"
35 #include "asterisk/utils.h"
36
37 #include <regex.h>
38
39 /* Regex to match date strings */
40 static regex_t date_regex;
41
42 /* Regex for YYYY-MM-DD */
43 #define REGEX_YMD "[0-9]{4}-[01][0-9]-[0-3][0-9]"
44
45 /* Regex for hh:mm(:ss(.s)); seconds and subseconds optional
46  * Handles the probably impossible case of a leap second, too */
47 #define REGEX_HMS "[0-2][0-9]:[0-5][0-9](:[0-6][0-9](.[0-9]+)?)?"
48
49 /* Regex for timezone: (+|-)hh(:mm), with optional colon. */
50 #define REGEX_TZ "(Z|[-+][0-2][0-9](:?[0-5][0-9])?)"
51
52 /* REGEX for ISO 8601, the time specifier optional */
53 #define ISO8601_PATTERN "^" REGEX_YMD "(T" REGEX_HMS REGEX_TZ ")?$"
54
55 static int check_type(struct ast_json *json, enum ast_json_type expected)
56 {
57         enum ast_json_type actual;
58
59         if (!json) {
60                 ast_log(LOG_ERROR, "Expected type %s, was NULL\n",
61                         ast_json_typename(expected));
62                 return 0;
63         }
64
65         actual = ast_json_typeof(json);
66         if (expected != actual) {
67                 ast_log(LOG_ERROR, "Expected type %s, was %s\n",
68                         ast_json_typename(expected), ast_json_typename(actual));
69                 return 0;
70         }
71         return 1;
72 }
73
74 static int check_range(intmax_t minval, intmax_t maxval, struct ast_json *json)
75 {
76         intmax_t v;
77
78         if (!check_type(json, AST_JSON_INTEGER)) {
79                 return 0;
80         }
81
82         v = ast_json_integer_get(json);
83
84         if (v < minval || maxval < v) {
85                 ast_log(LOG_ERROR, "Value out of range. Expected %jd <= %jd <= %jd\n", minval, v, maxval);
86                 return 0;
87         }
88         return 1;
89 }
90
91 int ast_ari_validate_void(struct ast_json *json)
92 {
93         return check_type(json, AST_JSON_NULL);
94 }
95
96 int ast_ari_validate_object(struct ast_json *json)
97 {
98         return check_type(json, AST_JSON_OBJECT);
99 }
100
101 int ast_ari_validate_byte(struct ast_json *json)
102 {
103         /* Java bytes are signed, which accounts for great fun for all */
104         return check_range(-128, 255, json);
105 }
106
107 int ast_ari_validate_boolean(struct ast_json *json)
108 {
109         enum ast_json_type actual = ast_json_typeof(json);
110         switch (actual) {
111         case AST_JSON_TRUE:
112         case AST_JSON_FALSE:
113                 return 1;
114         default:
115                 ast_log(LOG_ERROR, "Expected type boolean, was %s\n",
116                         ast_json_typename(actual));
117                 return 0;
118         }
119 }
120
121 int ast_ari_validate_int(struct ast_json *json)
122 {
123         /* Swagger int's are 32-bit */
124         return check_range(-2147483648LL, 2147483647LL, json);
125 }
126
127 int ast_ari_validate_long(struct ast_json *json)
128 {
129         /* All integral values are valid longs. No need for range check. */
130         return check_type(json, AST_JSON_INTEGER);
131 }
132
133 int ast_ari_validate_float(struct ast_json *json)
134 {
135         return check_type(json, AST_JSON_REAL);
136 }
137
138 int ast_ari_validate_double(struct ast_json *json)
139 {
140         return check_type(json, AST_JSON_REAL);
141 }
142
143 int ast_ari_validate_string(struct ast_json *json)
144 {
145         return check_type(json, AST_JSON_STRING);
146 }
147
148 int ast_ari_validate_date(struct ast_json *json)
149 {
150         /* Dates are ISO-8601 strings */
151         const char *str;
152         if (!check_type(json, AST_JSON_STRING)) {
153                 return 0;
154         }
155         str = ast_json_string_get(json);
156         ast_assert(str != NULL);
157         if (regexec(&date_regex, str, 0, NULL, 0) != 0) {
158                 ast_log(LOG_ERROR, "Date field is malformed: '%s'\n", str);
159                 return 0;
160         }
161         return 1;
162 }
163
164 int ast_ari_validate_list(struct ast_json *json, int (*fn)(struct ast_json *))
165 {
166         int res = 1;
167         size_t i;
168
169         if (!check_type(json, AST_JSON_ARRAY)) {
170                 return 0;
171         }
172
173         for (i = 0; i < ast_json_array_size(json); ++i) {
174                 int member_res;
175                 member_res = fn(ast_json_array_get(json, i));
176                 if (!member_res) {
177                         ast_log(LOG_ERROR,
178                                 "Array member %zu failed validation\n", i);
179                         res = 0;
180                 }
181         }
182
183         return res;
184 }
185
186 static int load_module(void)
187 {
188         int res;
189         res = regcomp(&date_regex, ISO8601_PATTERN,
190                 REG_EXTENDED | REG_ICASE | REG_NOSUB);
191
192         if (res != 0) {
193                 return AST_MODULE_LOAD_DECLINE;
194         }
195         return AST_MODULE_LOAD_SUCCESS;
196 }
197
198 static int unload_module(void)
199 {
200         regfree(&date_regex);
201         return 0;
202 }
203
204 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER | AST_MODFLAG_GLOBAL_SYMBOLS, "ARI Model validators",
205         .support_level = AST_MODULE_SUPPORT_CORE,
206         .load = load_module,
207         .unload = unload_module,
208         .load_pri = AST_MODPRI_APP_DEPEND,
209 );