Add some more hold music
[asterisk/asterisk.git] / app.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Channel Management
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <signal.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <dirent.h>
22 #include <asterisk/channel.h>
23 #include <asterisk/pbx.h>
24 #include <asterisk/file.h>
25 #include <asterisk/app.h>
26 #include <asterisk/dsp.h>
27 #include <asterisk/logger.h>
28 #include <asterisk/options.h>
29 #include <asterisk/utils.h>
30 #include <asterisk/lock.h>
31 #include "asterisk.h"
32 #include "astconf.h"
33
34 /* set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
35    "ludicrous time" (essentially never times out) */
36 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
37 {
38         int res,to,fto;
39         /* XXX Merge with full version? XXX */
40         if (prompt) {
41                 res = ast_streamfile(c, prompt, c->language);
42                 if (res < 0)
43                         return res;
44         }
45         fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
46         to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
47
48         if (timeout > 0) fto = to = timeout;
49         if (timeout < 0) fto = to = 1000000000;
50         res = ast_readstring(c, s, maxlen, to, fto, "#");
51         return res;
52 }
53
54
55 int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
56 {
57         int res,to,fto;
58         if (prompt) {
59                 res = ast_streamfile(c, prompt, c->language);
60                 if (res < 0)
61                         return res;
62         }
63         fto = 6000;
64         to = 2000;
65         if (timeout > 0) fto = to = timeout;
66         if (timeout < 0) fto = to = 1000000000;
67         res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
68         return res;
69 }
70
71 int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec)
72 {
73         int res;
74         struct ast_filestream *writer;
75         int rfmt;
76         int totalms=0, total;
77         
78         struct ast_frame *f;
79         struct ast_dsp *sildet;
80         /* Play prompt if requested */
81         if (prompt) {
82                 res = ast_streamfile(c, prompt, c->language);
83                 if (res < 0)
84                         return res;
85                 res = ast_waitstream(c,"");
86                 if (res < 0)
87                         return res;
88         }
89         rfmt = c->readformat;
90         res = ast_set_read_format(c, AST_FORMAT_SLINEAR);
91         if (res < 0) {
92                 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
93                 return -1;
94         }
95         sildet = ast_dsp_new();
96         if (!sildet) {
97                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
98                 return -1;
99         }
100         writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666);
101         if (!writer) {
102                 ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt);
103                 ast_dsp_free(sildet);
104                 return -1;
105         }
106         for(;;) {
107                 if ((res = ast_waitfor(c, 2000)) < 0) {
108                         ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt);
109                         break;
110                 }
111                 if (res) {
112                         f = ast_read(c);
113                         if (!f) {
114                                 ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt);
115                                 break;
116                         }
117                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
118                                 /* Ended happily with DTMF */
119                                 ast_frfree(f);
120                                 break;
121                         } else if (f->frametype == AST_FRAME_VOICE) {
122                                 ast_dsp_silence(sildet, f, &total); 
123                                 if (total > silence) {
124                                         /* Ended happily with silence */
125                                         ast_frfree(f);
126                                         break;
127                                 }
128                                 totalms += f->samples / 8;
129                                 if (totalms > maxsec * 1000) {
130                                         /* Ended happily with too much stuff */
131                                         ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec);
132                                         ast_frfree(f);
133                                         break;
134                                 }
135                         }
136                         ast_frfree(f);
137                 }
138         }
139         res = ast_set_read_format(c, rfmt);
140         if (res)
141                 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name);
142         ast_dsp_free(sildet);
143         ast_closestream(writer);
144         return 0;
145 }
146
147 int ast_app_has_voicemail(const char *mailbox)
148 {
149         DIR *dir;
150         struct dirent *de;
151         char fn[256];
152         char tmp[256]="";
153         char *mb, *cur;
154         char *context;
155         int ret;
156         /* If no mailbox, return immediately */
157         if (ast_strlen_zero(mailbox))
158                 return 0;
159         if (strchr(mailbox, ',')) {
160                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
161                 mb = tmp;
162                 ret = 0;
163                 while((cur = strsep(&mb, ","))) {
164                         if (!ast_strlen_zero(cur)) {
165                                 if (ast_app_has_voicemail(cur))
166                                         return 1; 
167                         }
168                 }
169                 return 0;
170         }
171         strncpy(tmp, mailbox, sizeof(tmp) - 1);
172         context = strchr(tmp, '@');
173         if (context) {
174                 *context = '\0';
175                 context++;
176         } else
177                 context = "default";
178         snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
179         dir = opendir(fn);
180         if (!dir)
181                 return 0;
182         while ((de = readdir(dir))) {
183                 if (!strncasecmp(de->d_name, "msg", 3))
184                         break;
185         }
186         closedir(dir);
187         if (de)
188                 return 1;
189         return 0;
190 }
191
192 int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
193 {
194         DIR *dir;
195         struct dirent *de;
196         char fn[256];
197         char tmp[256]="";
198         char *mb, *cur;
199         char *context;
200         int ret;
201         if (newmsgs)
202                 *newmsgs = 0;
203         if (oldmsgs)
204                 *oldmsgs = 0;
205         /* If no mailbox, return immediately */
206         if (ast_strlen_zero(mailbox))
207                 return 0;
208         if (strchr(mailbox, ',')) {
209                 int tmpnew, tmpold;
210                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
211                 mb = tmp;
212                 ret = 0;
213                 while((cur = strsep(&mb, ", "))) {
214                         if (!ast_strlen_zero(cur)) {
215                                 if (ast_app_messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
216                                         return -1;
217                                 else {
218                                         if (newmsgs)
219                                                 *newmsgs += tmpnew; 
220                                         if (oldmsgs)
221                                                 *oldmsgs += tmpold;
222                                 }
223                         }
224                 }
225                 return 0;
226         }
227         strncpy(tmp, mailbox, sizeof(tmp) - 1);
228         context = strchr(tmp, '@');
229         if (context) {
230                 *context = '\0';
231                 context++;
232         } else
233                 context = "default";
234         if (newmsgs) {
235                 snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
236                 dir = opendir(fn);
237                 if (dir) {
238                         while ((de = readdir(dir))) {
239                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
240                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
241                                                 (*newmsgs)++;
242                                         
243                         }
244                         closedir(dir);
245                 }
246         }
247         if (oldmsgs) {
248                 snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/Old", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
249                 dir = opendir(fn);
250                 if (dir) {
251                         while ((de = readdir(dir))) {
252                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
253                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
254                                                 (*oldmsgs)++;
255                                         
256                         }
257                         closedir(dir);
258                 }
259         }
260         return 0;
261 }
262
263 int ast_dtmf_stream(struct ast_channel *chan,struct ast_channel *peer,char *digits,int between) 
264 {
265         char *ptr=NULL;
266         int res=0;
267         struct ast_frame f;
268         if (!between)
269                 between = 100;
270
271         if (peer)
272                 res = ast_autoservice_start(peer);
273
274         if (!res) {
275                 res = ast_waitfor(chan,100);
276                 if (res > -1) {
277                         for (ptr=digits;*ptr;*ptr++) {
278                                 if (*ptr == 'w') {
279                                         res = ast_safe_sleep(chan, 500);
280                                         if (res) 
281                                                 break;
282                                         continue;
283                                 }
284                                 memset(&f, 0, sizeof(f));
285                                 f.frametype = AST_FRAME_DTMF;
286                                 f.subclass = *ptr;
287                                 f.src = "ast_dtmf_stream";
288                                 if (strchr("0123456789*#abcdABCD",*ptr)==NULL) {
289                                         ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
290                                 } else {
291                                         res = ast_write(chan, &f);
292                                         if (res) 
293                                                 break;
294                                         /* pause between digits */
295                                         res = ast_safe_sleep(chan,between);
296                                         if (res) 
297                                                 break;
298                                 }
299                         }
300                 }
301                 if (peer)
302                         res = ast_autoservice_stop(peer);
303         }
304         return res;
305 }
306
307 struct linear_state {
308         int fd;
309         int autoclose;
310         int allowoverride;
311         int origwfmt;
312 };
313
314 static void linear_release(struct ast_channel *chan, void *params)
315 {
316         struct linear_state *ls = params;
317         if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
318                 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
319         }
320         if (ls->autoclose)
321                 close(ls->fd);
322         free(params);
323 }
324
325 static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
326 {
327         struct ast_frame f;
328         short buf[2048 + AST_FRIENDLY_OFFSET / 2];
329         struct linear_state *ls = data;
330         int res;
331         len = samples * 2;
332         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
333                 ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
334                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
335         }
336         memset(&f, 0, sizeof(f));
337         res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
338         if (res > 0) {
339                 f.frametype = AST_FRAME_VOICE;
340                 f.subclass = AST_FORMAT_SLINEAR;
341                 f.data = buf + AST_FRIENDLY_OFFSET/2;
342                 f.datalen = res;
343                 f.samples = res / 2;
344                 f.offset = AST_FRIENDLY_OFFSET;
345                 ast_write(chan, &f);
346                 if (res == len)
347                         return 0;
348         }
349         return -1;
350 }
351
352 static void *linear_alloc(struct ast_channel *chan, void *params)
353 {
354         struct linear_state *ls;
355         /* In this case, params is already malloc'd */
356         if (params) {
357                 ls = params;
358                 if (ls->allowoverride)
359                         chan->writeinterrupt = 1;
360                 else
361                         chan->writeinterrupt = 0;
362                 ls->origwfmt = chan->writeformat;
363                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
364                         ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
365                         free(ls);
366                         ls = params = NULL;
367                 }
368         }
369         return params;
370 }
371
372 static struct ast_generator linearstream = 
373 {
374         alloc: linear_alloc,
375         release: linear_release,
376         generate: linear_generator,
377 };
378
379 int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
380 {
381         struct linear_state *lin;
382         char tmpf[256] = "";
383         int res = -1;
384         int autoclose = 0;
385         if (fd < 0) {
386                 if (!filename || ast_strlen_zero(filename))
387                         return -1;
388                 autoclose = 1;
389                 if (filename[0] == '/') 
390                         strncpy(tmpf, filename, sizeof(tmpf) - 1);
391                 else
392                         snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_VAR_DIR, "sounds", filename);
393                 fd = open(tmpf, O_RDONLY);
394                 if (fd < 0){
395                         ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
396                         return -1;
397                 }
398         }
399         lin = malloc(sizeof(struct linear_state));
400         if (lin) {
401                 memset(lin, 0, sizeof(lin));
402                 lin->fd = fd;
403                 lin->allowoverride = allowoverride;
404                 lin->autoclose = autoclose;
405                 res = ast_activate_generator(chan, &linearstream, lin);
406         }
407         return res;
408 }
409
410 int ast_control_streamfile(struct ast_channel *chan, char *file, char *fwd, char *rev, char *stop, char *pause, int skipms) 
411 {
412         struct timeval started, ended;
413         long elapsed = 0,last_elapsed =0;
414         char *breaks;
415         int blen=2;
416         int res=0;
417
418         if (stop)
419                 blen += strlen(stop);
420         if (pause)
421                 blen += strlen(pause);
422
423         breaks = alloca(blen + 1);
424         breaks[0] = '\0';
425         strcat(breaks, stop);
426         strcat(breaks, pause);
427
428         if (chan->_state != AST_STATE_UP)
429                 res = ast_answer(chan);
430
431         if (chan)
432                 ast_stopstream(chan);
433
434         for (;;) {
435                 gettimeofday(&started,NULL);
436
437                 if (chan)
438                         ast_stopstream(chan);
439                 res = ast_streamfile(chan, file, chan->language);
440                 if (!res) {
441                         res = 1;
442                         if (elapsed) {
443                                 ast_stream_fastforward(chan->stream, elapsed);
444                                 last_elapsed = elapsed - 200;
445                         }
446                         if (res)
447                                 res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
448                         else
449                                 break;
450                 }
451
452                 if (res < 1)
453                         break;
454
455                 if (pause != NULL && strchr(pause, res)) {
456                         gettimeofday(&ended, NULL);
457                         elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000) + last_elapsed);
458                         for(;;) {
459                                 if (chan)
460                                         ast_stopstream(chan);
461                                 res = ast_waitfordigit(chan, 1000);
462                                 if(res == 0)
463                                         continue;
464                                 else if(res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
465                                         break;
466                         }
467                         if (res == *pause) {
468                                 res = 0;
469                                 continue;
470                         }
471                 }
472                 if (res == -1)
473                         break;
474
475                 /* if we get one of our stop chars, return it to the calling function */
476                 if (stop && strchr(stop, res)) {
477                         /* res = 0; */
478                         break;
479                 }
480         }
481         if (chan)
482                 ast_stopstream(chan);
483
484         return res;
485 }