pjsip_cli: Fix memory leak in ast_sip_cli_print_sorcery_objectset.
[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 ast_hashtab *formatter_registry;
35
36 struct ast_sip_cli_formatter_entry *ast_sip_lookup_cli_formatter(const char *name)
37 {
38         struct ast_sip_cli_formatter_entry fake_entry = {
39                 .name = name,
40         };
41         return ast_hashtab_lookup(formatter_registry, &fake_entry);
42 }
43
44 int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags)
45 {
46         struct ast_sip_cli_context *context = arg;
47         struct ast_variable *i;
48         int max_name_width = 13;
49         int max_value_width = 14;
50         int width;
51         char *separator;
52         struct ast_variable *objset;
53
54         if (!context->output_buffer) {
55                 return -1;
56         }
57
58         objset = ast_sorcery_objectset_create(ast_sip_get_sorcery(),obj);
59         if (!objset) {
60                 return -1;
61         }
62
63         for (i = objset; i; i = i->next) {
64                 if (i->name) {
65                         width = strlen(i->name);
66                         max_name_width = width > max_name_width ? width : max_name_width;
67                 }
68                 if (i->value) {
69                         width = strlen(i->value);
70                         max_value_width = width > max_value_width ? width : max_value_width;
71                 }
72         }
73
74         separator = ast_alloca(max_name_width + max_value_width + 8);
75
76         memset(separator, '=', max_name_width + max_value_width + 3);
77         separator[max_name_width + max_value_width + 3] = 0;
78
79         ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, "ParameterName", "ParameterValue");
80         ast_str_append(&context->output_buffer, 0, " %s\n", separator);
81
82         objset = ast_variable_list_sort(objset);
83
84         for (i = objset; i; i = i->next) {
85                 ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, i->name, i->value);
86         }
87
88         ast_variables_destroy(objset);
89
90         return 0;
91 }
92
93 static char *complete_show_sorcery_object(struct ao2_container *container,
94         const char *word, int state)
95 {
96         char *result = NULL;
97         int wordlen = strlen(word);
98         int which = 0;
99
100         struct ao2_iterator i = ao2_iterator_init(container, 0);
101         void *object;
102
103         while ((object = ao2_t_iterator_next(&i, "iterate thru endpoints table"))) {
104                 if (!strncasecmp(word, ast_sorcery_object_get_id(object), wordlen)
105                         && ++which > state) {
106                         result = ast_strdup(ast_sorcery_object_get_id(object));
107                 }
108                 ao2_t_ref(object, -1, "toss iterator endpoint ptr before break");
109                 if (result) {
110                         break;
111                 }
112         }
113         ao2_iterator_destroy(&i);
114         return result;
115 }
116
117 static void dump_str_and_free(int fd, struct ast_str *buf)
118 {
119         ast_cli(fd, "%s", ast_str_buffer(buf));
120         ast_free(buf);
121 }
122
123 char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
124 {
125         RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
126         RAII_VAR(void *, object, NULL, ao2_cleanup);
127         int is_container = 0;
128         const char *cmd1;
129         const char *cmd2;
130         const char *object_id;
131         char formatter_type[64];
132         struct ast_sip_cli_formatter_entry *formatter_entry;
133
134         struct ast_sip_cli_context context = {
135                 .peers_mon_online = 0,
136                 .peers_mon_offline = 0,
137                 .peers_unmon_online = 0,
138                 .peers_unmon_offline = 0,
139                 .a = a,
140                 .indent_level = 0,
141                 .show_details = 0,
142                 .show_details_only_level_0 = 0,
143                 .recurse = 0,
144         };
145
146         if (cmd == CLI_INIT) {
147                 return NULL;
148         }
149
150         cmd1 = e->cmda[1];
151         cmd2 = e->cmda[2];
152         object_id = a->argv[3];
153
154         if (!ast_ends_with(cmd2, "s")) {
155                 ast_copy_string(formatter_type, cmd2, sizeof(formatter_type));
156                 is_container = 0;
157         } else {
158                 /* Take the plural "s" off of the object name. */
159                 ast_copy_string(formatter_type, cmd2, strlen(cmd2));
160                 is_container = 1;
161         }
162
163         if (!strcmp(cmd1, "show")) {
164                 context.show_details_only_level_0 = !is_container;
165                 context.recurse = 1;
166         } else {
167                 is_container = 1;
168         }
169
170         if (cmd == CLI_GENERATE
171                 && (is_container
172                         || a->argc > 4
173                         || (a->argc == 4 && ast_strlen_zero(a->word)))) {
174                 return CLI_SUCCESS;
175         }
176
177         context.output_buffer = ast_str_create(256);
178         if (!context.output_buffer) {
179                 return CLI_FAILURE;
180         }
181
182         formatter_entry = ast_sip_lookup_cli_formatter(formatter_type);
183         if (!formatter_entry) {
184                 ast_log(LOG_ERROR, "No formatter registered for object type %s.\n",
185                         formatter_type);
186                 ast_free(context.output_buffer);
187                 return CLI_FAILURE;
188         }
189         ast_str_append(&context.output_buffer, 0, "\n");
190         formatter_entry->print_header(NULL, &context, 0);
191         ast_str_append(&context.output_buffer, 0,
192                 " =========================================================================================\n\n");
193
194         if (is_container || cmd == CLI_GENERATE) {
195                 container = formatter_entry->get_container();
196                 if (!container) {
197                         ast_cli(a->fd, "No container returned for object type %s.\n",
198                                 formatter_type);
199                         ast_free(context.output_buffer);
200                         return CLI_FAILURE;
201                 }
202         }
203
204         if (cmd == CLI_GENERATE) {
205                 ast_free(context.output_buffer);
206                 return complete_show_sorcery_object(container, a->word, a->n);
207         }
208
209         if (is_container) {
210                 if (!ao2_container_count(container)) {
211                         dump_str_and_free(a->fd, context.output_buffer);
212                         ast_cli(a->fd, "No objects found.\n\n");
213                         return CLI_SUCCESS;
214                 }
215                 ao2_callback(container, OBJ_NODATA, formatter_entry->print_body, &context);
216         } else {
217                 if (ast_strlen_zero(object_id)) {
218                         dump_str_and_free(a->fd, context.output_buffer);
219                         ast_cli(a->fd, "No object specified.\n");
220                         return CLI_FAILURE;
221                 }
222                 object = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), formatter_type,
223                         object_id);
224                 if (!object) {
225                         dump_str_and_free(a->fd, context.output_buffer);
226                         ast_cli(a->fd, "Unable to find object %s.\n\n", object_id);
227                         return CLI_SUCCESS;
228                 }
229                 formatter_entry->print_body(object, &context, 0);
230         }
231
232         ast_str_append(&context.output_buffer, 0, "\n");
233         dump_str_and_free(a->fd, context.output_buffer);
234         return CLI_SUCCESS;
235 }
236
237 static int compare_formatters(const void *a, const void *b)
238 {
239         const struct ast_sip_cli_formatter_entry *afe = a;
240         const struct ast_sip_cli_formatter_entry *bfe = b;
241         if (!afe || !bfe) {
242                 ast_log(LOG_ERROR, "One of the arguments to compare_formatters was NULL\n");
243                 return -1;
244         }
245         return strcmp(afe->name, bfe->name);
246 }
247
248 static unsigned int hash_formatters(const void *a)
249 {
250         const struct ast_sip_cli_formatter_entry *afe = a;
251         return ast_hashtab_hash_string(afe->name);
252 }
253
254 int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
255 {
256         ast_hashtab_insert_safe(formatter_registry, formatter);
257         return 0;
258 }
259
260 int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
261 {
262         struct ast_sip_cli_formatter_entry *entry = ast_hashtab_lookup(formatter_registry, formatter);
263
264         if (!entry) {
265                 return -1;
266         }
267         ast_hashtab_remove_this_object(formatter_registry, entry);
268         return 0;
269 }
270
271 int ast_sip_initialize_cli(void)
272 {
273         formatter_registry = ast_hashtab_create(17, compare_formatters,
274                 ast_hashtab_resize_java, ast_hashtab_newsize_java, hash_formatters, 0);
275         if (!formatter_registry) {
276                 ast_log(LOG_ERROR, "Unable to create formatter_registry.\n");
277                 return -1;
278         }
279
280         return 0;
281 }
282
283 void ast_sip_destroy_cli(void)
284 {
285         if (formatter_registry) {
286                 ast_hashtab_destroy(formatter_registry, ast_free_ptr);
287         }
288 }