res_pjsip: Fix warning by deferring implicit type cast.
[asterisk/asterisk.git] / res / res_pjsip / pjsip_cli.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, 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
19 #include "asterisk.h"
20
21 #include <pjsip.h>
22 #include <pjsip_ua.h>
23
24 #include "asterisk/res_pjsip.h"
25 #include "include/res_pjsip_private.h"
26 #include "asterisk/res_pjsip_cli.h"
27 #include "asterisk/acl.h"
28 #include "asterisk/cli.h"
29 #include "asterisk/astobj2.h"
30 #include "asterisk/hashtab.h"
31 #include "asterisk/utils.h"
32 #include "asterisk/sorcery.h"
33
34 static struct ao2_container *formatter_registry;
35
36 int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags)
37 {
38         struct ast_sip_cli_context *context = arg;
39         struct ast_variable *i;
40         int max_name_width = 13;
41         int max_value_width = 14;
42         int width;
43         char *separator;
44         struct ast_variable *objset;
45
46         if (!context->output_buffer) {
47                 return -1;
48         }
49
50         objset = ast_sorcery_objectset_create(ast_sip_get_sorcery(),obj);
51         if (!objset) {
52                 return -1;
53         }
54
55         for (i = objset; i; i = i->next) {
56                 if (i->name) {
57                         width = strlen(i->name);
58                         max_name_width = width > max_name_width ? width : max_name_width;
59                 }
60                 if (i->value) {
61                         width = strlen(i->value);
62                         max_value_width = width > max_value_width ? width : max_value_width;
63                 }
64         }
65
66         separator = ast_alloca(max_name_width + max_value_width + 8);
67
68         memset(separator, '=', max_name_width + max_value_width + 3);
69         separator[max_name_width + max_value_width + 3] = 0;
70
71         ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, "ParameterName", "ParameterValue");
72         ast_str_append(&context->output_buffer, 0, " %s\n", separator);
73
74         objset = ast_variable_list_sort(objset);
75
76         for (i = objset; i; i = i->next) {
77                 ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, i->name, i->value);
78         }
79
80         ast_variables_destroy(objset);
81
82         return 0;
83 }
84
85 static char *complete_show_sorcery_object(struct ao2_container *container,
86         struct ast_sip_cli_formatter_entry *formatter_entry,
87         const char *word, int state)
88 {
89         char *result = NULL;
90         int wordlen = strlen(word);
91         int which = 0;
92
93         struct ao2_iterator i = ao2_iterator_init(container, 0);
94         void *object;
95
96         while ((object = ao2_t_iterator_next(&i, "iterate thru endpoints table"))) {
97                 const char *id = formatter_entry->get_id(object);
98                 if (!strncasecmp(word, id, wordlen)
99                         && ++which > state) {
100                         result = ast_strdup(id);
101                 }
102                 ao2_t_ref(object, -1, "toss iterator endpoint ptr before break");
103                 if (result) {
104                         break;
105                 }
106         }
107         ao2_iterator_destroy(&i);
108
109         return result;
110 }
111
112 static void dump_str_and_free(int fd, struct ast_str *buf)
113 {
114         ast_cli(fd, "%s", ast_str_buffer(buf));
115         ast_free(buf);
116 }
117
118 char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
119 {
120         RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
121         RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
122         RAII_VAR(void *, object, NULL, ao2_cleanup);
123         int is_container = 0;
124         const char *cmd1;
125         const char *cmd2;
126         const char *object_id;
127         char formatter_type[64];
128         const char *regex;
129
130         struct ast_sip_cli_context context = {
131                 .indent_level = 0,
132                 .show_details = 0,
133                 .show_details_only_level_0 = 0,
134                 .recurse = 0,
135         };
136
137         if (cmd == CLI_INIT) {
138                 return NULL;
139         }
140
141         cmd1 = e->cmda[1];
142         cmd2 = e->cmda[2];
143         object_id = a->argv[3];
144
145         if (!ast_ends_with(cmd2, "s")) {
146                 ast_copy_string(formatter_type, cmd2, sizeof(formatter_type));
147                 is_container = 0;
148         } else if (ast_ends_with(cmd2, "ies")) {
149                 /* Take the plural "ies" off of the object name and re[place with "y". */
150                 int l = strlen(cmd2);
151                 snprintf(formatter_type, 64, "%*.*sy", l - 3, l - 3, cmd2);
152                 is_container = 1;
153         } else {
154                 /* Take the plural "s" off of the object name. */
155                 ast_copy_string(formatter_type, cmd2, strlen(cmd2));
156                 is_container = 1;
157         }
158
159         if (!strcmp(cmd1, "show")) {
160                 context.show_details_only_level_0 = !is_container;
161                 context.recurse = 1;
162         } else {
163                 is_container = 1;
164         }
165
166         if (cmd != CLI_GENERATE
167                 && is_container
168                 && a->argc >= 4
169                 && strcmp(object_id, "like") == 0) {
170                 if (ast_strlen_zero(a->argv[4])) {
171                         return CLI_SHOWUSAGE;
172                 }
173                 regex = a->argv[4];
174         } else {
175                 regex = "";
176         }
177
178         if (cmd == CLI_GENERATE
179                 && (is_container
180                         || a->argc > 4
181                         || (a->argc == 4 && ast_strlen_zero(a->word)))) {
182                 return CLI_SUCCESS;
183         }
184
185         context.output_buffer = ast_str_create(256);
186         if (!context.output_buffer) {
187                 return CLI_FAILURE;
188         }
189
190         formatter_entry = ast_sip_lookup_cli_formatter(formatter_type);
191         if (!formatter_entry) {
192                 ast_log(LOG_ERROR, "No formatter registered for object type %s.\n",
193                         formatter_type);
194                 ast_free(context.output_buffer);
195                 return CLI_FAILURE;
196         }
197         ast_str_append(&context.output_buffer, 0, "\n");
198         formatter_entry->print_header(NULL, &context, 0);
199         ast_str_append(&context.output_buffer, 0,
200                 "==========================================================================================\n\n");
201
202         if (is_container || cmd == CLI_GENERATE) {
203                 container = formatter_entry->get_container(regex);
204                 if (!container) {
205                         ast_cli(a->fd, "No container returned for object type %s.\n",
206                                 formatter_type);
207                         ast_free(context.output_buffer);
208                         return CLI_FAILURE;
209                 }
210         }
211
212         if (cmd == CLI_GENERATE) {
213                 ast_free(context.output_buffer);
214                 return complete_show_sorcery_object(container, formatter_entry, a->word, a->n);
215         }
216
217         if (is_container) {
218                 if (!ao2_container_count(container)) {
219                         ast_free(context.output_buffer);
220                         ast_cli(a->fd, "No objects found.\n\n");
221                         return CLI_SUCCESS;
222                 }
223                 ao2_callback(container, OBJ_NODATA, formatter_entry->print_body, &context);
224                 ast_str_append(&context.output_buffer, 0, "\nObjects found: %d\n", ao2_container_count(container));
225
226         } else {
227                 if (ast_strlen_zero(object_id)) {
228                         ast_free(context.output_buffer);
229                         ast_cli(a->fd, "No object specified.\n");
230                         return CLI_FAILURE;
231                 }
232
233                 object = formatter_entry->retrieve_by_id(object_id);
234                 if (!object) {
235                         ast_free(context.output_buffer);
236                         ast_cli(a->fd, "Unable to find object %s.\n\n", object_id);
237                         return CLI_SUCCESS;
238                 }
239                 formatter_entry->print_body(object, &context, 0);
240         }
241
242         ast_str_append(&context.output_buffer, 0, "\n");
243         dump_str_and_free(a->fd, context.output_buffer);
244         return CLI_SUCCESS;
245 }
246
247 static int formatter_sort(const void *obj, const void *arg, int flags)
248 {
249         const struct ast_sip_cli_formatter_entry *left_obj = obj;
250         const struct ast_sip_cli_formatter_entry *right_obj = arg;
251         const char *right_key = arg;
252         int cmp = 0;
253
254         switch (flags & OBJ_SEARCH_MASK) {
255         case OBJ_SEARCH_OBJECT:
256                 right_key = right_obj->name;
257                 /* Fall through */
258         case OBJ_SEARCH_KEY:
259                 cmp = strcmp(left_obj->name, right_key);
260                 break;
261         case OBJ_SEARCH_PARTIAL_KEY:
262                 cmp = strncmp(left_obj->name, right_key, strlen(right_key));
263                 break;
264         default:
265                 cmp = 0;
266                 break;
267         }
268
269         return cmp;
270 }
271
272 static int formatter_compare(void *obj, void *arg, int flags)
273 {
274         const struct ast_sip_cli_formatter_entry *left_obj = obj;
275         const struct ast_sip_cli_formatter_entry *right_obj = arg;
276         const char *right_key = arg;
277         int cmp = 0;
278
279         switch (flags & OBJ_SEARCH_MASK) {
280         case OBJ_SEARCH_OBJECT:
281                 right_key = right_obj->name;
282                 /* Fall through */
283         case OBJ_SEARCH_KEY:
284                 if (strcmp(left_obj->name, right_key) == 0) {;
285                         cmp = CMP_MATCH | CMP_STOP;
286                 }
287                 break;
288         case OBJ_SEARCH_PARTIAL_KEY:
289                 if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) {
290                         cmp = CMP_MATCH;
291                 }
292                 break;
293         default:
294                 cmp = 0;
295                 break;
296         }
297
298         return cmp;
299 }
300
301 static int formatter_hash(const void *obj, int flags)
302 {
303         const struct ast_sip_cli_formatter_entry *left_obj = obj;
304         if (flags & OBJ_SEARCH_OBJECT) {
305                 return ast_str_hash(left_obj->name);
306         } else if (flags & OBJ_SEARCH_KEY) {
307                 return ast_str_hash(obj);
308         }
309
310         return -1;
311 }
312
313 struct ast_sip_cli_formatter_entry *ast_sip_lookup_cli_formatter(const char *name)
314 {
315         return ao2_find(formatter_registry, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
316 }
317
318 int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
319 {
320         ast_assert(formatter != NULL);
321         ast_assert(formatter->name != NULL);
322         ast_assert(formatter->print_body != NULL);
323         ast_assert(formatter->print_header != NULL);
324         ast_assert(formatter->get_container != NULL);
325         ast_assert(formatter->iterate != NULL);
326         ast_assert(formatter->get_id != NULL);
327         ast_assert(formatter->retrieve_by_id != NULL);
328
329         ao2_link(formatter_registry, formatter);
330
331         return 0;
332 }
333
334 int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
335 {
336         if (formatter) {
337                 ao2_wrlock(formatter_registry);
338                 if (ao2_ref(formatter, -1) == 2) {
339                         ao2_unlink_flags(formatter_registry, formatter, OBJ_NOLOCK);
340                 }
341                 ao2_unlock(formatter_registry);
342         }
343         return 0;
344 }
345
346 static char *handle_pjsip_show_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
347 {
348         switch(cmd) {
349         case CLI_INIT:
350                 e->command = "pjsip show version";
351                 e->usage =
352                         "Usage: pjsip show version\n"
353                         "       Show the version of pjproject that res_pjsip is running against\n";
354                 return NULL;
355         case CLI_GENERATE:
356                 return NULL;
357         }
358
359         ast_cli(a->fd, "PJPROJECT version currently running against: %s\n", pj_get_version());
360
361         return CLI_SUCCESS;
362 }
363
364 static struct ast_cli_entry pjsip_cli[] = {
365         AST_CLI_DEFINE(handle_pjsip_show_version, "Show the version of pjproject in use"),
366 };
367
368 int ast_sip_initialize_cli(void)
369 {
370         formatter_registry = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 17,
371                 formatter_hash, formatter_sort, formatter_compare);
372
373         if (!formatter_registry) {
374                 ast_log(LOG_ERROR, "Unable to create formatter_registry.\n");
375                 return -1;
376         }
377
378         ast_cli_register_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
379
380         return 0;
381 }
382
383 void ast_sip_destroy_cli(void)
384 {
385         ast_cli_unregister_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
386         ao2_ref(formatter_registry, -1);
387 }