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