diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index dd8dba5573..3749d7b98c 100644 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -1208,84 +1208,95 @@ static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclas } } -static int moh_scan_files(struct mohclass *class) { - - DIR *files_DIR; - struct dirent *files_dirent; - char dir_path[PATH_MAX - sizeof(class->dir)]; - char filepath[PATH_MAX]; - char *ext; - struct stat statbuf; - int res; - struct ast_vector_string *files; +static int on_moh_file(const char *directory, const char *filename, void *obj) +{ + struct ast_vector_string *files = obj; + char *full_path; + char *extension; - if (class->dir[0] != '/') { - snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir); - } else { - ast_copy_string(dir_path, class->dir, sizeof(dir_path)); - } - ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name); - files_DIR = opendir(dir_path); - if (!files_DIR) { - ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path); - return -1; + /* Skip files that starts with a dot */ + if (*filename == '.') { + ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n", + directory, filename); + return 0; } - files = moh_file_vector_alloc(16); /* 16 seems like a reasonable default */ - if (!files) { - closedir(files_DIR); - return -1; + /* We can't do anything with files that don't have an extension, + * so check that first and punt if we can't find something */ + extension = strrchr(filename, '.'); + if (!extension) { + ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n", + directory, filename); + return 0; } - while ((files_dirent = readdir(files_DIR))) { - char *filepath_copy; + /* The extension needs at least two characters (after the .) to be useful */ + if (strlen(extension) < 3) { + ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two " + "character extension\n", directory, filename); + return 0; + } - /* The file name must be at least long enough to have the file type extension */ - if ((strlen(files_dirent->d_name) < 4)) - continue; + /* Build the full path (excluding the extension) */ + if (ast_asprintf(&full_path, "%s/%.*s", + directory, + (int) (extension - filename), filename) < 0) { + /* If we don't have enough memory to build this path, there is no + * point in continuing */ + return 1; + } - /* Skip files that starts with a dot */ - if (files_dirent->d_name[0] == '.') - continue; + /* If the file is present in multiple formats, ensure we only put it + * into the list once. Pretty sure this is O(n^2). */ + if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) { + ast_free(full_path); + return 0; + } - /* Skip files without extensions... they are not audio */ - if (!strchr(files_dirent->d_name, '.')) - continue; + if (AST_VECTOR_APPEND(files, full_path)) { + /* AST_VECTOR_APPEND() can only fail on allocation failure, so + * we stop iterating */ + ast_free(full_path); + return 1; + } - snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name); + return 0; +} - if (stat(filepath, &statbuf)) - continue; +static int moh_filename_strcasecmp(const void *a, const void *b) +{ + const char **s1 = (const char **) a; + const char **s2 = (const char **) b; + return strcasecmp(*s1, *s2); +} - if (!S_ISREG(statbuf.st_mode)) - continue; +static int moh_scan_files(struct mohclass *class) { - if ((ext = strrchr(filepath, '.'))) - *ext = '\0'; + char dir_path[PATH_MAX - sizeof(class->dir)]; + struct ast_vector_string *files; - /* if the file is present in multiple formats, ensure we only put it into the list once */ - if (AST_VECTOR_GET_CMP(files, &filepath[0], !strcmp)) { - continue; - } + if (class->dir[0] != '/') { + snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir); + } else { + ast_copy_string(dir_path, class->dir, sizeof(dir_path)); + } - filepath_copy = ast_strdup(filepath); - if (!filepath_copy) { - break; - } + ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name); - if (ast_test_flag(class, MOH_SORTALPHA)) { - res = AST_VECTOR_ADD_SORTED(files, filepath_copy, strcasecmp); - } else { - res = AST_VECTOR_APPEND(files, filepath_copy); - } + /* 16 seems like a reasonable default */ + files = moh_file_vector_alloc(16); + if (!files) { + return -1; + } - if (res) { - ast_free(filepath_copy); - break; - } + if (ast_file_read_dir(dir_path, on_moh_file, files)) { + ao2_ref(files, -1); + return -1; } - closedir(files_DIR); + if (ast_test_flag(class, MOH_SORTALPHA)) { + AST_VECTOR_SORT(files, moh_filename_strcasecmp); + } AST_VECTOR_COMPACT(files);