issue #5648
[asterisk/asterisk.git] / res / res_musiconhold.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
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
20  *
21  * \brief Routines implementing music on hold
22  *
23  * \arg See also \ref Config_moh
24  * 
25  */
26
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <sys/time.h>
35 #include <sys/signal.h>
36 #include <netinet/in.h>
37 #include <sys/stat.h>
38 #include <dirent.h>
39 #ifdef ZAPATA_MOH
40 #ifdef __linux__
41 #include <linux/zaptel.h>
42 #else
43 #include <zaptel.h>
44 #endif /* __linux__ */
45 #endif
46 #include <unistd.h>
47 #include <sys/ioctl.h>
48
49 #include "asterisk.h"
50
51 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
52
53 #include "asterisk/lock.h"
54 #include "asterisk/file.h"
55 #include "asterisk/logger.h"
56 #include "asterisk/channel.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/options.h"
59 #include "asterisk/module.h"
60 #include "asterisk/translate.h"
61 #include "asterisk/say.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/config.h"
64 #include "asterisk/utils.h"
65 #include "asterisk/cli.h"
66
67 #define MAX_MOHFILES 512
68 #define MAX_MOHFILE_LEN 128
69
70 static char *app0 = "MusicOnHold";
71 static char *app1 = "WaitMusicOnHold";
72 static char *app2 = "SetMusicOnHold";
73 static char *app3 = "StartMusicOnHold";
74 static char *app4 = "StopMusicOnHold";
75
76 static char *synopsis0 = "Play Music On Hold indefinitely";
77 static char *synopsis1 = "Wait, playing Music On Hold";
78 static char *synopsis2 = "Set default Music On Hold class";
79 static char *synopsis3 = "Play Music On Hold";
80 static char *synopsis4 = "Stop Playing Music On Hold";
81
82 static char *descrip0 = "MusicOnHold(class): "
83 "Plays hold music specified by class.  If omitted, the default\n"
84 "music source for the channel will be used. Set the default \n"
85 "class with the SetMusicOnHold() application.\n"
86 "Returns -1 on hangup.\n"
87 "Never returns otherwise.\n";
88
89 static char *descrip1 = "WaitMusicOnHold(delay): "
90 "Plays hold music specified number of seconds.  Returns 0 when\n"
91 "done, or -1 on hangup.  If no hold music is available, the delay will\n"
92 "still occur with no sound.\n";
93
94 static char *descrip2 = "SetMusicOnHold(class): "
95 "Sets the default class for music on hold for a given channel.  When\n"
96 "music on hold is activated, this class will be used to select which\n"
97 "music is played.\n";
98
99 static char *descrip3 = "StartMusicOnHold(class): "
100 "Starts playing music on hold, uses default music class for channel.\n"
101 "Starts playing music specified by class.  If omitted, the default\n"
102 "music source for the channel will be used.  Always returns 0.\n";
103
104 static char *descrip4 = "StopMusicOnHold: "
105 "Stops playing music on hold.\n";
106
107 static int respawn_time = 20;
108
109 struct moh_files_state {
110         struct mohclass *class;
111         int origwfmt;
112         int samples;
113         int sample_queue;
114         unsigned char pos;
115         unsigned char save_pos;
116 };
117
118 #define MOH_QUIET               (1 << 0)
119 #define MOH_SINGLE              (1 << 1)
120 #define MOH_CUSTOM              (1 << 2)
121 #define MOH_RANDOMIZE           (1 << 3)
122
123 struct mohclass {
124         char name[MAX_MUSICCLASS];
125         char dir[256];
126         char args[256];
127         char mode[80];
128         char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
129         unsigned int flags;
130         int total_files;
131         int format;
132         int pid;                /* PID of mpg123 */
133         time_t start;
134         pthread_t thread;
135         struct mohdata *members;
136         /* Source of audio */
137         int srcfd;
138         /* FD for timing source */
139         int pseudofd;
140         struct mohclass *next;
141 };
142
143 struct mohdata {
144         int pipe[2];
145         int origwfmt;
146         struct mohclass *parent;
147         struct mohdata *next;
148 };
149
150 static struct mohclass *mohclasses;
151
152 AST_MUTEX_DEFINE_STATIC(moh_lock);
153
154 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
155 #define MPG_123 "/usr/bin/mpg123"
156 #define MAX_MP3S 256
157
158
159 static void ast_moh_free_class(struct mohclass **class) 
160 {
161         struct mohdata *members, *mtmp;
162         
163         members = (*class)->members;
164         while(members) {
165                 mtmp = members;
166                 members = members->next;
167                 free(mtmp);
168         }
169         free(*class);
170         *class = NULL;
171 }
172
173
174 static void moh_files_release(struct ast_channel *chan, void *data)
175 {
176         struct moh_files_state *state = chan->music_state;
177
178         if (chan && state) {
179                 if (option_verbose > 2)
180                         ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
181
182                 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
183                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
184                 }
185                 state->save_pos = state->pos + 1;
186         }
187 }
188
189
190 static int ast_moh_files_next(struct ast_channel *chan) 
191 {
192         struct moh_files_state *state = chan->music_state;
193         int tries;
194
195         if (state->save_pos) {
196                 state->pos = state->save_pos - 1;
197                 state->save_pos = 0;
198         } else {
199                 /* Try 20 times to find something good */
200                 for (tries=0;tries < 20;tries++) {
201                         state->samples = 0;
202                         if (chan->stream) {
203                                 ast_closestream(chan->stream);
204                                 chan->stream = NULL;
205                                 state->pos++;
206                         }
207
208                         if (ast_test_flag(state->class, MOH_RANDOMIZE))
209                                 state->pos = rand();
210
211                         /* check to see if this file's format can be opened */
212                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) != -1)
213                                 break;
214
215                 }
216         }
217
218         state->pos = state->pos % state->class->total_files;
219         
220         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
221                 ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
222                 return -1;
223         }
224         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
225                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
226                 state->pos++;
227                 return -1;
228         }
229
230         if (option_debug)
231                 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
232
233         if (state->samples)
234                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
235
236         return 0;
237 }
238
239
240 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
241 {
242         struct ast_frame *f = NULL;
243         
244         if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
245                 if (!ast_moh_files_next(chan))
246                         f = ast_readframe(chan->stream);
247         }
248
249         return f;
250 }
251
252 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
253 {
254         struct moh_files_state *state = chan->music_state;
255         struct ast_frame *f = NULL;
256         int res = 0;
257
258         state->sample_queue += samples;
259
260         while (state->sample_queue > 0) {
261                 if ((f = moh_files_readframe(chan))) {
262                         state->samples += f->samples;
263                         res = ast_write(chan, f);
264                         state->sample_queue -= f->samples;
265                         ast_frfree(f);
266                         if (res < 0) {
267                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
268                                 return -1;
269                         }
270                 } else
271                         return -1;      
272         }
273         return res;
274 }
275
276
277 static void *moh_files_alloc(struct ast_channel *chan, void *params)
278 {
279         struct moh_files_state *state;
280         struct mohclass *class = params;
281         int allocated = 0;
282
283         if (!chan->music_state && (state = malloc(sizeof(struct moh_files_state)))) {
284                 chan->music_state = state;
285                 allocated = 1;
286         } else 
287                 state = chan->music_state;
288
289         if (state) {
290                 if (allocated || state->class != class) {
291                         /* initialize */
292                         memset(state, 0, sizeof(struct moh_files_state));
293                         state->class = class;
294                 }
295
296                 state->origwfmt = chan->writeformat;
297
298                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
299                         ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
300                         free(chan->music_state);
301                         chan->music_state = NULL;
302                 } else {
303                         if (option_verbose > 2)
304                                 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
305                 }
306         }
307         
308         return chan->music_state;
309 }
310
311 static struct ast_generator moh_file_stream = 
312 {
313         alloc: moh_files_alloc,
314         release: moh_files_release,
315         generate: moh_files_generator,
316 };
317
318 static int spawn_mp3(struct mohclass *class)
319 {
320         int fds[2];
321         int files = 0;
322         char fns[MAX_MP3S][80];
323         char *argv[MAX_MP3S + 50];
324         char xargs[256];
325         char *argptr;
326         int argc = 0;
327         DIR *dir = NULL;
328         struct dirent *de;
329
330         
331         if (!strcasecmp(class->dir, "nodir")) {
332                 files = 1;
333         } else {
334                 dir = opendir(class->dir);
335                 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
336                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
337                         return -1;
338                 }
339         }
340
341         if (!ast_test_flag(class, MOH_CUSTOM)) {
342                 argv[argc++] = "mpg123";
343                 argv[argc++] = "-q";
344                 argv[argc++] = "-s";
345                 argv[argc++] = "--mono";
346                 argv[argc++] = "-r";
347                 argv[argc++] = "8000";
348                 
349                 if (!ast_test_flag(class, MOH_SINGLE)) {
350                         argv[argc++] = "-b";
351                         argv[argc++] = "2048";
352                 }
353                 
354                 argv[argc++] = "-f";
355                 
356                 if (ast_test_flag(class, MOH_QUIET))
357                         argv[argc++] = "4096";
358                 else
359                         argv[argc++] = "8192";
360                 
361                 /* Look for extra arguments and add them to the list */
362                 strncpy(xargs, class->args, sizeof(xargs) - 1);
363                 argptr = xargs;
364                 while (!ast_strlen_zero(argptr)) {
365                         argv[argc++] = argptr;
366                         argptr = strchr(argptr, ',');
367                         if (argptr) {
368                                 *argptr = '\0';
369                                 argptr++;
370                         }
371                 }
372         } else  {
373                 /* Format arguments for argv vector */
374                 strncpy(xargs, class->args, sizeof(xargs) - 1);
375                 argptr = xargs;
376                 while (!ast_strlen_zero(argptr)) {
377                         argv[argc++] = argptr;
378                         argptr = strchr(argptr, ' ');
379                         if (argptr) {
380                                 *argptr = '\0';
381                                 argptr++;
382                         }
383                 }
384         }
385
386
387         if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
388                 strncpy(fns[files], class->dir, sizeof(fns[files]) - 1);
389                 argv[argc++] = fns[files];
390                 files++;
391         } else if (dir) {
392                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
393                         if ((strlen(de->d_name) > 3) && 
394                             ((ast_test_flag(class, MOH_CUSTOM) && 
395                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
396                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
397                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
398                                 strncpy(fns[files], de->d_name, sizeof(fns[files]) - 1);
399                                 argv[argc++] = fns[files];
400                                 files++;
401                         }
402                 }
403         }
404         argv[argc] = NULL;
405         if (dir) {
406                 closedir(dir);
407         }
408         if (pipe(fds)) {        
409                 ast_log(LOG_WARNING, "Pipe failed\n");
410                 return -1;
411         }
412 #if 0
413         printf("%d files total, %d args total\n", files, argc);
414         {
415                 int x;
416                 for (x=0;argv[x];x++)
417                         printf("arg%d: %s\n", x, argv[x]);
418         }
419 #endif  
420         if (!files) {
421                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
422                 close(fds[0]);
423                 close(fds[1]);
424                 return -1;
425         }
426         if (time(NULL) - class->start < respawn_time) {
427                 sleep(respawn_time - (time(NULL) - class->start));
428         }
429         time(&class->start);
430         class->pid = fork();
431         if (class->pid < 0) {
432                 close(fds[0]);
433                 close(fds[1]);
434                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
435                 return -1;
436         }
437         if (!class->pid) {
438                 int x;
439                 close(fds[0]);
440                 /* Stdout goes to pipe */
441                 dup2(fds[1], STDOUT_FILENO);
442                 /* Close unused file descriptors */
443                 for (x=3;x<8192;x++) {
444                         if (-1 != fcntl(x, F_GETFL)) {
445                                 close(x);
446                         }
447                 }
448                 /* Child */
449                 chdir(class->dir);
450                 if (ast_test_flag(class, MOH_CUSTOM)) {
451                         execv(argv[0], argv);
452                 } else {
453                         /* Default install is /usr/local/bin */
454                         execv(LOCAL_MPG_123, argv);
455                         /* Many places have it in /usr/bin */
456                         execv(MPG_123, argv);
457                         /* Check PATH as a last-ditch effort */
458                         execvp("mpg123", argv);
459                 }
460                 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
461                 close(fds[1]);
462                 exit(1);
463         } else {
464                 /* Parent */
465                 close(fds[1]);
466         }
467         return fds[0];
468 }
469
470 static void *monmp3thread(void *data)
471 {
472 #define MOH_MS_INTERVAL         100
473
474         struct mohclass *class = data;
475         struct mohdata *moh;
476         char buf[8192];
477         short sbuf[8192];
478         int res, res2;
479         int len;
480         struct timeval tv, tv_tmp;
481
482         tv.tv_sec = 0;
483         tv.tv_usec = 0;
484         for(;/* ever */;) {
485                 /* Spawn mp3 player if it's not there */
486                 if (class->srcfd < 0) {
487                         if ((class->srcfd = spawn_mp3(class)) < 0) {
488                                 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
489                                 /* Try again later */
490                                 sleep(500);
491                         }
492                 }
493                 if (class->pseudofd > -1) {
494                         /* Pause some amount of time */
495                         res = read(class->pseudofd, buf, sizeof(buf));
496                 } else {
497                         long delta;
498                         /* Reliable sleep */
499                         tv_tmp = ast_tvnow();
500                         if (ast_tvzero(tv))
501                                 tv = tv_tmp;
502                         delta = ast_tvdiff_ms(tv_tmp, tv);
503                         if (delta < MOH_MS_INTERVAL) {  /* too early */
504                                 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
505                                 usleep(1000 * (MOH_MS_INTERVAL - delta));
506                         } else {
507                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
508                                 tv = tv_tmp;
509                         }
510                         res = 8 * MOH_MS_INTERVAL;      /* 8 samples per millisecond */
511                 }
512                 if (!class->members)
513                         continue;
514                 /* Read mp3 audio */
515                 len = ast_codec_get_len(class->format, res);
516                 
517                 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
518                         if (!res2) {
519                                 close(class->srcfd);
520                                 class->srcfd = -1;
521                                 if (class->pid) {
522                                         kill(class->pid, SIGKILL);
523                                         class->pid = 0;
524                                 }
525                         } else
526                                 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
527                         continue;
528                 }
529                 ast_mutex_lock(&moh_lock);
530                 moh = class->members;
531                 while (moh) {
532                         /* Write data */
533                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) 
534                                 if (option_debug)
535                                         ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
536                         moh = moh->next;
537                 }
538                 ast_mutex_unlock(&moh_lock);
539         }
540         return NULL;
541 }
542
543 static int moh0_exec(struct ast_channel *chan, void *data)
544 {
545         if (ast_moh_start(chan, data)) {
546                 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
547                 return -1;
548         }
549         while (!ast_safe_sleep(chan, 10000));
550         ast_moh_stop(chan);
551         return -1;
552 }
553
554 static int moh1_exec(struct ast_channel *chan, void *data)
555 {
556         int res;
557         if (!data || !atoi(data)) {
558                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
559                 return -1;
560         }
561         if (ast_moh_start(chan, NULL)) {
562                 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
563                 return -1;
564         }
565         res = ast_safe_sleep(chan, atoi(data) * 1000);
566         ast_moh_stop(chan);
567         return res;
568 }
569
570 static int moh2_exec(struct ast_channel *chan, void *data)
571 {
572         if (ast_strlen_zero(data)) {
573                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
574                 return -1;
575         }
576         strncpy(chan->musicclass, data, sizeof(chan->musicclass) - 1);
577         return 0;
578 }
579
580 static int moh3_exec(struct ast_channel *chan, void *data)
581 {
582         char *class = NULL;
583         if (data && strlen(data))
584                 class = data;
585         if (ast_moh_start(chan, class)) 
586                 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
587
588         return 0;
589 }
590
591 static int moh4_exec(struct ast_channel *chan, void *data)
592 {
593         ast_moh_stop(chan);
594
595         return 0;
596 }
597
598 static struct mohclass *get_mohbyname(char *name)
599 {
600         struct mohclass *moh;
601         moh = mohclasses;
602         while (moh) {
603                 if (!strcasecmp(name, moh->name))
604                         return moh;
605                 moh = moh->next;
606         }
607         return NULL;
608 }
609
610 static struct mohdata *mohalloc(struct mohclass *cl)
611 {
612         struct mohdata *moh;
613         long flags;
614         moh = malloc(sizeof(struct mohdata));
615         if (!moh)
616                 return NULL;
617         memset(moh, 0, sizeof(struct mohdata));
618         if (pipe(moh->pipe)) {
619                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
620                 free(moh);
621                 return NULL;
622         }
623         /* Make entirely non-blocking */
624         flags = fcntl(moh->pipe[0], F_GETFL);
625         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
626         flags = fcntl(moh->pipe[1], F_GETFL);
627         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
628         moh->parent = cl;
629         moh->next = cl->members;
630         cl->members = moh;
631         return moh;
632 }
633
634 static void moh_release(struct ast_channel *chan, void *data)
635 {
636         struct mohdata *moh = data, *prev, *cur;
637         int oldwfmt;
638         ast_mutex_lock(&moh_lock);
639         /* Unlink */
640         prev = NULL;
641         cur = moh->parent->members;
642         while (cur) {
643                 if (cur == moh) {
644                         if (prev)
645                                 prev->next = cur->next;
646                         else
647                                 moh->parent->members = cur->next;
648                         break;
649                 }
650                 prev = cur;
651                 cur = cur->next;
652         }
653         ast_mutex_unlock(&moh_lock);
654         close(moh->pipe[0]);
655         close(moh->pipe[1]);
656         oldwfmt = moh->origwfmt;
657         free(moh);
658         if (chan) {
659                 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
660                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
661                 if (option_verbose > 2)
662                         ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
663         }
664 }
665
666 static void *moh_alloc(struct ast_channel *chan, void *params)
667 {
668         struct mohdata *res;
669         struct mohclass *class = params;
670
671         res = mohalloc(class);
672         if (res) {
673                 res->origwfmt = chan->writeformat;
674                 if (ast_set_write_format(chan, class->format)) {
675                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
676                         moh_release(NULL, res);
677                         res = NULL;
678                 }
679                 if (option_verbose > 2)
680                         ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
681         }
682         return res;
683 }
684
685 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
686 {
687         struct ast_frame f;
688         struct mohdata *moh = data;
689         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
690         int res;
691
692         if (!moh->parent->pid)
693                 return -1;
694
695         len = ast_codec_get_len(moh->parent->format, samples);
696
697         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
698                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
699                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
700         }
701         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
702 #if 0
703         if (res != len) {
704                 ast_log(LOG_WARNING, "Read only %d of %d bytes: %s\n", res, len, strerror(errno));
705         }
706 #endif
707         if (res <= 0)
708                 return 0;
709
710         memset(&f, 0, sizeof(f));
711         
712         f.frametype = AST_FRAME_VOICE;
713         f.subclass = moh->parent->format;
714         f.mallocd = 0;
715         f.datalen = res;
716         f.data = buf + AST_FRIENDLY_OFFSET / 2;
717         f.offset = AST_FRIENDLY_OFFSET;
718         f.samples = ast_codec_get_samples(&f);
719
720         if (ast_write(chan, &f) < 0) {
721                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
722                 return -1;
723         }
724
725         return 0;
726 }
727
728 static struct ast_generator mohgen = 
729 {
730         alloc: moh_alloc,
731         release: moh_release,
732         generate: moh_generate,
733 };
734
735 static int moh_scan_files(struct mohclass *class) {
736
737         DIR *files_DIR;
738         struct dirent *files_dirent;
739         char path[512];
740         char filepath[MAX_MOHFILE_LEN];
741         char *ext;
742         struct stat statbuf;
743         int dirnamelen;
744         int i;
745         
746         files_DIR = opendir(class->dir);
747         if (!files_DIR) {
748                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
749                 return -1;
750         }
751
752         class->total_files = 0;
753         dirnamelen = strlen(class->dir) + 2;
754         getcwd(path, 512);
755         chdir(class->dir);
756         memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
757         while ((files_dirent = readdir(files_DIR))) {
758                 if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
759                         continue;
760
761                 snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
762
763                 if (stat(filepath, &statbuf))
764                         continue;
765
766                 if (!S_ISREG(statbuf.st_mode))
767                         continue;
768
769                 if ((ext = strrchr(filepath, '.'))) {
770                         *ext = '\0';
771                         ext++;
772                 }
773
774                 /* if the file is present in multiple formats, ensure we only put it into the list once */
775                 for (i = 0; i < class->total_files; i++)
776                         if (!strcmp(filepath, class->filearray[i]))
777                                 break;
778
779                 if (i == class->total_files)
780                         strcpy(class->filearray[class->total_files++], filepath);
781         }
782
783         closedir(files_DIR);
784         chdir(path);
785         return class->total_files;
786 }
787
788 static int moh_register(struct mohclass *moh)
789 {
790 #ifdef ZAPATA_MOH
791         int x;
792 #endif
793         ast_mutex_lock(&moh_lock);
794         if (get_mohbyname(moh->name)) {
795                 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
796                 free(moh);      
797                 ast_mutex_unlock(&moh_lock);
798                 return -1;
799         }
800         ast_mutex_unlock(&moh_lock);
801
802         time(&moh->start);
803         moh->start -= respawn_time;
804         
805         if (!strcasecmp(moh->mode, "files")) {
806                 if (!moh_scan_files(moh)) {
807                         ast_moh_free_class(&moh);
808                         return -1;
809                 }
810                 if (strchr(moh->args, 'r'))
811                         ast_set_flag(moh, MOH_RANDOMIZE);
812         } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
813
814                 if (!strcasecmp(moh->mode, "custom"))
815                         ast_set_flag(moh, MOH_CUSTOM);
816                 else if (!strcasecmp(moh->mode, "mp3nb"))
817                         ast_set_flag(moh, MOH_SINGLE);
818                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
819                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
820                 else if (!strcasecmp(moh->mode, "quietmp3"))
821                         ast_set_flag(moh, MOH_QUIET);
822                 
823                 moh->srcfd = -1;
824 #ifdef ZAPATA_MOH
825                 /* Open /dev/zap/pseudo for timing...  Is
826                    there a better, yet reliable way to do this? */
827                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
828                 if (moh->pseudofd < 0) {
829                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
830                 } else {
831                         x = 320;
832                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
833                 }
834 #else
835                 moh->pseudofd = -1;
836 #endif
837                 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
838                         ast_log(LOG_WARNING, "Unable to create moh...\n");
839                         if (moh->pseudofd > -1)
840                                 close(moh->pseudofd);
841                         ast_moh_free_class(&moh);
842                         return -1;
843                 }
844         } else {
845                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
846                 ast_moh_free_class(&moh);
847                 return -1;
848         }
849         ast_mutex_lock(&moh_lock);
850         moh->next = mohclasses;
851         mohclasses = moh;
852         ast_mutex_unlock(&moh_lock);
853         return 0;
854 }
855
856 static void local_ast_moh_cleanup(struct ast_channel *chan)
857 {
858         if (chan->music_state) {
859                 free(chan->music_state);
860                 chan->music_state = NULL;
861         }
862 }
863
864 static int local_ast_moh_start(struct ast_channel *chan, char *class)
865 {
866         struct mohclass *mohclass;
867
868         if (ast_strlen_zero(class))
869                 class = chan->musicclass;
870         if (ast_strlen_zero(class))
871                 class = "default";
872         ast_mutex_lock(&moh_lock);
873         mohclass = get_mohbyname(class);
874         ast_mutex_unlock(&moh_lock);
875
876         if (!mohclass) {
877                 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
878                 return -1;
879         }
880
881         ast_set_flag(chan, AST_FLAG_MOH);
882         if (mohclass->total_files) {
883                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
884         } else
885                 return ast_activate_generator(chan, &mohgen, mohclass);
886 }
887
888 static void local_ast_moh_stop(struct ast_channel *chan)
889 {
890         ast_clear_flag(chan, AST_FLAG_MOH);
891         ast_deactivate_generator(chan);
892
893         if (chan->music_state) {
894                 if (chan->stream) {
895                         ast_closestream(chan->stream);
896                         chan->stream = NULL;
897                 }
898         }
899 }
900
901 static struct mohclass *moh_class_malloc(void)
902 {
903         struct mohclass *class;
904
905         class = malloc(sizeof(struct mohclass));
906
907         if (!class)
908                 return NULL;
909
910         memset(class, 0, sizeof(struct mohclass));
911
912         class->format = AST_FORMAT_SLINEAR;
913
914         return class;
915 }
916
917 static int load_moh_classes(void)
918 {
919         struct ast_config *cfg;
920         struct ast_variable *var;
921         struct mohclass *class; 
922         char *data;
923         char *args;
924         char *cat;
925         int numclasses = 0;
926         static int dep_warning = 0;
927
928         cfg = ast_config_load("musiconhold.conf");
929
930         if (!cfg)
931                 return 0;
932
933         cat = ast_category_browse(cfg, NULL);
934         for (; cat; cat = ast_category_browse(cfg, cat)) {
935                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
936                         class = moh_class_malloc();
937                         if (!class) {
938                                 ast_log(LOG_WARNING, "Out of memory!\n");
939                                 break;
940                         }                               
941                         ast_copy_string(class->name, cat, sizeof(class->name)); 
942                         var = ast_variable_browse(cfg, cat);
943                         while (var) {
944                                 if (!strcasecmp(var->name, "mode"))
945                                         ast_copy_string(class->mode, var->value, sizeof(class->name)); 
946                                 else if (!strcasecmp(var->name, "directory"))
947                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
948                                 else if (!strcasecmp(var->name, "application"))
949                                         ast_copy_string(class->args, var->value, sizeof(class->args));
950                                 else if (!strcasecmp(var->name, "random"))
951                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
952                                 else if (!strcasecmp(var->name, "format")) {
953                                         class->format = ast_getformatbyname(var->value);
954                                         if (!class->format) {
955                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
956                                                 class->format = AST_FORMAT_SLINEAR;
957                                         }
958                                 }
959                                         var = var->next;
960                         }
961
962                         if (ast_strlen_zero(class->dir)) {
963                                 if (!strcasecmp(class->mode, "custom")) {
964                                         strcpy(class->dir, "nodir");
965                                 } else {
966                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
967                                         free(class);
968                                         continue;
969                                 }
970                         }
971                         if (ast_strlen_zero(class->mode)) {
972                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
973                                 free(class);
974                                 continue;
975                         }
976                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
977                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
978                                 free(class);
979                                 continue;
980                         }
981
982                         moh_register(class);
983                         numclasses++;
984                 }
985         }
986         
987
988         /* Deprecated Old-School Configuration */
989         var = ast_variable_browse(cfg, "classes");
990         while (var) {
991                 if (!dep_warning) {
992                         ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
993                         dep_warning = 1;
994                 }
995                 data = strchr(var->value, ':');
996                 if (data) {
997                         *data++ = '\0';
998                         args = strchr(data, ',');
999                         if (args)
1000                                 *args++ = '\0';
1001                         if (!(get_mohbyname(var->name))) {
1002                                 class = moh_class_malloc();
1003                                 if (!class) {
1004                                         ast_log(LOG_WARNING, "Out of memory!\n");
1005                                         return numclasses;
1006                                 }
1007                                 
1008                                 ast_copy_string(class->name, var->name, sizeof(class->name));
1009                                 ast_copy_string(class->dir, data, sizeof(class->dir));
1010                                 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1011                                 if (args)
1012                                         ast_copy_string(class->args, args, sizeof(class->args));
1013                                 
1014                                 moh_register(class);
1015                                 numclasses++;
1016                         }
1017                 }
1018                 var = var->next;
1019         }
1020         var = ast_variable_browse(cfg, "moh_files");
1021         while (var) {
1022                 if (!dep_warning) {
1023                         ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
1024                         dep_warning = 1;
1025                 }
1026                 if (!(get_mohbyname(var->name))) {
1027                         args = strchr(var->value, ',');
1028                         if (args)
1029                                 *args++ = '\0';
1030                         class = moh_class_malloc();
1031                         if (!class) {
1032                                 ast_log(LOG_WARNING, "Out of memory!\n");
1033                                 return numclasses;
1034                         }
1035                         
1036                         ast_copy_string(class->name, var->name, sizeof(class->name));
1037                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1038                         strcpy(class->mode, "files");
1039                         if (args)       
1040                                 ast_copy_string(class->args, args, sizeof(class->args));
1041                         
1042                         moh_register(class);
1043                         numclasses++;
1044                 }
1045                 var = var->next;
1046         }
1047
1048         ast_config_destroy(cfg);
1049
1050         return numclasses;
1051 }
1052
1053 static void ast_moh_destroy(void)
1054 {
1055         struct mohclass *moh, *tmp;
1056         char buff[8192];
1057         int bytes, tbytes=0, stime = 0, pid = 0;
1058
1059         if (option_verbose > 1)
1060                 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1061         ast_mutex_lock(&moh_lock);
1062         moh = mohclasses;
1063
1064         while (moh) {
1065                 if (moh->pid) {
1066                         ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1067                         stime = time(NULL) + 2;
1068                         pid = moh->pid;
1069                         moh->pid = 0;
1070                         kill(pid, SIGKILL);
1071                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
1072                                 tbytes = tbytes + bytes;
1073                         }
1074                         ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1075                         close(moh->srcfd);
1076                 }
1077                 tmp = moh;
1078                 moh = moh->next;
1079                 ast_moh_free_class(&tmp);
1080         }
1081         mohclasses = NULL;
1082         ast_mutex_unlock(&moh_lock);
1083 }
1084
1085 static void moh_on_off(int on)
1086 {
1087         struct ast_channel *chan = NULL;
1088
1089         while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1090                 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1091                         if (on)
1092                                 local_ast_moh_start(chan, NULL);
1093                         else
1094                                 ast_deactivate_generator(chan);
1095                 }
1096                 ast_mutex_unlock(&chan->lock);
1097         }
1098 }
1099
1100 static int moh_cli(int fd, int argc, char *argv[]) 
1101 {
1102         int x;
1103
1104         moh_on_off(0);
1105         ast_moh_destroy();
1106         x = load_moh_classes();
1107         moh_on_off(1);
1108         ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1109         return 0;
1110 }
1111
1112 static int cli_files_show(int fd, int argc, char *argv[])
1113 {
1114         int i;
1115         struct mohclass *class;
1116
1117         ast_mutex_lock(&moh_lock);
1118         for (class = mohclasses; class; class = class->next) {
1119                 if (!class->total_files)
1120                         continue;
1121
1122                 ast_cli(fd, "Class: %s\n", class->name);
1123                 for (i = 0; i < class->total_files; i++)
1124                         ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1125         }
1126         ast_mutex_unlock(&moh_lock);
1127
1128         return 0;
1129 }
1130
1131 static int moh_classes_show(int fd, int argc, char *argv[])
1132 {
1133         struct mohclass *class;
1134
1135         ast_mutex_lock(&moh_lock);
1136         for (class = mohclasses; class; class = class->next) {
1137                 ast_cli(fd, "Class: %s\n", class->name);
1138                 ast_cli(fd, "\tMode: %s\n", ast_strlen_zero(class->mode) ? "<none>" : class->mode);
1139                 ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "<none>" : class->dir);
1140                 if (ast_test_flag(class, MOH_CUSTOM))
1141                         ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "<none>" : class->args);
1142                 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1143         }
1144         ast_mutex_unlock(&moh_lock);
1145
1146         return 0;
1147 }
1148
1149 static struct ast_cli_entry  cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1150
1151 static struct ast_cli_entry  cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1152
1153 static struct ast_cli_entry  cli_moh_files_show = { { "moh", "files", "show"}, cli_files_show, "List MOH file-based classes", "Lists all loaded file-based MOH classes and their files", NULL};
1154
1155 static int init_classes(void) 
1156 {
1157         struct mohclass *moh;
1158     
1159         if (!load_moh_classes())                /* Load classes from config */
1160                 return 0;                       /* Return if nothing is found */
1161         moh = mohclasses;
1162         while (moh) {
1163                 if (moh->total_files)
1164                         moh_scan_files(moh);
1165                 moh = moh->next;
1166         }
1167         return 1;
1168 }
1169
1170 int load_module(void)
1171 {
1172         int res;
1173
1174         res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1175         ast_register_atexit(ast_moh_destroy);
1176         ast_cli_register(&cli_moh);
1177         ast_cli_register(&cli_moh_files_show);
1178         ast_cli_register(&cli_moh_classes_show);
1179         if (!res)
1180                 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1181         if (!res)
1182                 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1183         if (!res)
1184                 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1185         if (!res)
1186                 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1187
1188         if (!init_classes()) {  /* No music classes configured, so skip it */
1189                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
1190         } else {
1191                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1192         }
1193
1194         return 0;
1195 }
1196
1197 int reload(void)
1198 {
1199         if (init_classes())
1200                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1201
1202         return 0;
1203 }
1204
1205 int unload_module(void)
1206 {
1207         return -1;
1208 }
1209
1210 char *description(void)
1211 {
1212         return "Music On Hold Resource";
1213 }
1214
1215 int usecount(void)
1216 {
1217         /* Never allow Music On Hold to be unloaded
1218            unresolve needed symbols in the dialer */
1219 #if 0
1220         int res;
1221         STANDARD_USECOUNT(res);
1222         return res;
1223 #else
1224         return 1;
1225 #endif
1226 }
1227
1228 char *key()
1229 {
1230         return ASTERISK_GPL_KEY;
1231 }