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