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