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