Browse files

Merge branch 'master' into processor-pool

  • Loading branch information...
2 parents 7472d51 + ec3ae89 commit fa02b45aaa982904507743f89ad39385eb390bb0 @hzeller committed Oct 5, 2012
Showing with 246 additions and 155 deletions.
  1. +17 −14 README.md
  2. +4 −4 file-handler.h
  3. +120 −48 folve-filesystem.cc
  4. +31 −11 folve-filesystem.h
  5. +11 −17 folve-main.cc
  6. +63 −57 status-server.cc
  7. +0 −4 status-server.h
View
31 README.md
@@ -82,16 +82,16 @@ For hints on how to compile on older systems see INSTALL.md.
Folve requires at least two parameters: the directory where your original
FLAC files reside and the mount point of this filesystem.
-Also, do be useful, you need to supply at least one configuration directory
-with the `-c <config-dir>` option. Very useful is the `-p <port>` that starts
-a HTTP status server. Let's use some example filters from this distribution;
+Also, do be useful, you need to supply the directory that contains filter
+directories with the `-C <config-dir>` option.
+Very useful is the `-p <port>` that starts a HTTP status server. Let's use
+some example filters from this distribution;
if you are in the Folve source directory, you find the directory `demo-filters/`
-that contains subdirectories with filters.
-Let's choose some filters to play with:
+that contains subdirectories with filters. If we pass this directory to folve,
+folve will search in this directory for named filters:
mkdir /tmp/test-mount
- ./folve -c demo-filters/SantaLucia -c demo-filters/lowpass \
- -c demo-filters/highpass -p 17322 -f \
+ ./folve -C demo-filters -p 17322 -f \
/path/to/your/directory/with/flacs /tmp/test-mount
Now you can access the fileystem under that mount point; it has the same
@@ -115,6 +115,9 @@ directory with the `fusermount` command:
fusermount -u /tmp/test-mount
### Filter Configuration ###
+With the `-C` option, you give folve a directory in which it looks for
+subdirectories with named filter configurations.
+
Filters are WAV files containing an impulse response (IR). This is
used by jconvolver's convolution engine to create a
[Finite Impulse Response](http://en.wikipedia.org/wiki/Finite_impulse_response)
@@ -157,8 +160,8 @@ please let me know.)
usage: folve [options] <original-dir> <mount-point-dir>
Options: (in sequence of usefulness)
- -c <cfg-dir> : Convolver configuration directory.
- You can supply this option multiple times:
+ -C <cfg-dir> : Convolver base configuration directory.
+ Sub-directories name the different filters.
Select on the HTTP status page.
-p <port> : Port to run the HTTP status server on.
-r <refresh> : Seconds between refresh of status page;
@@ -179,10 +182,10 @@ directory -- and the result is split between these two files.
To manually switch the configuration from the command line, you can use `wget`
or `curl`, whatever you prefer:
- wget -q -O/dev/null http://localhost:17322/settings?f=2
- curl http://localhost:17322/settings?f=2
+ wget -q -O/dev/null http://localhost:17322/settings?f=highpass
+ curl http://localhost:17322/settings?f=SantaLucia
-The parameter given to `f=` is the configuration in the same sequence you
-supplied on startup, starting to count from 1. Configuration 0 means
-'no filter' (And no, there is no security built-in. If you want people from
+The parameter given to `f=` is the name of the subdirectory in your base
+configuration directory. An empty string is no filter, i.e. 'pass through'.
+(And no, there is no security built-in. If you want people from
messing with the configuration of your Folve-daemon, do not use `-p <port>` :)).
View
8 file-handler.h
@@ -42,7 +42,7 @@ struct HandlerStats {
float max_output_value; // Clipping ? Absolute value, should be [0 .. 1].
bool in_gapless; // We were handed a processor to continue.
bool out_gapless; // We passed on our processor to the next.
- int filter_id; // The filter-id is in use. 0 for pass-through.
+ std::string filter_dir; // The filter-id is in use. "" for pass-through.
};
class SoundProcessor;
@@ -53,10 +53,10 @@ class SoundProcessor;
// fuse filesystem (see file-handler-cache.h for rationale)
class FileHandler {
public:
- explicit FileHandler(int filter_id) : filter_id_(filter_id) {}
+ explicit FileHandler(const std::string &filter) : filter_dir_(filter) {}
virtual ~FileHandler() {}
- int filter_id() const { return filter_id_; }
+ const std::string &filter_dir() const { return filter_dir_; }
// Returns bytes read or a negative value indicating a negative errno.
virtual int Read(char *buf, size_t size, off_t offset) = 0;
@@ -70,7 +70,7 @@ class FileHandler {
virtual bool AcceptProcessor(SoundProcessor *s) { return false; }
private:
- const int filter_id_;
+ const std::string filter_dir_;
};
#endif // FOLVE_FILE_HANDLER_H
View
168 folve-filesystem.cc
@@ -18,6 +18,7 @@
#include "folve-filesystem.h"
#include <FLAC/metadata.h>
+#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -52,14 +53,14 @@ namespace {
// everything that is not a sound-file.
class PassThroughHandler : public FileHandler {
public:
- PassThroughHandler(int filedes, int filter_id,
+ PassThroughHandler(int filedes, const std::string &filter_id,
const HandlerStats &known_stats)
: FileHandler(filter_id), filedes_(filedes),
file_size_(-1), max_accessed_(0), info_stats_(known_stats) {
DLogf("Creating PassThrough filter for '%s'", known_stats.filename.c_str());
struct stat st;
file_size_ = (Stat(&st) == 0) ? st.st_size : -1;
- info_stats_.filter_id = 0; // pass through.
+ info_stats_.filter_dir = ""; // pass through.
}
~PassThroughHandler() { close(filedes_); }
@@ -108,7 +109,7 @@ class SndFileHandler :
static FileHandler *Create(FolveFilesystem *fs,
int filedes, const char *fs_path,
const std::string &underlying_file,
- int filter_id,
+ const std::string &filter_subdir,
const std::string &zita_config_dir,
HandlerStats *partial_file_info) {
SF_INFO in_info;
@@ -161,7 +162,7 @@ class SndFileHandler :
sf_close(snd);
return NULL;
}
- return new SndFileHandler(fs, fs_path, filter_id,
+ return new SndFileHandler(fs, fs_path, filter_subdir,
underlying_file, filedes, snd, in_info,
*partial_file_info, config_path);
}
@@ -243,12 +244,13 @@ class SndFileHandler :
private:
// TODO(hzeller): trim parameter list.
- SndFileHandler(FolveFilesystem *fs, const char *fs_path, int filter_id,
+ SndFileHandler(FolveFilesystem *fs, const char *fs_path,
+ const std::string &filter_dir,
const std::string &underlying_file,
int filedes, SNDFILE *snd_in,
const SF_INFO &in_info, const HandlerStats &file_info,
const std::string &config_path)
- : FileHandler(filter_id), fs_(fs),
+ : FileHandler(filter_dir), fs_(fs),
filedes_(filedes), snd_in_(snd_in), in_info_(in_info),
config_path_(config_path),
base_stats_(file_info),
@@ -574,48 +576,46 @@ class SndFileHandler :
} // namespace
FolveFilesystem::FolveFilesystem()
- : current_cfg_index_(0), debug_ui_enabled_(false), gapless_processing_(false),
+ : debug_ui_enabled_(false), gapless_processing_(false),
open_file_cache_(4), total_file_openings_(0), total_file_reopen_(0) {
- config_dirs_.push_back(""); // The first config is special: empty.
}
FileHandler *FolveFilesystem::CreateFromDescriptor(
int filedes,
- int cfg_idx,
+ const std::string &config_dir,
const char *fs_path,
const std::string &underlying_file) {
HandlerStats file_info;
file_info.filename = fs_path;
- file_info.filter_id = cfg_idx;
- if (cfg_idx != 0) {
+ file_info.filter_dir = config_dir;
+ if (!config_dir.empty()) {
+ const std::string full_config_path = base_config_dir_ + "/" + config_dir;
FileHandler *filter = SndFileHandler::Create(this, filedes, fs_path,
underlying_file,
- cfg_idx,
- config_dirs()[cfg_idx],
+ config_dir, full_config_path,
&file_info);
if (filter != NULL) return filter;
}
// Every other file-type is just passed through as is.
- return new PassThroughHandler(filedes, cfg_idx, file_info);
+ return new PassThroughHandler(filedes, config_dir, file_info);
}
-std::string FolveFilesystem::CacheKey(int config_idx, const char *fs_path) {
- std::string result;
- Appendf(&result, "%d/%s", config_idx, fs_path);
- return result;
+std::string FolveFilesystem::CacheKey(const std::string &config_path,
+ const char *fs_path) {
+ return config_path + fs_path;
}
FileHandler *FolveFilesystem::GetOrCreateHandler(const char *fs_path) {
- const int config_idx = current_cfg_index_;
- const std::string cache_key = CacheKey(config_idx, fs_path);
+ const std::string config_path = current_config_subdir_;
+ const std::string cache_key = CacheKey(config_path, fs_path);
const std::string underlying_file = underlying_dir() + fs_path;
FileHandler *handler = open_file_cache_.FindAndPin(cache_key);
if (handler == NULL) {
int filedes = open(underlying_file.c_str(), O_RDONLY);
if (filedes < 0)
return NULL;
++total_file_openings_;
- handler = CreateFromDescriptor(filedes, config_idx,
+ handler = CreateFromDescriptor(filedes, config_path,
fs_path, underlying_file);
handler = open_file_cache_.InsertPinned(cache_key, handler);
} else {
@@ -625,7 +625,7 @@ FileHandler *FolveFilesystem::GetOrCreateHandler(const char *fs_path) {
}
int FolveFilesystem::StatByFilename(const char *fs_path, struct stat *st) {
- const std::string cache_key = CacheKey(current_cfg_index_, fs_path);
+ const std::string cache_key = CacheKey(current_config_subdir_, fs_path);
FileHandler *handler = open_file_cache_.FindAndPin(cache_key);
if (handler == 0)
return -1;
@@ -635,10 +635,19 @@ int FolveFilesystem::StatByFilename(const char *fs_path, struct stat *st) {
}
void FolveFilesystem::Close(const char *fs_path, const FileHandler *handler) {
- const std::string cache_key = CacheKey(handler->filter_id(), fs_path);
+ assert(handler != NULL);
+ const std::string cache_key = CacheKey(handler->filter_dir(), fs_path);
open_file_cache_.Unpin(cache_key);
}
+static bool IsDirectory(const std::string &path) {
+ if (path.empty()) return false;
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0)
+ return false;
+ return (st.st_mode & S_IFMT) == S_IFDIR;
+}
+
bool FolveFilesystem::ListDirectory(const std::string &fs_dir,
const std::string &suffix,
std::set<std::string> *files) {
@@ -655,49 +664,112 @@ bool FolveFilesystem::ListDirectory(const std::string &fs_dir,
return true;
}
-void FolveFilesystem::SwitchCurrentConfigIndex(int i) {
- if (i < 0 || i >= (int) config_dirs_.size())
- return;
- if (i != current_cfg_index_) {
- if (i == 0) {
- syslog(LOG_INFO, "Switching to pass-through mode.");
- } else {
- syslog(LOG_INFO, "Switching config directory to '%s'",
- config_dirs()[i].c_str());
- }
- current_cfg_index_ = i;
+bool FolveFilesystem::SanitizeConfigSubdir(std::string *subdir_path) const {
+ if (base_config_dir_.length() + 1 + subdir_path->length() > PATH_MAX)
+ return false; // uh, someone wants to buffer overflow us ?
+ const std::string to_verify_path = base_config_dir_ + "/" + *subdir_path;
+ char all_path[PATH_MAX];
+ // This will as well eat symbolic links that break out, though one could
+ // argue that that would be sane. We could think of some light
+ // canonicalization that only removes ./ and ../
+ const char *verified = realpath(to_verify_path.c_str(), all_path);
+ if (verified == NULL) { // bogus directory.
+ return false;
}
+ if (strncmp(verified, base_config_dir_.c_str(),
+ base_config_dir_.length()) != 0) {
+ // Attempt to break out with ../-tricks.
+ return false;
+ }
+ if (!IsDirectory(verified))
+ return false;
+
+ // Derive from sanitized dir. So someone can write lowpass/../highpass
+ // or '.' for empty filter. Or ./highpass. And all work.
+ *subdir_path = ((strlen(verified) == base_config_dir_.length())
+ ? "" // chose subdir '.'
+ : verified + base_config_dir_.length() + 1 /*slash*/);
+ return true;
}
-static bool IsDirectory(const std::string &path) {
- if (path.empty()) return false;
- struct stat st;
- if (stat(path.c_str(), &st) != 0)
+bool FolveFilesystem::SwitchCurrentConfigDir(const std::string &subdir_in) {
+ std::string subdir = subdir_in;
+ if (!subdir.empty() && !SanitizeConfigSubdir(&subdir)) {
+ syslog(LOG_INFO, "Invalid config switch attempt to '%s'",
+ subdir_in.c_str());
return false;
- return (st.st_mode & S_IFMT) == S_IFDIR;
+ }
+ if (subdir != current_config_subdir_) {
+ current_config_subdir_ = subdir;
+ if (subdir.empty()) {
+ syslog(LOG_INFO, "Switching to pass-through mode.");
+ } else {
+ syslog(LOG_INFO, "Switching config directory to '%s'", subdir.c_str());
+ }
+ return true;
+ }
+ return false;
}
bool FolveFilesystem::CheckInitialized() {
if (underlying_dir().empty()) {
fprintf(stderr, "Don't know the underlying directory to read from.\n");
return false;
}
+
if (!IsDirectory(underlying_dir())) {
fprintf(stderr, "<underlying-dir>: '%s' not a directory.\n",
underlying_dir().c_str());
return false;
}
- for (size_t i = 1; i < config_dirs_.size(); ++i) {
- if (!IsDirectory(config_dirs_[i])) {
- fprintf(stderr, "<config-dir>: '%s' not a directory.\n",
- config_dirs_[i].c_str());
- return false;
- }
+ if (base_config_dir_.empty() || !IsDirectory(base_config_dir_)) {
+ fprintf(stderr, "<config-dir>: '%s' not a directory.\n",
+ base_config_dir_.c_str());
+ return false;
}
- if (config_dirs_.size() > 1) {
+
+ return true;
+}
+
+void FolveFilesystem::SetupInitialConfig() {
+ std::set<std::string> available_dirs = ListConfigDirs(true);
+ // Some sanity checks.
+ if (available_dirs.size() == 1) {
+ syslog(LOG_NOTICE, "No filter configuration directories given. "
+ "Any files will be just passed through verbatim.");
+ }
+ if (available_dirs.size() > 1) {
// By default, lets set the index to the first filter the user provided.
- SwitchCurrentConfigIndex(1);
+ SwitchCurrentConfigDir(*++available_dirs.begin());
}
- return true;
}
+
+const std::set<std::string> FolveFilesystem::GetAvailableConfigDirs() const {
+ return ListConfigDirs(false);
+}
+
+const std::set<std::string> FolveFilesystem::ListConfigDirs(bool warn_invalid)
+ const {
+ std::set<std::string> result;
+ result.insert(""); // empty directory: pass-through.
+ DIR *dp = opendir(base_config_dir_.c_str());
+ if (dp == NULL) return result;
+ struct dirent *dent;
+ while ((dent = readdir(dp)) != NULL) {
+ std::string subdir = dent->d_name;
+ if (subdir == "." || subdir == "..")
+ continue;
+ if (!SanitizeConfigSubdir(&subdir)) {
+ if (warn_invalid) {
+ syslog(LOG_INFO, "Note: '%s' ignored in config directory; not a "
+ "directory or pointing outside base directory.", dent->d_name);
+ }
+ continue;
+ }
+ result.insert(subdir);
+ }
+ closedir(dp);
+ return result;
+}
+
View
42 folve-filesystem.h
@@ -43,20 +43,29 @@ class FolveFilesystem {
void set_underlying_dir(const std::string &dir) { underlying_dir_ = dir; }
const std::string &underlying_dir() const { return underlying_dir_; }
- // Config directories contain the filter configurations.
- void add_config_dir(const std::string &config_dir) {
- config_dirs_.push_back(config_dir);
+ // Set the base configuration directory.
+ void SetBaseConfigDir(const std::string &config_dir) {
+ base_config_dir_ = config_dir;
}
- const std::vector<std::string> &config_dirs() const { return config_dirs_; }
- // Switch the current config to i. Values out of range are not accepted.
- void SwitchCurrentConfigIndex(int i);
- int current_cfg_index() const { return current_cfg_index_; }
+ // Return a set of available named configurations. Essentially the names
+ // of subdirectories in the configuration dir.
+ const std::set<std::string> GetAvailableConfigDirs() const;
+
+ // Switch the current config to subdir. Returns 'true', if this was a valid
+ // choice and we actually did a switch to a new directory.
+ bool SwitchCurrentConfigDir(const std::string &subdir);
+ const std::string &current_config_subdir() const {
+ return current_config_subdir_;
+ }
// Check if properly initialized. Return 'false' if not and print a message
// to stderr.
bool CheckInitialized();
+ // After startup: choose the initial configuation.
+ void SetupInitialConfig();
+
// Create a new filter given the filesystem path and the underlying
// path.
// Returns NULL, if it cannot be created.
@@ -89,15 +98,26 @@ class FolveFilesystem {
private:
// Get cache key, depending on the given configuration.
- std::string CacheKey(int config_idx, const char *fs_path);
+ std::string CacheKey(const std::string &config_path, const char *fs_path);
- FileHandler *CreateFromDescriptor(int filedes, int cfg_idx,
+ FileHandler *CreateFromDescriptor(int filedes, const std::string &cfg_dir,
const char *fs_path,
const std::string &underlying_file);
+ // Sanitize path to configuration subdirectory. Checks if someone tries
+ // to break out of the given base directory.
+ // Return if this is a sane directory.
+ // Passes the sanitized directory in the parameter.
+ bool SanitizeConfigSubdir(std::string *subdir_path) const;
+
+ // List available config directories; if "warn_invalid" is true,
+ // non-directories or symbolic links breaking out of the directory are
+ // reported.
+ const std::set<std::string> ListConfigDirs(bool warn_invalid) const;
+
std::string underlying_dir_;
- std::vector<std::string> config_dirs_;
- int current_cfg_index_;
+ std::string base_config_dir_;
+ std::string current_config_subdir_;
bool debug_ui_enabled_;
bool gapless_processing_;
FileHandlerCache open_file_cache_;
View
28 folve-main.cc
@@ -30,6 +30,7 @@
#include <sys/time.h>
#include <syslog.h>
#include <unistd.h>
+#include <sndfile.h> // for sf_version_string
#include "folve-filesystem.h"
#include "status-server.h"
@@ -154,8 +155,10 @@ static void *folve_init(struct fuse_conn_info *conn) {
char *ident = (char*) malloc(ident_len); // openlog() keeps reference. Leaks.
snprintf(ident, ident_len, "folve[%d]", getpid());
openlog(ident, LOG_CONS|LOG_PERROR, LOG_USER);
- syslog(LOG_INFO, "Version " FOLVE_VERSION " started. "
- "Serving '%s' on mount point '%s'",
+ syslog(LOG_INFO, "Version " FOLVE_VERSION " started "
+ "(with fuse=%d.%d; sndfile=%s). ",
+ FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION, sf_version_string());
+ syslog(LOG_INFO, "Serving '%s' on mount point '%s'",
folve_rt.fs->underlying_dir().c_str(), folve_rt.mount_point);
if (folve::IsDebugLogEnabled()) {
syslog(LOG_INFO, "Debug logging enabled (-D)");
@@ -174,16 +177,7 @@ static void *folve_init(struct fuse_conn_info *conn) {
}
}
- // Some sanity checks.
- if (folve_rt.fs->config_dirs().size() == 1) {
- syslog(LOG_NOTICE, "No filter configuration directories given. "
- "Any files will be just passed through verbatim.");
- }
- if (folve_rt.fs->config_dirs().size() > 2 && folve_rt.status_port < 0) {
- syslog(LOG_WARNING, "Multiple filter configurations given, but no HTTP "
- "status port. You only can switch filters via the HTTP interface; "
- "add -p <port>");
- }
+ folve_rt.fs->SetupInitialConfig();
return NULL;
}
@@ -194,8 +188,8 @@ static void folve_destroy(void *) {
static int usage(const char *prg) {
printf("usage: %s [options] <original-dir> <mount-point-dir>\n", prg);
printf("Options: (in sequence of usefulness)\n"
- "\t-c <cfg-dir> : Convolver configuration directory.\n"
- "\t You can supply this option multiple times:\n"
+ "\t-C <cfg-dir> : Convolver base configuration directory.\n"
+ "\t Sub-directories name the different filters.\n"
"\t Select on the HTTP status page.\n"
"\t-p <port> : Port to run the HTTP status server on.\n"
"\t-r <refresh> : Seconds between refresh of status page;\n"
@@ -254,9 +248,9 @@ int FolveOptionHandling(void *data, const char *arg, int key,
rt->refresh_time = atoi(arg + 2); // strip "-r"
return 0;
case FOLVE_OPT_CONFIG: {
- const char *config_dir = realpath(arg + 2, realpath_buf); // strip "-c"
+ const char *config_dir = realpath(arg + 2, realpath_buf); // strip "-C"
if (config_dir != NULL) {
- rt->fs->add_config_dir(config_dir);
+ rt->fs->SetBaseConfigDir(config_dir);
} else {
fprintf(stderr, "Invalid config dir '%s': %s\n",
arg + 2, strerror(errno));
@@ -286,7 +280,7 @@ int main(int argc, char *argv[]) {
static struct fuse_opt folve_options[] = {
FUSE_OPT_KEY("-p ", FOLVE_OPT_PORT),
FUSE_OPT_KEY("-r ", FOLVE_OPT_REFRESH_TIME),
- FUSE_OPT_KEY("-c ", FOLVE_OPT_CONFIG),
+ FUSE_OPT_KEY("-C ", FOLVE_OPT_CONFIG),
FUSE_OPT_KEY("-D", FOLVE_OPT_DEBUG),
FUSE_OPT_KEY("-g", FOLVE_OPT_GAPLESS),
FUSE_OPT_END // This fails to compile for fuse <= 2.8.1; get >= 2.8.4
View
120 status-server.cc
@@ -65,11 +65,16 @@ static const char kStartHtmlHeader[] = "<html><head>"
// that into a C-string that we can include it in the binary.
static const char kCSS[] =
"<style type='text/css'>"
+ " body { font-family:Sans-Serif; }\n"
" a:link { text-decoration:none; }\n"
" a:visited { text-decoration:none; }\n"
" a:hover { text-decoration:underline; }\n"
" a:active { text-decoration:underline; }\n"
" .rounded_box, .filter_sel {\n"
+ " float: left;\n"
+ " margin: 5px;\n"
+ " margin-right: 5px;\n"
+ " margin-bottom: 5px;\n"
" padding: 5px 15px;\n"
" border-radius: 5px;\n"
" -moz-border-radius: 5px; }\n"
@@ -137,12 +142,8 @@ StatusServer::StatusServer(FolveFilesystem *fs)
}
void StatusServer::SetFilter(const char *filter) {
- if (filter == NULL || *filter == '\0') return;
- char *end;
- int index = strtol(filter, &end, 10);
- if (end == NULL || *end != '\0') return;
- filter_switched_ = (index != filesystem_->current_cfg_index());
- filesystem_->SwitchCurrentConfigIndex(index);
+ if (filter == NULL) return;
+ filter_switched_ = filesystem_->SwitchCurrentConfigDir(filter);
}
void StatusServer::SetDebug(const char *dbg) {
@@ -151,7 +152,6 @@ void StatusServer::SetDebug(const char *dbg) {
}
bool StatusServer::Start(int port) {
- PrepareConfigDirectoriesForUI();
daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, NULL,
&HandleHttp, this,
MHD_OPTION_END);
@@ -186,6 +186,28 @@ void StatusServer::RetireHandlerEvent(FileHandler *handler) {
}
}
+// The directories are user-input, so we need to sanitize stuff.
+static void AppendSanitizedUrlParam(const std::string &in, std::string *out) {
+ for (std::string::const_iterator i = in.begin(); i != in.end(); ++i) {
+ if (isupper(*i) || islower(*i) || isdigit(*i)) {
+ out->append(1, *i);
+ } else {
+ Appendf(out, "%%%02x", (unsigned char) *i);
+ }
+ }
+}
+
+static void AppendSanitizedHTML(const std::string &in, std::string *out) {
+ for (std::string::const_iterator i = in.begin(); i != in.end(); ++i) {
+ switch (*i) {
+ case '<': out->append("&lt;"); break;
+ case '>': out->append("&gt;"); break;
+ case '&': out->append("&amp;"); break;
+ default: out->append(1, *i);
+ }
+ }
+}
+
// As ugly #defines, so that gcc can warn about printf() format problems.
#define sMessageRowHtml \
"<td>%s</td><td colspan='3' style='font-size:small;'>%s</td>"
@@ -242,73 +264,57 @@ void StatusServer::AppendFileInfo(const char *progress_style,
content_.append("<td>-</td>");
}
- Appendf(&content_, "<td class='fb'>&nbsp;%s (%s)&nbsp;</td>",
- stats.format.c_str(), ui_config_directories_[stats.filter_id].c_str());
- Appendf(&content_,"<td class='fn'>%s</td>", stats.filename.c_str());
- content_.append("</tr>\n");
-}
-
-void StatusServer::PrepareConfigDirectoriesForUI() {
- // Essentially only keep the directory name.
- if (!ui_config_directories_.empty()) return;
- ui_config_directories_.push_back("None : Pass Through");
- for (size_t i = 1; i < filesystem_->config_dirs().size(); ++i) {
- std::string d = filesystem_->config_dirs()[i];
- while (d.length() > 0 && d[d.length() - 1] == '/') {
- d.resize(d.length() - 1); // trim trailing slashes.
- }
- const std::string::size_type slash_pos = d.find_last_of('/');
- if (slash_pos != std::string::npos) {
- d = d.substr(slash_pos + 1);
- }
- ui_config_directories_.push_back(d);
- }
+ const char *filter_dir = stats.filter_dir.empty()
+ ? "Pass Through" : stats.filter_dir.c_str();
+ Appendf(&content_, "<td class='fb'>&nbsp;%s (", stats.format.c_str());
+ AppendSanitizedHTML(filter_dir, &content_);
+ content_.append(")&nbsp;</td><td class='fn'>");
+ AppendSanitizedHTML(stats.filename, &content_);
+ content_.append("</td></tr>\n");
}
-static void CreateSelection(std::string *result,
- const std::vector<std::string> &option_titles,
- const std::vector<std::string> &options,
- int selected) {
+static void CreateSelection(const std::set<std::string> &options,
+ const std::string &selected,
+ std::string *result) {
if (options.size() == 1) {
- result->append(options[0]); // no reason to make this a form :)
+ result->append(selected);
return;
}
- for (size_t i = 0; i < options.size(); ++i) {
- const std::string &c = options[i];
- result->append("&nbsp;");
- const bool active = (int) i == selected;
+ typedef std::set<std::string> Set;
+ for (Set::const_iterator it = options.begin(); it != options.end(); ++it) {
+ const bool active = (*it == selected);
+ const char *title = it->empty() ? "None : Pass Through" : it->c_str();
if (active) {
- Appendf(result, "<span title='%s' class='filter_sel active'>%s</span>\n",
- (option_titles.size() > i) ? option_titles[i].c_str() : "",
- c.c_str());
+ result->append("<span class='filter_sel active'>");
+ AppendSanitizedHTML(title, result);
+ result->append("</span>");
} else {
- Appendf(result, "<a title='%s' class='filter_sel inactive' "
- "href='%s?f=%zd'>%s</a>\n",
- (option_titles.size() > i) ? option_titles[i].c_str() : "",
- kSettingsUrl, i, c.c_str());
+ Appendf(result, "<a class='filter_sel inactive' href='%s?f=",
+ kSettingsUrl);
+ AppendSanitizedUrlParam(*it, result);
+ result->append("'>");
+ AppendSanitizedHTML(title, result);
+ result->append("</a>\n");
}
}
}
void StatusServer::AppendSettingsForm() {
- content_.append("<p>Active filter: ");
- CreateSelection(&content_,
- show_details()
- ? filesystem_->config_dirs()
- : std::vector<std::string>(),
- ui_config_directories_,
- filesystem_->current_cfg_index());
- if (filesystem_->config_dirs().size() == 1) {
- content_.append(" (This is a boring configuration, add filter directories "
- "with -c &lt;dir&gt; [-c &lt;another-dir&gt; ...] :-) )");
+ content_.append("<p><span class='filter_sel'>Active filter:</span>");
+ std::set<std::string> available_dirs = filesystem_->GetAvailableConfigDirs();
+ CreateSelection(available_dirs,
+ filesystem_->current_config_subdir(),
+ &content_);
+ if (available_dirs.empty() == 1) {
+ content_.append(" (This is a boring configuration, add filter directories)");
} else if (filter_switched_) {
- content_.append("&nbsp;<span class='rounded_box' "
+ content_.append("<span class='rounded_box' "
"style='font-size:small;background:#FFFFa0;'>"
"Affects re- or newly opened files.</span>");
filter_switched_ = false; // only show once.
}
// TODO: re-add something for filesystem_->is_debug_ui_enabled()
- content_.append("</p><hr/>");
+ content_.append("</p><hr style='clear:both;'/>");
}
struct CompareStats {
@@ -333,7 +339,7 @@ const std::string &StatusServer::CreatePage() {
content_.append(kCSS);
content_.append("</head>\n");
- content_.append("<body style='font-family:Sans-Serif;'>\n");
+ content_.append("<body>\n");
Appendf(&content_, "<center style='background-color:#A0FFA0;'>"
"Welcome to "
"<a href='https://github.com/hzeller/folve#readme'>Folve</a> "
View
4 status-server.h
@@ -53,7 +53,6 @@ class StatusServer : protected FileHandlerCache::Observer {
// Some helper functions to create the page:
void AppendSettingsForm();
- void PrepareConfigDirectoriesForUI();
void AppendFileInfo(const char *progress_style, const HandlerStats &stats);
// Set filter or debug mode from http-request. Gracefully handles garbage.
@@ -72,9 +71,6 @@ class StatusServer : protected FileHandlerCache::Observer {
int expunged_retired_;
folve::Mutex retired_mutex_;
- // Config directories with common prefix removed to have them concise.
- std::vector<std::string> ui_config_directories_;
-
double total_seconds_filtered_;
double total_seconds_music_seen_;
int meta_refresh_time_;

0 comments on commit fa02b45

Please sign in to comment.