fix authentication header extra space (issue #5329)
[asterisk/asterisk.git] / 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 /*
20  *
21  * Tone Management
22  * 
23  * This set of function allow us to play a list of tones on a channel.
24  * Each element has two frequencies, which are mixed together and a
25  * duration. For silence both frequencies can be set to 0.
26  * The playtones can be given as a comma separated string.
27  *
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <math.h>                       /* For PI */
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/indications.h"
40 #include "asterisk/frame.h"
41 #include "asterisk/options.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/utils.h"
46
47 static int midi_tohz[128] = {
48                         8,8,9,9,10,10,11,12,12,13,14,
49                         15,16,17,18,19,20,21,23,24,25,
50                         27,29,30,32,34,36,38,41,43,46,
51                         48,51,55,58,61,65,69,73,77,82,
52                         87,92,97,103,110,116,123,130,138,146,
53                         155,164,174,184,195,207,220,233,246,261,
54                         277,293,311,329,349,369,391,415,440,466,
55                         493,523,554,587,622,659,698,739,783,830,
56                         880,932,987,1046,1108,1174,1244,1318,1396,1479,
57                         1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,
58                         2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,
59                         4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,
60                         8869,9397,9956,10548,11175,11839,12543
61                         };
62
63 struct playtones_item {
64         int freq1;
65         int freq2;
66         int duration;
67         int modulate;
68 };
69
70 struct playtones_def {
71         int vol;
72         int reppos;
73         int nitems;
74         int interruptible;
75         struct playtones_item *items;
76 };
77
78 struct playtones_state {
79         int vol;
80         int reppos;
81         int nitems;
82         struct playtones_item *items;
83         int npos;
84         int pos;
85         int origwfmt;
86         struct ast_frame f;
87         unsigned char offset[AST_FRIENDLY_OFFSET];
88         short data[4000];
89 };
90
91 static void playtones_release(struct ast_channel *chan, void *params)
92 {
93         struct playtones_state *ps = params;
94         if (chan) {
95                 ast_set_write_format(chan, ps->origwfmt);
96         }
97         if (ps->items) free(ps->items);
98         free(ps);
99 }
100
101 static void * playtones_alloc(struct ast_channel *chan, void *params)
102 {
103         struct playtones_def *pd = params;
104         struct playtones_state *ps = malloc(sizeof(struct playtones_state));
105         if (!ps)
106                 return NULL;
107         memset(ps, 0, sizeof(struct playtones_state));
108         ps->origwfmt = chan->writeformat;
109         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
110                 ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
111                 playtones_release(NULL, ps);
112                 ps = NULL;
113         } else {
114                 ps->vol = pd->vol;
115                 ps->reppos = pd->reppos;
116                 ps->nitems = pd->nitems;
117                 ps->items = pd->items;
118         }
119         /* Let interrupts interrupt :) */
120         if (pd->interruptible)
121                 ast_set_flag(chan, AST_FLAG_WRITE_INT);
122         else
123                 ast_clear_flag(chan, AST_FLAG_WRITE_INT);
124         return ps;
125 }
126
127 static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
128 {
129         struct playtones_state *ps = data;
130         struct playtones_item *pi;
131         int x;
132         /* we need to prepare a frame with 16 * timelen samples as we're 
133          * generating SLIN audio
134          */
135         len = samples * 2;
136         if (len > sizeof(ps->data) / 2 - 1) {
137                 ast_log(LOG_WARNING, "Can't generate that much data!\n");
138                 return -1;
139         }
140         memset(&ps->f, 0, sizeof(ps->f));
141
142         pi = &ps->items[ps->npos];
143         for (x=0;x<len/2;x++) {
144                 if (pi->modulate)
145                 /* Modulate 1st tone with 2nd, to 90% modulation depth */
146                 ps->data[x] = ps->vol * 2 * (
147                         sin((pi->freq1 * 2.0 * M_PI / 8000.0) * (ps->pos + x)) *
148                         (0.9 * fabs(sin((pi->freq2 * 2.0 * M_PI / 8000.0) * (ps->pos + x))) + 0.1)
149                         );
150                 else
151                         /* Add 2 tones together */
152                         ps->data[x] = ps->vol * (
153                                 sin((pi->freq1 * 2.0 * M_PI / 8000.0) * (ps->pos + x)) +
154                                 sin((pi->freq2 * 2.0 * M_PI / 8000.0) * (ps->pos + x))
155                         );
156         }
157         ps->f.frametype = AST_FRAME_VOICE;
158         ps->f.subclass = AST_FORMAT_SLINEAR;
159         ps->f.datalen = len;
160         ps->f.samples = samples;
161         ps->f.offset = AST_FRIENDLY_OFFSET;
162         ps->f.data = ps->data;
163         ps->f.delivery.tv_sec = 0;
164         ps->f.delivery.tv_usec = 0;
165         ast_write(chan, &ps->f);
166
167         ps->pos += x;
168         if (pi->duration && ps->pos >= pi->duration * 8) {      /* item finished? */
169                 ps->pos = 0;                                    /* start new item */
170                 ps->npos++;
171                 if (ps->npos >= ps->nitems) {                   /* last item? */
172                         if (ps->reppos == -1)                   /* repeat set? */
173                                 return -1;
174                         ps->npos = ps->reppos;                  /* redo from top */
175                 }
176         }
177         return 0;
178 }
179
180 static struct ast_generator playtones = {
181         alloc: playtones_alloc,
182         release: playtones_release,
183         generate: playtones_generator,
184 };
185
186 int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
187 {
188         char *s, *data = ast_strdupa(playlst); /* cute */
189         struct playtones_def d = { vol, -1, 0, 1, NULL};
190         char *stringp=NULL;
191         char *separator;
192         if (!data)
193                 return -1;
194         if (vol < 1)
195                 d.vol = 8192;
196
197         d.interruptible = interruptible;
198         
199         stringp=data;
200         /* the stringp/data is not null here */
201         /* check if the data is separated with '|' or with ',' by default */
202         if (strchr(stringp,'|'))
203                 separator = "|";
204         else
205                 separator = ",";
206         s = strsep(&stringp,separator);
207         while (s && *s) {
208                 int freq1, freq2, time, modulate=0, midinote=0;
209
210                 if (s[0]=='!')
211                         s++;
212                 else if (d.reppos == -1)
213                         d.reppos = d.nitems;
214                 if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
215                         /* f1+f2/time format */
216                 } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
217                         /* f1+f2 format */
218                         time = 0;
219                 } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3) {
220                         /* f1*f2/time format */
221                         modulate = 1;
222                 } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) {
223                         /* f1*f2 format */
224                         time = 0;
225                         modulate = 1;
226                 } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
227                         /* f1/time format */
228                         freq2 = 0;
229                 } else if (sscanf(s, "%d", &freq1) == 1) {
230                         /* f1 format */
231                         freq2 = 0;
232                         time = 0;
233                 } else if (sscanf(s, "M%d+M%d/%d", &freq1, &freq2, &time) == 3) {
234                         /* Mf1+Mf2/time format */
235                         midinote = 1;
236                 } else if (sscanf(s, "M%d+M%d", &freq1, &freq2) == 2) {
237                         /* Mf1+Mf2 format */
238                         time = 0;
239                         midinote = 1;
240                 } else if (sscanf(s, "M%d*M%d/%d", &freq1, &freq2, &time) == 3) {
241                         /* Mf1*Mf2/time format */
242                         modulate = 1;
243                         midinote = 1;
244                 } else if (sscanf(s, "M%d*M%d", &freq1, &freq2) == 2) {
245                         /* Mf1*Mf2 format */
246                         time = 0;
247                         modulate = 1;
248                         midinote = 1;
249                 } else if (sscanf(s, "M%d/%d", &freq1, &time) == 2) {
250                         /* Mf1/time format */
251                         freq2 = -1;
252                         midinote = 1;
253                 } else if (sscanf(s, "M%d", &freq1) == 1) {
254                         /* Mf1 format */
255                         freq2 = -1;
256                         time = 0;
257                         midinote = 1;
258                 } else {
259                         ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
260                         return -1;
261                 }
262
263                 if (midinote) {
264                         /* midi notes must be between 0 and 127 */
265                         if ((freq1 >= 0) && (freq1 <= 127))
266                                 freq1 = midi_tohz[freq1];
267                         else
268                                 freq1 = 0;
269
270                         if ((freq2 >= 0) && (freq2 <= 127))
271                                 freq2 = midi_tohz[freq2];
272                         else
273                                 freq2 = 0;
274                 }
275
276                 d.items = realloc(d.items,(d.nitems+1)*sizeof(struct playtones_item));
277                 if (d.items == NULL) {
278                         ast_log(LOG_WARNING, "Realloc failed!\n");
279                         return -1;
280                 }
281                 d.items[d.nitems].freq1    = freq1;
282                 d.items[d.nitems].freq2    = freq2;
283                 d.items[d.nitems].duration = time;
284                 d.items[d.nitems].modulate = modulate;
285                 d.nitems++;
286
287                 s = strsep(&stringp,separator);
288         }
289
290         if (ast_activate_generator(chan, &playtones, &d)) {
291                 free(d.items);
292                 return -1;
293         }
294         return 0;
295 }
296
297 void ast_playtones_stop(struct ast_channel *chan)
298 {
299         ast_deactivate_generator(chan);
300 }
301
302 /*--------------------------------------------*/
303
304 struct tone_zone *tone_zones;
305 static struct tone_zone *current_tonezone;
306
307 /* Protect the tone_zones list (highly unlikely that two things would change
308  * it at the same time, but still! */
309 AST_MUTEX_DEFINE_EXPORTED(tzlock);
310
311 /* Set global indication country */
312 int ast_set_indication_country(const char *country)
313 {
314         if (country) {
315                 struct tone_zone *z = ast_get_indication_zone(country);
316                 if (z) {
317                         if (option_verbose > 2)
318                                 ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n",country);
319                         current_tonezone = z;
320                         return 0;
321                 }
322         }
323         return 1; /* not found */
324 }
325
326 /* locate tone_zone, given the country. if country == NULL, use the default country */
327 struct tone_zone *ast_get_indication_zone(const char *country)
328 {
329         struct tone_zone *tz;
330         int alias_loop = 0;
331
332         /* we need some tonezone, pick the first */
333         if (country == NULL && current_tonezone)
334                 return current_tonezone;        /* default country? */
335         if (country == NULL && tone_zones)
336                 return tone_zones;              /* any country? */
337         if (country == NULL)
338                 return 0;       /* not a single country insight */
339
340         if (ast_mutex_lock(&tzlock)) {
341                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
342                 return 0;
343         }
344         do {
345                 for (tz=tone_zones; tz; tz=tz->next) {
346                         if (strcasecmp(country,tz->country)==0) {
347                                 /* tone_zone found */
348                                 if (tz->alias && tz->alias[0]) {
349                                         country = tz->alias;
350                                         break;
351                                 }
352                                 ast_mutex_unlock(&tzlock);
353                                 return tz;
354                         }
355                 }
356         } while (++alias_loop<20 && tz);
357         ast_mutex_unlock(&tzlock);
358         if (alias_loop==20)
359                 ast_log(LOG_NOTICE,"Alias loop for '%s' forcefull broken\n",country);
360         /* nothing found, sorry */
361         return 0;
362 }
363
364 /* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
365 struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
366 {
367         struct tone_zone_sound *ts;
368
369         /* we need some tonezone, pick the first */
370         if (zone == NULL && current_tonezone)
371                 zone = current_tonezone;        /* default country? */
372         if (zone == NULL && tone_zones)
373                 zone = tone_zones;              /* any country? */
374         if (zone == NULL)
375                 return 0;       /* not a single country insight */
376
377         if (ast_mutex_lock(&tzlock)) {
378                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
379                 return 0;
380         }
381         for (ts=zone->tones; ts; ts=ts->next) {
382                 if (strcasecmp(indication,ts->name)==0) {
383                         /* found indication! */
384                         ast_mutex_unlock(&tzlock);
385                         return ts;
386                 }
387         }
388         /* nothing found, sorry */
389         ast_mutex_unlock(&tzlock);
390         return 0;
391 }
392
393 /* helper function to delete a tone_zone in its entirety */
394 static inline void free_zone(struct tone_zone* zone)
395 {
396         while (zone->tones) {
397                 struct tone_zone_sound *tmp = zone->tones->next;
398                 free((void*)zone->tones->name);
399                 free((void*)zone->tones->data);
400                 free(zone->tones);
401                 zone->tones = tmp;
402         }
403         if (zone->ringcadance)
404                 free((void*)zone->ringcadance);
405         free(zone);
406 }
407
408 /*--------------------------------------------*/
409
410 /* add a new country, if country exists, it will be replaced. */
411 int ast_register_indication_country(struct tone_zone *zone)
412 {
413         struct tone_zone *tz,*pz;
414
415         if (ast_mutex_lock(&tzlock)) {
416                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
417                 return -1;
418         }
419         for (pz=NULL,tz=tone_zones; tz; pz=tz,tz=tz->next) {
420                 if (strcasecmp(zone->country,tz->country)==0) {
421                         /* tone_zone already there, replace */
422                         zone->next = tz->next;
423                         if (pz)
424                                 pz->next = zone;
425                         else
426                                 tone_zones = zone;
427                         /* if we are replacing the default zone, re-point it */
428                         if (tz == current_tonezone)
429                                 current_tonezone = zone;
430                         /* now free the previous zone */
431                         free_zone(tz);
432                         ast_mutex_unlock(&tzlock);
433                         return 0;
434                 }
435         }
436         /* country not there, add */
437         zone->next = NULL;
438         if (pz)
439                 pz->next = zone;
440         else
441                 tone_zones = zone;
442         ast_mutex_unlock(&tzlock);
443
444         if (option_verbose > 2)
445                 ast_verbose(VERBOSE_PREFIX_3 "Registered indication country '%s'\n",zone->country);
446         return 0;
447 }
448
449 /* remove an existing country and all its indications, country must exist.
450  * Also, all countries which are an alias for the specified country are removed. */
451 int ast_unregister_indication_country(const char *country)
452 {
453         struct tone_zone *tz, *pz = NULL, *tmp;
454         int res = -1;
455
456         if (ast_mutex_lock(&tzlock)) {
457                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
458                 return -1;
459         }
460         tz = tone_zones;
461         while (tz) {
462                 if (country==NULL ||
463                     (strcasecmp(country, tz->country)==0 ||
464                      strcasecmp(country, tz->alias)==0)) {
465                         /* tone_zone found, remove */
466                         tmp = tz->next;
467                         if (pz)
468                                 pz->next = tmp;
469                         else
470                                 tone_zones = tmp;
471                         /* if we are unregistering the default country, w'll notice */
472                         if (tz == current_tonezone) {
473                                 ast_log(LOG_NOTICE,"Removed default indication country '%s'\n",tz->country);
474                                 current_tonezone = NULL;
475                         }
476                         if (option_verbose > 2)
477                                 ast_verbose(VERBOSE_PREFIX_3 "Unregistered indication country '%s'\n",tz->country);
478                         free_zone(tz);
479                         if (tone_zones == tz)
480                                 tone_zones = tmp;
481                         tz = tmp;
482                         res = 0;
483                 }
484                 else {
485                         /* next zone please */
486                         pz = tz;
487                         tz = tz->next;
488                 }
489         }
490         ast_mutex_unlock(&tzlock);
491         return res;
492 }
493
494 /* add a new indication to a tone_zone. tone_zone must exist. if the indication already
495  * exists, it will be replaced. */
496 int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist)
497 {
498         struct tone_zone_sound *ts,*ps;
499
500         /* is it an alias? stop */
501         if (zone->alias[0])
502                 return -1;
503
504         if (ast_mutex_lock(&tzlock)) {
505                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
506                 return -2;
507         }
508         for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
509                 if (strcasecmp(indication,ts->name)==0) {
510                         /* indication already there, replace */
511                         free((void*)ts->name);
512                         free((void*)ts->data);
513                         break;
514                 }
515         }
516         if (!ts) {
517                 /* not there, we have to add */
518                 ts = malloc(sizeof(struct tone_zone_sound));
519                 if (!ts) {
520                         ast_log(LOG_WARNING, "Out of memory\n");
521                         ast_mutex_unlock(&tzlock);
522                         return -2;
523                 }
524                 ts->next = NULL;
525         }
526         ts->name = strdup(indication);
527         ts->data = strdup(tonelist);
528         if (ts->name==NULL || ts->data==NULL) {
529                 ast_log(LOG_WARNING, "Out of memory\n");
530                 ast_mutex_unlock(&tzlock);
531                 return -2;
532         }
533         if (ps)
534                 ps->next = ts;
535         else
536                 zone->tones = ts;
537         ast_mutex_unlock(&tzlock);
538         return 0;
539 }
540
541 /* remove an existing country's indication. Both country and indication must exist */
542 int ast_unregister_indication(struct tone_zone *zone, const char *indication)
543 {
544         struct tone_zone_sound *ts,*ps = NULL, *tmp;
545         int res = -1;
546
547         /* is it an alias? stop */
548         if (zone->alias[0])
549                 return -1;
550
551         if (ast_mutex_lock(&tzlock)) {
552                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
553                 return -1;
554         }
555         ts = zone->tones;
556         while (ts) {
557                 if (strcasecmp(indication,ts->name)==0) {
558                         /* indication found */
559                         tmp = ts->next;
560                         if (ps)
561                                 ps->next = tmp;
562                         else
563                                 zone->tones = tmp;
564                         free((void*)ts->name);
565                         free((void*)ts->data);
566                         free(ts);
567                         ts = tmp;
568                         res = 0;
569                 }
570                 else {
571                         /* next zone please */
572                         ps = ts;
573                         ts = ts->next;
574                 }
575         }
576         /* indication not found, goodbye */
577         ast_mutex_unlock(&tzlock);
578         return res;
579 }