diff options
author | Max Kellermann <max@musicpd.org> | 2018-11-02 19:15:08 +0100 |
---|---|---|
committer | Max Kellermann <max@musicpd.org> | 2018-11-02 19:15:08 +0100 |
commit | 528f5b9cb94821e9b5e575b8ce99440bbbfc38ad (patch) | |
tree | a42a21ecb05dcbfc12351bdb08635b0c8dcffdef /src/song | |
parent | 96ae0ec93aa3a008ec15dd595bfe1890f411d58c (diff) |
song/Filter: allow escaping quotes in filter expressions
Closes #397
Diffstat (limited to 'src/song')
-rw-r--r-- | src/song/BaseSongFilter.cxx | 3 | ||||
-rw-r--r-- | src/song/Escape.cxx | 41 | ||||
-rw-r--r-- | src/song/Escape.hxx | 31 | ||||
-rw-r--r-- | src/song/Filter.cxx | 25 | ||||
-rw-r--r-- | src/song/TagSongFilter.cxx | 3 | ||||
-rw-r--r-- | src/song/UriSongFilter.cxx | 3 | ||||
-rw-r--r-- | src/song/meson.build | 1 |
7 files changed, 98 insertions, 9 deletions
diff --git a/src/song/BaseSongFilter.cxx b/src/song/BaseSongFilter.cxx index b30527478..393a9f3be 100644 --- a/src/song/BaseSongFilter.cxx +++ b/src/song/BaseSongFilter.cxx @@ -19,13 +19,14 @@ #include "config.h" #include "BaseSongFilter.hxx" +#include "Escape.hxx" #include "LightSong.hxx" #include "util/UriUtil.hxx" std::string BaseSongFilter::ToExpression() const noexcept { - return "(base \"" + value + "\")"; + return "(base \"" + EscapeFilterString(value) + "\")"; } bool diff --git a/src/song/Escape.cxx b/src/song/Escape.cxx new file mode 100644 index 000000000..25c64789b --- /dev/null +++ b/src/song/Escape.cxx @@ -0,0 +1,41 @@ +/* + * Copyright 2003-2018 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "Escape.hxx" + +static constexpr bool +MustEscape(char ch) noexcept +{ + return ch == '"' || ch == '\'' || ch == '\\'; +} + +std::string +EscapeFilterString(const std::string &src) noexcept +{ + std::string result; + result.reserve(src.length() + 16); + + for (char ch : src) { + if (MustEscape(ch)) + result.push_back('\\'); + result.push_back(ch); + } + + return result; +} diff --git a/src/song/Escape.hxx b/src/song/Escape.hxx new file mode 100644 index 000000000..b63faf06e --- /dev/null +++ b/src/song/Escape.hxx @@ -0,0 +1,31 @@ +/* + * Copyright 2003-2018 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SONG_ESCAPE_HXX +#define MPD_SONG_ESCAPE_HXX + +#include "util/Compiler.h" + +#include <string> + +gcc_pure +std::string +EscapeFilterString(const std::string &src) noexcept; + +#endif diff --git a/src/song/Filter.cxx b/src/song/Filter.cxx index ff1be66ee..d873ea3bd 100644 --- a/src/song/Filter.cxx +++ b/src/song/Filter.cxx @@ -173,13 +173,26 @@ ExpectQuoted(const char *&s) if (!IsQuote(quote)) throw std::runtime_error("Quoted string expected"); - const char *begin = s; - const char *end = strchr(s, quote); - if (end == nullptr) - throw std::runtime_error("Closing quote not found"); + char buffer[4096]; + size_t length = 0; + + while (*s != quote) { + if (*s == '\\') + /* backslash escapes the following character */ + ++s; + + if (*s == 0) + throw std::runtime_error("Closing quote not found"); + + buffer[length++] = *s++; + + if (length >= sizeof(buffer)) + throw std::runtime_error("Quoted value is too long"); + } + + s = StripLeft(s + 1); - s = StripLeft(end + 1); - return {begin, end}; + return {buffer, length}; } ISongFilterPtr diff --git a/src/song/TagSongFilter.cxx b/src/song/TagSongFilter.cxx index c05f1765b..a6578331b 100644 --- a/src/song/TagSongFilter.cxx +++ b/src/song/TagSongFilter.cxx @@ -19,6 +19,7 @@ #include "config.h" #include "TagSongFilter.hxx" +#include "Escape.hxx" #include "LightSong.hxx" #include "tag/Tag.hxx" #include "tag/Fallback.hxx" @@ -30,7 +31,7 @@ TagSongFilter::ToExpression() const noexcept ? "any" : tag_item_names[type]; - return std::string("(") + name + " " + (negated ? "!=" : "==") + " \"" + filter.GetValue() + "\")"; + return std::string("(") + name + " " + (negated ? "!=" : "==") + " \"" + EscapeFilterString(filter.GetValue()) + "\")"; } bool diff --git a/src/song/UriSongFilter.cxx b/src/song/UriSongFilter.cxx index 4445d8c08..584595b65 100644 --- a/src/song/UriSongFilter.cxx +++ b/src/song/UriSongFilter.cxx @@ -19,12 +19,13 @@ #include "config.h" #include "UriSongFilter.hxx" +#include "Escape.hxx" #include "LightSong.hxx" std::string UriSongFilter::ToExpression() const noexcept { - return std::string("(file ") + (negated ? "!=" : "==") + " \"" + filter.GetValue() + "\")"; + return std::string("(file ") + (negated ? "!=" : "==") + " \"" + EscapeFilterString(filter.GetValue()) + "\")"; } bool diff --git a/src/song/meson.build b/src/song/meson.build index 5e4cd534d..71eda8d65 100644 --- a/src/song/meson.build +++ b/src/song/meson.build @@ -1,6 +1,7 @@ song = static_library( 'song', 'DetachedSong.cxx', + 'Escape.cxx', 'StringFilter.cxx', 'UriSongFilter.cxx', 'BaseSongFilter.cxx', |