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