Fix a couple of memory leaks
[asterisk/asterisk.git] / res / res_indications.c
1 /** @file res_indications.c 
2  *
3  * Asterisk -- A telephony toolkit for Linux.
4  *
5  * Load the indications
6  * 
7  * Copyright (C) 2002, Pauline Middelink
8  *
9  * Pauline Middelink <middelink@polyware.nl>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License
13  *
14  * Load the country specific dialtones into the asterisk PBX.
15  */
16  
17 #include <unistd.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <stdlib.h>
21 #include <pthread.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <asterisk/lock.h>
26 #include <asterisk/file.h>
27 #include <asterisk/cli.h>
28 #include <asterisk/logger.h>
29 #include <asterisk/config.h>
30 #include <asterisk/channel.h>
31 #include <asterisk/pbx.h>
32 #include <asterisk/module.h>
33 #include <asterisk/translate.h>
34 #include <asterisk/indications.h>
35
36
37 // Globals
38 static const char dtext[] = "Indications Configuration";
39 static const char config[] = "indications.conf";
40
41 /*
42  * Help for commands provided by this module ...
43  */
44 static char help_add_indication[] =
45 "Usage: add indication <country> <indication> \"<tonelist>\"\n"
46 "       Add the given indication to the country.\n";
47
48 static char help_remove_indication[] =
49 "Usage: remove indication <country> <indication>\n"
50 "       Remove the given indication from the country.\n";
51
52 static char help_show_indications[] =
53 "Usage: show indications [<country> ...]\n"
54 "       Show either a condensed for of all country/indications, or the\n"
55 "       indications for the specified countries.\n";
56
57 /*
58  * Implementation of functions provided by this module
59  */
60
61 /*
62  * ADD INDICATION command stuff
63  */
64 static int handle_add_indication(int fd, int argc, char *argv[])
65 {
66         struct tone_zone *tz;
67         int created_country = 0;
68         if (argc != 5) return RESULT_SHOWUSAGE;
69
70         tz = ast_get_indication_zone(argv[2]);
71         if (!tz) {
72                 /* country does not exist, create it */
73                 ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n",argv[2]);
74
75                 tz = malloc(sizeof(struct tone_zone));
76                 if (!tz) {
77                         ast_log(LOG_WARNING, "Out of memory\n");
78                         return -1;
79                 }
80                 memset(tz,0,sizeof(struct tone_zone));
81                 strncpy(tz->country,argv[2],sizeof(tz->country)-1);
82                 if (ast_register_indication_country(tz)) {
83                         ast_log(LOG_WARNING, "Unable to register new country\n");
84                         free(tz);
85                         return -1;
86                 }
87                 created_country = 1;
88         }
89         if (ast_register_indication(tz,argv[3],argv[4])) {
90                 ast_log(LOG_WARNING, "Unable to register indication %s/%s\n",argv[2],argv[3]);
91                 if (created_country)
92                         ast_unregister_indication_country(argv[2]);
93                 return -1;
94         }
95         return 0;
96 }
97
98 /*
99  * REMOVE INDICATION command stuff
100  */
101 static int handle_remove_indication(int fd, int argc, char *argv[])
102 {
103         struct tone_zone *tz;
104         if (argc != 3 && argc != 4) return RESULT_SHOWUSAGE;
105
106         if (argc == 3) {
107                 /* remove entiry country */
108                 if (ast_unregister_indication_country(argv[2])) {
109                         ast_log(LOG_WARNING, "Unable to unregister indication country %s\n",argv[2]);
110                         return -1;
111                 }
112                 return 0;
113         }
114
115         tz = ast_get_indication_zone(argv[2]);
116         if (!tz) {
117                 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n",argv[2],argv[3]);
118                 return -1;
119         }
120         if (ast_unregister_indication(tz,argv[3])) {
121                 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n",argv[2],argv[3]);
122                 return -1;
123         }
124         return 0;
125 }
126
127 /*
128  * SHOW INDICATIONS command stuff
129  */
130 static int handle_show_indications(int fd, int argc, char *argv[])
131 {
132         struct tone_zone *tz;
133         char buf[256];
134         int found_country = 0;
135
136         if (ast_mutex_lock(&tzlock)) {
137                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
138                 return 0;
139         }
140         if (argc == 2) {
141                 /* no arguments, show a list of countries */
142                 ast_cli(fd,"Country Alias   Description\n"
143                            "===========================\n");
144                 for (tz=tone_zones; tz; tz=tz->next) {
145                         ast_cli(fd,"%-7.7s %-7.7s %s\n", tz->country, tz->alias, tz->description);
146                 }
147                 ast_mutex_unlock(&tzlock);
148                 return 0;
149         }
150         /* there was a request for specific country(ies), lets humor them */
151         for (tz=tone_zones; tz; tz=tz->next) {
152                 int i,j;
153                 for (i=2; i<argc; i++) {
154                         if (strcasecmp(tz->country,argv[i])==0 &&
155                             !tz->alias[0]) {
156                                 struct tone_zone_sound* ts;
157                                 if (!found_country) {
158                                         found_country = 1;
159                                         ast_cli(fd,"Country Indication      PlayList\n"
160                                                    "=====================================\n");
161                                 }
162                                 j = snprintf(buf,sizeof(buf),"%-7.7s %-15.15s ",tz->country,"<ringcadance>");
163                                 for (i=0; i<tz->nrringcadance; i++) {
164                                         j += snprintf(buf+j,sizeof(buf)-j,"%d,",tz->ringcadance[i]);
165                                 }
166                                 if (tz->nrringcadance) j--;
167                                 strncpy(buf+j,"\n",sizeof(buf)-j);
168                                 ast_cli(fd,buf);
169                                 for (ts=tz->tones; ts; ts=ts->next)
170                                         ast_cli(fd,"%-7.7s %-15.15s %s\n",tz->country,ts->name,ts->data);
171                                 break;
172                         }
173                 }
174         }
175         if (!found_country)
176                 ast_cli(fd,"No countries matched your criteria.\n");
177         ast_mutex_unlock(&tzlock);
178         return -1;
179 }
180
181 /*
182  * Playtones command stuff
183  */
184 static int handle_playtones(struct ast_channel *chan, void *data)
185 {
186         struct tone_zone_sound *ts;
187         int res;
188
189         if (!data || !((char*)data)[0]) {
190                 ast_log(LOG_NOTICE,"Nothing to play\n");
191                 return -1;
192         }
193         ts = ast_get_indication_tone(chan->zone, (const char*)data);
194         if (ts && ts->data[0])
195                 res = ast_playtones_start(chan, 0, ts->data, 0);
196         else
197                 res = ast_playtones_start(chan, 0, (const char*)data, 0);
198         if (res)
199                 ast_log(LOG_NOTICE,"Unable to start playtones\n");
200         return res;
201 }
202
203 /*
204  * StopPlaylist command stuff
205  */
206 static int handle_stopplaytones(struct ast_channel *chan, void *data)
207 {
208         ast_playtones_stop(chan);
209         return 0;
210 }
211
212 /*
213  * Load module stuff
214  */
215 static int ind_load_module(void)
216 {
217         struct ast_config *cfg;
218         struct ast_variable *v;
219         char *cxt;
220         char *c;
221         struct tone_zone *tones;
222         const char *country = NULL;
223
224         /* that the following cast is needed, is yuk! */
225         /* yup, checked it out. It is NOT written to. */
226         cfg = ast_load((char *)config);
227         if (!cfg)
228                 return 0;
229
230         /* Use existing config to populate the Indication table */
231         cxt = ast_category_browse(cfg, NULL);
232         while(cxt) {
233                 /* All categories but "general" are considered countries */
234                 if (!strcasecmp(cxt, "general")) {
235                         cxt = ast_category_browse(cfg, cxt);
236                         continue;
237                 }
238                 tones = malloc(sizeof(struct tone_zone));
239                 if (!tones) {
240                         ast_log(LOG_WARNING,"Out of memory\n");
241                         ast_destroy(cfg);
242                         return -1;
243                 }
244                 memset(tones,0,sizeof(struct tone_zone));
245                 strncpy(tones->country,cxt,sizeof(tones->country));
246
247                 v = ast_variable_browse(cfg, cxt);
248                 while(v) {
249                         if (!strcasecmp(v->name, "description")) {
250                                 strncpy(tones->description, v->value, sizeof(tones->description)-1);
251                         } else if (!strcasecmp(v->name,"ringcadance")) {
252                                 char *ring,*rings = ast_strdupa(v->value);
253                                 c = rings;
254                                 ring = strsep(&c,",");
255                                 while (ring) {
256                                         int *tmp, val;
257                                         if (!isdigit(ring[0]) || (val=atoi(ring))==-1) {
258                                                 ast_log(LOG_WARNING,"Invalid ringcadance given '%s' at line %d.\n",ring,v->lineno);
259                                                 ring = strsep(&c,",");
260                                                 continue;
261                                         }
262                                         tmp = realloc(tones->ringcadance,(tones->nrringcadance+1)*sizeof(int));
263                                         if (!tmp) {
264                                                 ast_log(LOG_WARNING, "Out of memory\n");
265                                                 ast_destroy(cfg);
266                                                 return -1;
267                                         }
268                                         tones->ringcadance = tmp;
269                                         tmp[tones->nrringcadance] = val;
270                                         tones->nrringcadance++;
271                                         /* next item */
272                                         ring = strsep(&c,",");
273                                 }
274                         } else if (!strcasecmp(v->name,"alias")) {
275                                 char *countries = ast_strdupa(v->value);
276                                 c = countries;
277                                 country = strsep(&c,",");
278                                 while (country) {
279                                         struct tone_zone* azone = malloc(sizeof(struct tone_zone));
280                                         if (!azone) {
281                                                 ast_log(LOG_WARNING,"Out of memory\n");
282                                                 ast_destroy(cfg);
283                                                 return -1;
284                                         }
285                                         memset(azone,0,sizeof(struct tone_zone));
286                                         strncpy(azone->country,country,sizeof(azone->country));
287                                         strncpy(azone->alias, cxt, sizeof(azone->alias)-1);
288                                         if (ast_register_indication_country(azone)) {
289                                                 ast_log(LOG_WARNING, "Unable to register indication alias at line %d.\n",v->lineno);
290                                                 free(tones);
291                                         }
292                                         /* next item */
293                                         country = strsep(&c,",");
294                                 }
295                         } else {
296                                 // add tone to country
297                                 struct tone_zone_sound *ps,*ts;
298                                 for (ps=NULL,ts=tones->tones; ts; ps=ts, ts=ts->next) {
299                                         if (strcasecmp(v->name,ts->name)==0) {
300                                                 /* already there */
301                                                 ast_log(LOG_NOTICE,"Duplicate entry '%s', skipped.\n",v->name);
302                                                 goto out;
303                                         }
304                                 }
305                                 /* not there, add it to the back */
306                                 ts = malloc(sizeof(struct tone_zone_sound));
307                                 if (!ts) {
308                                         ast_log(LOG_WARNING, "Out of memory\n");
309                                         ast_destroy(cfg);
310                                         return -1;
311                                 }
312                                 ts->next = NULL;
313                                 ts->name = strdup(v->name);
314                                 ts->data = strdup(v->value);
315                                 if (ps)
316                                         ps->next = ts;
317                                 else
318                                         tones->tones = ts;
319                         }
320 out:                    v = v->next;
321                 }
322                 if (tones->description[0] || tones->alias[0] || tones->tones) {
323                         if (ast_register_indication_country(tones)) {
324                                 ast_log(LOG_WARNING, "Unable to register indication at line %d.\n",v->lineno);
325                                 free(tones);
326                         }
327                 } else free(tones);
328
329                 cxt = ast_category_browse(cfg, cxt);
330         }
331
332         /* determine which country is the default */
333         country = ast_variable_retrieve(cfg,"general","country");
334         if (!country || !*country || ast_set_indication_country(country))
335                 ast_log(LOG_WARNING,"Unable to set the default country (for indication tones)\n");
336
337         ast_destroy(cfg);
338         return 0;
339 }
340
341 /*
342  * CLI entries for commands provided by this module
343  */
344 static struct ast_cli_entry add_indication_cli =
345         { { "add", "indication", NULL }, handle_add_indication,
346                 "Add the given indication to the country", help_add_indication,
347                 NULL };
348
349 static struct ast_cli_entry remove_indication_cli =
350         { { "remove", "indication", NULL }, handle_remove_indication,
351                 "Remove the given indication from the country", help_remove_indication,
352                 NULL };
353
354 static struct ast_cli_entry show_indications_cli =
355         { { "show", "indications", NULL }, handle_show_indications,
356                 "Show a list of all country/indications", help_show_indications,
357                 NULL };
358
359 /*
360  * Standard module functions ...
361  */
362 int unload_module(void)
363 {
364         /* remove the registed indications... */
365         ast_unregister_indication_country(NULL);
366
367         /* and the functions */
368         ast_cli_unregister(&add_indication_cli);
369         ast_cli_unregister(&remove_indication_cli);
370         ast_cli_unregister(&show_indications_cli);
371         ast_unregister_application("Playlist");
372         ast_unregister_application("StopPlaylist");
373         return 0;
374 }
375
376 int load_module(void)
377 {
378         if (ind_load_module()) return -1;
379  
380         ast_cli_register(&add_indication_cli);
381         ast_cli_register(&remove_indication_cli);
382         ast_cli_register(&show_indications_cli);
383         ast_register_application("Playtones", handle_playtones, "Play a tone list","Play a tone list, either registered (through indications.conf) or a direct list of tones and durations.");
384         ast_register_application("StopPlaytones", handle_stopplaytones, "Stop playing a tone list","Stop playing a tone list");
385
386         return 0;
387 }
388
389 int reload(void)
390 {
391         /* remove the registed indications... */
392         ast_unregister_indication_country(NULL);
393
394         return ind_load_module();
395 }
396
397 char *description(void)
398 {
399         /* that the following cast is needed, is yuk! */
400         return (char*)dtext;
401 }
402
403 int usecount(void)
404 {
405         return 0;
406 }
407
408 char *key()
409 {
410         return ASTERISK_GPL_KEY;
411 }