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