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