support new format for musiconhold.conf (issue #4908)
[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") || !strcasecmp(moh->mode, "quietmp3nb"))
803                         ast_set_flag(moh, MOH_SINGLE);
804                 else if (!strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb"))
805                         ast_set_flag(moh, MOH_QUIET);
806                 
807                 moh->srcfd = -1;
808 #ifdef ZAPATA_MOH
809                 /* Open /dev/zap/pseudo for timing...  Is
810                    there a better, yet reliable way to do this? */
811                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
812                 if (moh->pseudofd < 0) {
813                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
814                 } else {
815                         x = 320;
816                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
817                 }
818 #else
819                 moh->pseudofd = -1;
820 #endif
821                 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
822                         ast_log(LOG_WARNING, "Unable to create moh...\n");
823                         if (moh->pseudofd > -1)
824                                 close(moh->pseudofd);
825                         ast_moh_free_class(&moh);
826                         return -1;
827                 }
828         } else {
829                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
830                 ast_moh_free_class(&moh);
831                 return -1;
832         }
833         ast_mutex_lock(&moh_lock);
834         moh->next = mohclasses;
835         mohclasses = moh;
836         ast_mutex_unlock(&moh_lock);
837         return 0;
838 }
839
840 static void local_ast_moh_cleanup(struct ast_channel *chan)
841 {
842         if (chan->music_state) {
843                 free(chan->music_state);
844                 chan->music_state = NULL;
845         }
846 }
847
848 static int local_ast_moh_start(struct ast_channel *chan, char *class)
849 {
850         struct mohclass *mohclass;
851
852         if (!class || ast_strlen_zero(class))
853                 class = chan->musicclass;
854         if (!class || ast_strlen_zero(class))
855                 class = "default";
856         ast_mutex_lock(&moh_lock);
857         mohclass = get_mohbyname(class);
858         ast_mutex_unlock(&moh_lock);
859
860         if (!mohclass) {
861                 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
862                 return -1;
863         }
864
865         ast_set_flag(chan, AST_FLAG_MOH);
866         if (mohclass->total_files) {
867                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
868         } else
869                 return ast_activate_generator(chan, &mohgen, mohclass);
870 }
871
872 static void local_ast_moh_stop(struct ast_channel *chan)
873 {
874         ast_clear_flag(chan, AST_FLAG_MOH);
875         ast_deactivate_generator(chan);
876
877         if (chan->music_state) {
878                 if (chan->stream) {
879                         ast_closestream(chan->stream);
880                         chan->stream = NULL;
881                 }
882         }
883 }
884
885 static struct mohclass *moh_class_malloc(void)
886 {
887         struct mohclass *class;
888
889         class = malloc(sizeof(struct mohclass));
890
891         if (!class)
892                 return NULL;
893
894         memset(class, 0, sizeof(struct mohclass));
895
896         class->format = AST_FORMAT_SLINEAR;
897
898         return class;
899 }
900
901 static int load_moh_classes(void)
902 {
903         struct ast_config *cfg;
904         struct ast_variable *var;
905         struct mohclass *class; 
906         char *data;
907         char *args;
908         char *cat;
909         int numclasses = 0;
910         static int dep_warning = 0;
911
912         cfg = ast_config_load("musiconhold.conf");
913
914         if (!cfg)
915                 return 0;
916
917         cat = ast_category_browse(cfg, NULL);
918         for (; cat; cat = ast_category_browse(cfg, cat)) {
919                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
920                         class = moh_class_malloc();
921                         if (!class) {
922                                 ast_log(LOG_WARNING, "Out of memory!\n");
923                                 break;
924                         }                               
925                         ast_copy_string(class->name, cat, sizeof(class->name)); 
926                         var = ast_variable_browse(cfg, cat);
927                         while (var) {
928                                 if (!strcasecmp(var->name, "mode"))
929                                         ast_copy_string(class->mode, var->value, sizeof(class->name)); 
930                                 else if (!strcasecmp(var->name, "directory"))
931                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
932                                 else if (!strcasecmp(var->name, "application"))
933                                         ast_copy_string(class->args, var->value, sizeof(class->args));
934                                 else if (!strcasecmp(var->name, "random"))
935                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
936                                 else if (!strcasecmp(var->name, "format")) {
937                                         class->format = ast_getformatbyname(var->value);
938                                         if (!class->format) {
939                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
940                                                 class->format = AST_FORMAT_SLINEAR;
941                                         }
942                                 }
943                                         var = var->next;
944                         }
945
946                         if (ast_strlen_zero(class->dir)) {
947                                 if (!strcasecmp(class->mode, "custom")) {
948                                         strcpy(class->dir, "nodir");
949                                 } else {
950                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
951                                         free(class);
952                                         continue;
953                                 }
954                         }
955                         if (ast_strlen_zero(class->mode)) {
956                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
957                                 free(class);
958                                 continue;
959                         }
960                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
961                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
962                                 free(class);
963                                 continue;
964                         }
965
966                         moh_register(class);
967                         numclasses++;
968                 }
969         }
970         
971
972         /* Deprecated Old-School Configuration */
973         var = ast_variable_browse(cfg, "classes");
974         while (var) {
975                 if (!dep_warning) {
976                         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");
977                         dep_warning = 1;
978                 }
979                 data = strchr(var->value, ':');
980                 if (data) {
981                         *data++ = '\0';
982                         args = strchr(data, ',');
983                         if (args)
984                                 *args++ = '\0';
985                         if (!(get_mohbyname(var->name))) {
986                                 class = moh_class_malloc();
987                                 if (!class) {
988                                         ast_log(LOG_WARNING, "Out of memory!\n");
989                                         return numclasses;
990                                 }
991                                 
992                                 ast_copy_string(class->name, var->name, sizeof(class->name));
993                                 ast_copy_string(class->dir, data, sizeof(class->dir));
994                                 ast_copy_string(class->mode, var->value, sizeof(class->mode));
995                                 if (args)
996                                         ast_copy_string(class->args, args, sizeof(class->args));
997                                 
998                                 moh_register(class);
999                                 numclasses++;
1000                         }
1001                 }
1002                 var = var->next;
1003         }
1004         var = ast_variable_browse(cfg, "moh_files");
1005         while (var) {
1006                 if (!dep_warning) {
1007                         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");
1008                         dep_warning = 1;
1009                 }
1010                 if (!(get_mohbyname(var->name))) {
1011                         args = strchr(var->value, ',');
1012                         if (args)
1013                                 *args++ = '\0';
1014                         class = moh_class_malloc();
1015                         if (!class) {
1016                                 ast_log(LOG_WARNING, "Out of memory!\n");
1017                                 return numclasses;
1018                         }
1019                         
1020                         ast_copy_string(class->name, var->name, sizeof(class->name));
1021                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1022                         strcpy(class->mode, "files");
1023                         if (args)       
1024                                 ast_copy_string(class->args, args, sizeof(class->args));
1025                         
1026                         moh_register(class);
1027                         numclasses++;
1028                 }
1029                 var = var->next;
1030         }
1031
1032         ast_config_destroy(cfg);
1033
1034         return numclasses;
1035 }
1036
1037 static void ast_moh_destroy(void)
1038 {
1039         struct mohclass *moh, *tmp;
1040         char buff[8192];
1041         int bytes, tbytes=0, stime = 0, pid = 0;
1042
1043         if (option_verbose > 1)
1044                 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1045         ast_mutex_lock(&moh_lock);
1046         moh = mohclasses;
1047
1048         while (moh) {
1049                 if (moh->pid) {
1050                         ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1051                         stime = time(NULL) + 2;
1052                         pid = moh->pid;
1053                         moh->pid = 0;
1054                         kill(pid, SIGKILL);
1055                         while ((ast_wait_for_input(moh->srcfd, 100) > -1) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
1056                                 tbytes = tbytes + bytes;
1057                         }
1058                         ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1059                         close(moh->srcfd);
1060                 }
1061                 tmp = moh;
1062                 moh = moh->next;
1063                 ast_moh_free_class(&tmp);
1064         }
1065         mohclasses = NULL;
1066         ast_mutex_unlock(&moh_lock);
1067 }
1068
1069 static void moh_on_off(int on)
1070 {
1071         struct ast_channel *chan = NULL;
1072
1073         while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1074                 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1075                         if (on)
1076                                 local_ast_moh_start(chan, NULL);
1077                         else
1078                                 ast_deactivate_generator(chan);
1079                 }
1080                 ast_mutex_unlock(&chan->lock);
1081         }
1082 }
1083
1084 static int moh_cli(int fd, int argc, char *argv[]) 
1085 {
1086         int x;
1087
1088         moh_on_off(0);
1089         ast_moh_destroy();
1090         x = load_moh_classes();
1091         moh_on_off(1);
1092         ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1093         return 0;
1094 }
1095
1096 static int cli_files_show(int fd, int argc, char *argv[])
1097 {
1098         int i;
1099         struct mohclass *class;
1100
1101         ast_mutex_lock(&moh_lock);
1102         for (class = mohclasses; class; class = class->next) {
1103                 if (!class->total_files)
1104                         continue;
1105
1106                 ast_cli(fd, "Class: %s\n", class->name);
1107                 for (i = 0; i < class->total_files; i++)
1108                         ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1109         }
1110         ast_mutex_unlock(&moh_lock);
1111
1112         return 0;
1113 }
1114
1115 static int moh_classes_show(int fd, int argc, char *argv[])
1116 {
1117         struct mohclass *class;
1118
1119         ast_mutex_lock(&moh_lock);
1120         for (class = mohclasses; class; class = class->next) {
1121                 ast_cli(fd, "Class: %s\n", class->name);
1122                 ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "<none>" : class->dir);
1123                 if (ast_test_flag(class, MOH_CUSTOM))
1124                         ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "<none>" : class->args);
1125                 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1126         }
1127         ast_mutex_unlock(&moh_lock);
1128
1129         return 0;
1130 }
1131
1132 static struct ast_cli_entry  cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1133
1134 static struct ast_cli_entry  cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1135
1136 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};
1137
1138 static void init_classes(void) 
1139 {
1140         struct mohclass *moh;
1141     
1142         load_moh_classes();
1143         moh = mohclasses;
1144         while (moh) {
1145                 if (moh->total_files)
1146                         moh_scan_files(moh);
1147                 moh = moh->next;
1148         }
1149 }
1150
1151 int load_module(void)
1152 {
1153         int res;
1154
1155         ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1156         res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1157         ast_register_atexit(ast_moh_destroy);
1158         ast_cli_register(&cli_moh);
1159         ast_cli_register(&cli_moh_files_show);
1160         ast_cli_register(&cli_moh_classes_show);
1161         if (!res)
1162                 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1163         if (!res)
1164                 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1165         if (!res)
1166                 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1167         if (!res)
1168                 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1169
1170         init_classes();
1171
1172         return 0;
1173 }
1174
1175 int reload(void)
1176 {
1177         init_classes();
1178
1179         return 0;
1180 }
1181
1182 int unload_module(void)
1183 {
1184         return -1;
1185 }
1186
1187 char *description(void)
1188 {
1189         return "Music On Hold Resource";
1190 }
1191
1192 int usecount(void)
1193 {
1194         /* Never allow Music On Hold to be unloaded
1195            unresolve needed symbols in the dialer */
1196 #if 0
1197         int res;
1198         STANDARD_USECOUNT(res);
1199         return res;
1200 #else
1201         return 1;
1202 #endif
1203 }
1204
1205 char *key()
1206 {
1207         return ASTERISK_GPL_KEY;
1208 }