Skip to content
Snippets Groups Projects
file.c 50.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
    {
    	s->owner = chan;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    int ast_playstream(struct ast_filestream *s)
    {
    
    	if (ast_format_get_type(s->fmt->format) == AST_MEDIA_TYPE_AUDIO)
    
    		res = ast_readaudio_callback(s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    		res = ast_readvideo_callback(s);
    
    	return (res == FSREAD_FAILURE) ? -1 : 0;
    
    int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return fs->fmt->seek(fs, sample_offset, whence);
    }
    
    int ast_truncstream(struct ast_filestream *fs)
    {
    	return fs->fmt->trunc(fs);
    }
    
    
    off_t ast_tellstream(struct ast_filestream *fs)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return fs->fmt->tell(fs);
    }
    
    
    int ast_ratestream(struct ast_filestream *fs)
    {
    	return ast_format_get_sample_rate(fs->fmt->format);
    }
    
    
    int ast_stream_fastforward(struct ast_filestream *fs, off_t ms)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return ast_seekstream(fs, ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
    
    int ast_stream_rewind(struct ast_filestream *fs, off_t ms)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return ast_seekstream(fs, -ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_closestream(struct ast_filestream *f)
    {
    
    	/* This used to destroy the filestream, but it now just decrements a refcount.
    
    	 * We close the stream in order to quit queuing frames now, because we might
    
    	 * change the writeformat, which could result in a subsequent write error, if
    	 * the format is different. */
    
    	if (f == NULL) {
    		return 0;
    	}
    
    /*
     * Look the various language-specific places where a file could exist.
     */
    
    int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	char *buf;
    	int buflen;
    
    	if (preflang == NULL)
    		preflang = "";
    
    	buflen = strlen(preflang) + strlen(filename) + 4;	/* room for everything */
    
    	buf = ast_alloca(buflen);
    
    	return fileexists_core(filename, fmt, preflang, buf, buflen, NULL) ? 1 : 0;
    
    int ast_filedelete(const char *filename, const char *fmt)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return filehelper(filename, NULL, fmt, ACTION_DELETE);
    
    int ast_filerename(const char *filename, const char *filename2, const char *fmt)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return filehelper(filename, filename2, fmt, ACTION_RENAME);
    
    int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return filehelper(filename, filename2, fmt, ACTION_COPY);
    
    static int __ast_file_read_dirs(const char *path, ast_file_on_file on_file,
    
    				void *obj, int max_depth)
    {
    	DIR *dir;
    	struct dirent *entry;
    	int res;
    
    
    		ast_log(LOG_ERROR, "Error opening directory - %s: %s\n",
    
    		return -1;
    	}
    
    	--max_depth;
    
    	res = 0;
    
    	while ((entry = readdir(dir)) != NULL && !errno) {
    
    		int is_file = 0;
    		int is_dir = 0;
    		RAII_VAR(char *, full_path, NULL, ast_free);
    
    
    		if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
    			continue;
    		}
    
    /*
     * If the dirent structure has a d_type use it to determine if we are dealing with
     * a file or directory. Unfortunately if it doesn't have it, or if the type is
     * unknown, or a link then we'll need to use the stat function instead.
     */
    #ifdef _DIRENT_HAVE_D_TYPE
    		if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK) {
    			is_file = entry->d_type == DT_REG;
    			is_dir = entry->d_type == DT_DIR;
    		} else
    #endif
    		{
    			struct stat statbuf;
    
    			/*
    
    			 * Don't use alloca or we risk blowing out the stack if recursing
    			 * into subdirectories.
    
    			full_path = ast_malloc(strlen(path) + strlen(entry->d_name) + 2);
    			if (!full_path) {
    				return -1;
    			}
    			sprintf(full_path, "%s/%s", path, entry->d_name);
    
    				ast_log(LOG_ERROR, "Error reading path stats - %s: %s\n",
    
    				/*
    				 * Output an error, but keep going. It could just be
    				 * a broken link and other files could be fine.
    				 */
    				continue;
    			}
    
    			is_file = S_ISREG(statbuf.st_mode);
    			is_dir = S_ISDIR(statbuf.st_mode);
    		}
    
    		if (is_file) {
    			/* If the handler returns non-zero then stop */
    
    			if ((res = on_file(path, entry->d_name, obj))) {
    
    				break;
    			}
    			/* Otherwise move on to next item in directory */
    			continue;
    		}
    
    		if (!is_dir) {
    
    			ast_debug(5, "Skipping %s: not a regular file or directory\n", full_path);
    
    			continue;
    		}
    
    		/* Only re-curse into sub-directories if not at the max depth */
    		if (max_depth != 0) {
    
    			if (!full_path) {
    				/* Don't use alloca.  See note above. */
    				full_path = ast_malloc(strlen(path) + strlen(entry->d_name) + 2);
    				if (!full_path) {
    					return -1;
    				}
    				sprintf(full_path, "%s/%s", path, entry->d_name);
    
    			if ((res = __ast_file_read_dirs(full_path, on_file, obj, max_depth))) {
    
    				break;
    			}
    		}
    	}
    
    	closedir(dir);
    
    	if (!res && errno) {
    		ast_log(LOG_ERROR, "Error while reading directories - %s: %s\n",
    
    		res = -1;
    	}
    
    	return res;
    }
    
    #if !defined(__GLIBC__)
    /*!
     * \brief Lock to hold when iterating over directories.
     *
     * Currently, 'readdir' is not required to be thread-safe. In most modern implementations
     * it should be safe to make concurrent calls into 'readdir' that specify different directory
     * streams (glibc would be one of these). However, since it is potentially unsafe for some
     * implementations we'll use our own locking in order to achieve synchronization for those.
     */
    AST_MUTEX_DEFINE_STATIC(read_dirs_lock);
    #endif
    
    int ast_file_read_dirs(const char *dir_name, ast_file_on_file on_file, void *obj, int max_depth)
    {
    	int res;
    
    	errno = 0;
    
    #if !defined(__GLIBC__)
    	ast_mutex_lock(&read_dirs_lock);
    #endif
    
    
    	res = __ast_file_read_dirs(dir_name, on_file, obj, max_depth);
    
    
    #if !defined(__GLIBC__)
    	ast_mutex_unlock(&read_dirs_lock);
    #endif
    
    	return res;
    }
    
    
    int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_filestream *fs;
    
    	struct ast_filestream *vfs=NULL;
    
    Jeff Peeler's avatar
    Jeff Peeler committed
    	int seekattempt;
    
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	fs = ast_openstream(chan, filename, preflang);
    
    		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
    
    		ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n",
    			filename, ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf), strerror(errno));
    
    Jeff Peeler's avatar
    Jeff Peeler committed
    
    	/* check to see if there is any data present (not a zero length file),
    	 * done this way because there is no where for ast_openstream_full to
    	 * return the file had no data. */
    
    	pos = ftello(fs->f);
    	seekattempt = fseeko(fs->f, -1, SEEK_END);
    	if (seekattempt) {
    		if (errno == EINVAL) {
    			/* Zero-length file, as opposed to a pipe */
    			return 0;
    		} else {
    			ast_seekstream(fs, 0, SEEK_SET);
    		}
    
    		fseeko(fs->f, pos, SEEK_SET);
    
    	vfs = ast_openvstream(chan, filename, preflang);
    
    		ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_format_get_name(vfs->fmt->format));
    
    	if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MASQ_NOSTREAM))
    
    		fs->orig_chan_name = ast_strdup(ast_channel_name(chan));
    
    	if (ast_applystream(chan, fs))
    		return -1;
    	if (vfs && ast_applystream(chan, vfs))
    		return -1;
    
    	ast_test_suite_event_notify("PLAYBACK", "Message: %s\r\nChannel: %s", filename, ast_channel_name(chan));
    
    	res = ast_playstream(fs);
    	if (!res && vfs)
    		res = ast_playstream(vfs);
    
    
    	if (VERBOSITY_ATLEAST(3)) {
    		ast_channel_lock(chan);
    		ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", ast_channel_name(chan), filename, ast_format_get_name(ast_channel_writeformat(chan)), preflang ? preflang : "default");
    		ast_channel_unlock(chan);
    	}
    
    struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
    
    	struct ast_filestream *fs = NULL;
    
    	int format_found = 0;
    
    	AST_RWLIST_RDLOCK(&formats);
    
    	AST_RWLIST_TRAVERSE(&formats, f, list) {
    
    		if (!exts_compare(f->exts, type))
    			continue;
    
    		bfile = fopen(fn, "r");
    
    
    		if (!bfile || (fs = get_filestream(f, bfile)) == NULL || open_wrapper(fs) ) {
    
    			ast_log(LOG_WARNING, "Unable to open %s\n", fn);
    
    			if (fs) {
    				ast_closestream(fs);
    			}
    
    		}
    		/* found it */
    		fs->trans = NULL;
    		fs->fmt = f;
    		fs->flags = flags;
    		fs->mode = mode;
    
    		fs->filename = ast_strdup(filename);
    
    	AST_RWLIST_UNLOCK(&formats);
    
    		ast_log(LOG_WARNING, "No such format '%s'\n", type);
    
    struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* compiler claims this variable can be used before initialization... */
    	FILE *bfile = NULL;
    
    	struct ast_filestream *fs = NULL;
    	char *buf = NULL;
    
    	int format_found = 0;
    
    	AST_RWLIST_RDLOCK(&formats);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* set the O_TRUNC flag if and only if there is no O_APPEND specified */
    
    	/* We really can't use O_APPEND as it will break WAV header updates */
    
    	if (flags & O_APPEND) {
    
    		flags &= ~O_APPEND;
    	} else {
    
    	/* XXX need to fix this - we should just do the fopen,
    	 * not open followed by fdopen()
    	 */
    
    	AST_RWLIST_TRAVERSE(&formats, f, list) {
    
    		char *fn, *orig_fn = NULL;
    
    		if (!exts_compare(f->exts, type))
    			continue;
    
    		fd = open(fn, flags | myflags, mode);
    
    		if (fd > -1) {
    			/* fdopen() the resulting file stream */
    			bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
    			if (!bfile) {
    				ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
    				close(fd);
    				fd = -1;
    			}
    		}
    
    		if (ast_opt_cache_record_files && (fd > -1)) {
    
    			fclose(bfile);	/* this also closes fd */
    
    			/*
    			  We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
    			  What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
    			*/
    			orig_fn = ast_strdupa(fn);
    			for (c = fn; *c; c++)
    				if (*c == '/')
    					*c = '_';
    
    			size = strlen(fn) + strlen(record_cache_dir) + 2;
    
    			buf = ast_alloca(size);
    
    			strcpy(buf, record_cache_dir);
    			strcat(buf, "/");
    			strcat(buf, fn);
    
    			if (fd > -1) {
    				/* fdopen() the resulting file stream */
    				bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
    				if (!bfile) {
    					ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
    					close(fd);
    					fd = -1;
    				}
    			}
    
    			fs = get_filestream(f, bfile);
    
    			if (fs) {
    				if ((fs->write_buffer = ast_malloc(32768))) {
    					setvbuf(fs->f, fs->write_buffer, _IOFBF, 32768);
    				}
    			}
    
    			if (!fs || rewrite_wrapper(fs, comment)) {
    
    				ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
    				close(fd);
    				if (orig_fn) {
    					unlink(fn);
    
    				if (fs) {
    					ast_closestream(fs);
    					fs = NULL;
    				}
    
    				if (!buf) {
    					ast_free(fn);
    				}
    
    			}
    			fs->trans = NULL;
    			fs->fmt = f;
    			fs->flags = flags;
    			fs->mode = mode;
    			if (orig_fn) {
    
    				fs->realfilename = ast_strdup(orig_fn);
    				fs->filename = ast_strdup(fn);
    
    			} else {
    				fs->realfilename = NULL;
    
    				fs->filename = ast_strdup(filename);
    
    			fs->vfs = NULL;
    			/* If truncated, we'll be at the beginning; if not truncated, then append */
    			f->seek(fs, 0, SEEK_END);
    
    		} else if (errno != EEXIST) {
    			ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
    			if (orig_fn)
    				unlink(orig_fn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		/* if buf != NULL then fn is already free and pointing to it */
    		if (!buf)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	AST_RWLIST_UNLOCK(&formats);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "No such format '%s'\n", type);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return fs;
    }
    
    
    static void waitstream_control(struct ast_channel *c,
    		enum ast_waitstream_fr_cb_values type,
    		ast_waitstream_fr_cb cb,
    		int skip_ms)
    {
    	switch (type)
    	{
    	case AST_WAITSTREAM_CB_FASTFORWARD:
    		{
    			int eoftest;
    			ast_stream_fastforward(ast_channel_stream(c), skip_ms);
    			eoftest = fgetc(ast_channel_stream(c)->f);
    			if (feof(ast_channel_stream(c)->f)) {
    				ast_stream_rewind(ast_channel_stream(c), skip_ms);
    			} else {
    				ungetc(eoftest, ast_channel_stream(c)->f);
    			}
    		}
    		break;
    	case AST_WAITSTREAM_CB_REWIND:
    		ast_stream_rewind(ast_channel_stream(c), skip_ms);
    		break;
    	default:
    		break;
    	}
    
    	if (cb) {
    
    		long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_get_sample_rate(ast_channel_stream(c)->fmt->format) / 1000);
    
    		cb(c, ms_len, type);
    	}
    
    	ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
    		"Control: %s\r\n"
    		"SkipMs: %d\r\n",
    		ast_channel_name(c),
    		(type == AST_WAITSTREAM_CB_FASTFORWARD) ? "FastForward" : "Rewind",
    		skip_ms);
    }
    
    
    /*!
     * \brief the core of all waitstream() functions
     */
    
    static int waitstream_core(struct ast_channel *c,
    	const char *breakon,
    	const char *forward,
    	const char *reverse,
    	int skip_ms,
    	int audiofd,
    	int cmdfd,
    	const char *context,
    	ast_waitstream_fr_cb cb)
    
    	const char *orig_chan_name = NULL;
    
    
    	/* Switch the channel to end DTMF frame only. waitstream_core doesn't care about the start of DTMF. */
    
    	ast_channel_set_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    	if (ast_test_flag(ast_channel_flags(c), AST_FLAG_MASQ_NOSTREAM))
    
    		orig_chan_name = ast_strdupa(ast_channel_name(c));
    
    	if (ast_channel_stream(c) && cb) {
    
    		long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_get_sample_rate(ast_channel_stream(c)->fmt->format) / 1000);
    
    		cb(c, ms_len, AST_WAITSTREAM_CB_START);
    	}
    
    
    	while (ast_channel_stream(c)) {
    
    		if (orig_chan_name && strcasecmp(orig_chan_name, ast_channel_name(c))) {
    
    			ast_stopstream(c);
    			err = 1;
    			break;
    		}
    
    
    		ms = ast_sched_wait(ast_channel_sched(c));
    
    		if (ms < 0 && !ast_channel_timingfunc(c)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ms < 0)
    			ms = 1000;
    
    Joshua Colp's avatar
    Joshua Colp committed
    		if (cmdfd < 0) {
    
    			res = ast_waitfor(c, ms);
    			if (res < 0) {
    				ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
    
    				ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    				return res;
    			}
    		} else {
    			int outfd;
    			struct ast_channel *rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
    			if (!rchan && (outfd < 0) && (ms)) {
    				/* Continue */
    				if (errno == EINTR)
    					continue;
    				ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
    
    				ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    				return -1;
    
    			} else if (outfd > -1) { /* this requires cmdfd set */
    				/* The FD we were watching has something waiting */
    
    				ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    			/* if rchan is set, it is 'c' */
    			res = rchan ? 1 : 0; /* map into 'res' values */
    		}
    		if (res > 0) {
    			struct ast_frame *fr = ast_read(c);
    
    				ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    					const char exten[2] = { fr->subclass.integer, '\0' };
    
    					if (ast_exists_extension(c, context, exten, 1,
    
    						S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
    
    						res = fr->subclass.integer;
    
    						ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    					res = fr->subclass.integer;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    					if (strchr(forward, res)) {
    
    						waitstream_control(c, AST_WAITSTREAM_CB_FASTFORWARD, cb, skip_ms);
    
    						waitstream_control(c, AST_WAITSTREAM_CB_REWIND, cb, skip_ms);
    
    					} else if (strchr(breakon, res)) {
    
    						ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
    							"Control: %s\r\n",
    							ast_channel_name(c),
    							"Break");
    
    
    						ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    				}
    				break;
    			case AST_FRAME_CONTROL:
    
    				switch (fr->subclass.integer) {
    
    				case AST_CONTROL_STREAM_STOP:
    				case AST_CONTROL_STREAM_SUSPEND:
    				case AST_CONTROL_STREAM_RESTART:
    					/* Fall-through and break out */
    					ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
    						"Control: %s\r\n",
    						ast_channel_name(c),
    						"Break");
    					res = fr->subclass.integer;
    					ast_frfree(fr);
    
    					ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    					return res;
    				case AST_CONTROL_STREAM_REVERSE:
    					if (!skip_ms) {
    						skip_ms = 3000;
    					}
    					waitstream_control(c, AST_WAITSTREAM_CB_REWIND, cb, skip_ms);
    					break;
    				case AST_CONTROL_STREAM_FORWARD:
    					if (!skip_ms) {
    						skip_ms = 3000;
    					}
    					waitstream_control(c, AST_WAITSTREAM_CB_FASTFORWARD, cb, skip_ms);
    					break;
    
    				case AST_CONTROL_HANGUP:
    
    				case AST_CONTROL_BUSY:
    
    				case AST_CONTROL_CONGESTION:
    
    					ast_frfree(fr);
    
    					ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    					return -1;
    				case AST_CONTROL_RINGING:
    				case AST_CONTROL_ANSWER:
    
    				case AST_CONTROL_VIDUPDATE:
    
    				case AST_CONTROL_SRCUPDATE:
    
    				case AST_CONTROL_SRCCHANGE:
    
    				case AST_CONTROL_HOLD:
    				case AST_CONTROL_UNHOLD:
    
    				case AST_CONTROL_CONNECTED_LINE:
    				case AST_CONTROL_REDIRECTING:
    				case AST_CONTROL_AOC:
    
    				case AST_CONTROL_UPDATE_RTP_PEER:
    
    				case AST_CONTROL_PVT_CAUSE_CODE:
    
    					/* Unimportant */
    					break;
    				default:
    
    					ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass.integer);
    
    			case AST_FRAME_VOICE:
    				/* Write audio if appropriate */
    
    				if (audiofd > -1) {
    					if (write(audiofd, fr->data.ptr, fr->datalen) < 0) {
    						ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
    					}
    				}
    
    			}
    			ast_frfree(fr);
    
    		ast_sched_runq(ast_channel_sched(c));
    
    	ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
    
    	return (err || ast_channel_softhangup_internal_flag(c)) ? -1 : 0;
    
    int ast_waitstream_fr_w_cb(struct ast_channel *c,
    	const char *breakon,
    	const char *forward,
    	const char *reverse,
    	int ms,
    	ast_waitstream_fr_cb cb)
    {
    	return waitstream_core(c, breakon, forward, reverse, ms,
    		-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */, cb);
    }
    
    
    int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *reverse, int ms)
    
    	return waitstream_core(c, breakon, forward, reverse, ms,
    
    		-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */, NULL /* no callback */);
    
    /*! \internal
     * \brief Clean up the return value of a waitstream call
     *
     * It's possible for a control frame to come in from an external source and break the
     * playback. From a consumer of most ast_waitstream_* function callers, this should
     * appear like normal playback termination, i.e., return 0 and not the value of the
     * control frame.
     */
    static int sanitize_waitstream_return(int return_value)
    {
    	switch (return_value) {
    	case AST_CONTROL_STREAM_STOP:
    	case AST_CONTROL_STREAM_SUSPEND:
    	case AST_CONTROL_STREAM_RESTART:
    		/* Fall through and set return_value to 0 */
    		return_value = 0;
    		break;
    	default:
    		/* Do nothing */
    		break;
    	}
    
    	return return_value;
    }
    
    
    int ast_waitstream(struct ast_channel *c, const char *breakon)
    {
    
    	int res;
    
    	res = waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL, NULL /* no callback */);
    
    	return sanitize_waitstream_return(res);
    
    }
    
    int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
    {
    
    	int res;
    
    	res = waitstream_core(c, breakon, NULL, NULL, 0,
    
    		audiofd, cmdfd, NULL /* no context */, NULL /* no callback */);
    
    
    	return sanitize_waitstream_return(res);
    
    int ast_waitstream_exten(struct ast_channel *c, const char *context)
    {
    
    	/* Waitstream, with return in the case of a valid 1 digit extension */
    	/* in the current or specified context being pressed */
    
    		context = ast_channel_context(c);
    
    	res = waitstream_core(c, NULL, NULL, NULL, 0,
    
    		-1, -1, context, NULL /* no callback */);
    
    
    	return sanitize_waitstream_return(res);
    
    /*
     * if the file name is non-empty, try to play it.
     * Return 0 if success, -1 if error, digit if interrupted by a digit.
     * If digits == "" then we can simply check for non-zero.
     */
    
    int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	int res = 0;
    	if (!ast_strlen_zero(file)) {
    
    		res = ast_streamfile(chan, file, ast_channel_language(chan));
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		if (!res) {
    			res = ast_waitstream(chan, digits);
    		}
    	}
    	return res;
    
    char *ast_format_str_reduce(char *fmts)
    {
    
    	struct ast_format_def *f;
    	struct ast_format_def *fmts_ptr[AST_MAX_FORMATS];
    
    	char *fmts_str[AST_MAX_FORMATS];
    	char *stringp, *type;
    	char *orig = fmts;
    
    	int i, j, x, first, found = 0;
    
    	int len = strlen(fmts) + 1;
    
    
    	if (AST_RWLIST_RDLOCK(&formats)) {
    		ast_log(LOG_WARNING, "Unable to lock format list\n");
    		return NULL;
    	}
    
    	stringp = ast_strdupa(fmts);
    
    	for (x = 0; (type = strsep(&stringp, "|")) && x < AST_MAX_FORMATS; x++) {
    		AST_RWLIST_TRAVERSE(&formats, f, list) {
    			if (exts_compare(f->exts, type)) {
    				found = 1;
    				break;
    			}
    		}
    
    		fmts_str[x] = type;
    		if (found) {
    			fmts_ptr[x] = f;
    		} else {
    			fmts_ptr[x] = NULL;
    		}
    	}
    	AST_RWLIST_UNLOCK(&formats);
    
    
    	for (i = 0; i < x; i++) {
    
    		/* ignore invalid entries */
    		if (!fmts_ptr[i]) {
    			ast_log(LOG_WARNING, "ignoring unknown format '%s'\n", fmts_str[i]);
    			continue;
    		}
    
    
    		/* special handling for the first entry */
    
    			res = snprintf(fmts, len, "%s", fmts_str[i]);
    			fmts += res;
    			len -= res;
    
    			continue;
    		}
    
    		found = 0;
    		for (j = 0; j < i; j++) {
    			/* this is a duplicate */
    			if (fmts_ptr[j] == fmts_ptr[i]) {
    				found = 1;
    				break;
    			}
    		}
    
    		if (!found) {
    
    			res = snprintf(fmts, len, "|%s", fmts_str[i]);
    			fmts += res;
    			len -= res;
    
    	if (first) {
    		ast_log(LOG_WARNING, "no known formats found in format list (%s)\n", orig);
    		return NULL;
    	}
    
    
    static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    {
    #define FORMAT "%-10s %-10s %-20s\n"
    #define FORMAT2 "%-10s %-10s %-20s\n"
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core show file formats";
    		e->usage =
    			"Usage: core show file formats\n"
    			"       Displays currently registered file formats (if any).\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != 4)
    		return CLI_SHOWUSAGE;
    
    	ast_cli(a->fd, FORMAT, "Format", "Name", "Extensions");
    	ast_cli(a->fd, FORMAT, "------", "----", "----------");
    
    	AST_RWLIST_RDLOCK(&formats);
    	AST_RWLIST_TRAVERSE(&formats, f, list) {
    
    		ast_cli(a->fd, FORMAT2, ast_format_get_name(f->format), f->name, f->exts);
    
    	AST_RWLIST_UNLOCK(&formats);
    
    	ast_cli(a->fd, "%d file formats registered.\n", count_fmt);
    	return CLI_SUCCESS;
    
    struct ast_format *ast_get_format_for_file_ext(const char *file_ext)
    
    {
    	struct ast_format_def *f;
    	SCOPED_RDLOCK(lock, &formats.lock);
    	AST_RWLIST_TRAVERSE(&formats, f, list) {
    		if (exts_compare(f->exts, file_ext)) {
    
    static struct ast_cli_entry cli_file[] = {
    
    	AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats")
    
    static void file_shutdown(void)
    {
    	ast_cli_unregister_multiple(cli_file, ARRAY_LEN(cli_file));
    
    	STASIS_MESSAGE_TYPE_CLEANUP(ast_format_register_type);
    	STASIS_MESSAGE_TYPE_CLEANUP(ast_format_unregister_type);
    
    	STASIS_MESSAGE_TYPE_INIT(ast_format_register_type);
    	STASIS_MESSAGE_TYPE_INIT(ast_format_unregister_type);
    
    	ast_cli_register_multiple(cli_file, ARRAY_LEN(cli_file));
    
    	ast_register_cleanup(file_shutdown);