search: fix parsing of quoted metatag values.

* Fix #4552: Multiple quoted search terms not parsed correctly.
* Allow quotes to be escaped in quoted metatags.
* Allow spaces to be escaped in unquoted metatags.
* Allow the empty string to be used in metatags.

Examples:

* `source:""` and `source:''` (same as `source:none`)
* `source:foo\ bar\ baz` (same as `source:"foo bar baz"`)
* `source:"don't say \"lazy\""` (use \" to write a literal ")
* `source:'don\'t say "lazy"'` (use \' to write a literal ')
* `source:"C:\\Windows"` (use \\ to write a literal \)
This commit is contained in:
evazion
2020-08-12 17:57:22 -05:00
parent dd8c3fad2c
commit d884cb6642
2 changed files with 74 additions and 24 deletions

View File

@@ -332,8 +332,9 @@ class PostQueryBuilder
end
def source_matches(source, quoted = false)
case source.downcase
in "none" unless quoted
if source.empty?
Post.where_like(:source, "")
elsif source.downcase == "none" && !quoted
Post.where_like(:source, "")
else
Post.where_ilike(:source, source + "*")
@@ -644,14 +645,7 @@ class PostQueryBuilder
if scanner.scan(/(-)?(#{METATAGS.join("|")}):/io)
operator = scanner.captures.first
metatag = scanner.captures.second.downcase
if scanner.scan(/"(.+)"/) || scanner.scan(/'(.+)'/)
value = scanner.captures.first
quoted = true
else
value = scanner.scan(/[^ ]*/)
quoted = false
end
value, quoted = scan_string(scanner)
if metatag.in?(COUNT_METATAG_SYNONYMS)
metatag = metatag.singularize + "_count"
@@ -675,23 +669,41 @@ class PostQueryBuilder
terms
end
def scan_string(scanner)
if scanner.scan(/"((?:\\"|[^"])*)"/)
value = scanner.captures.first.gsub(/\\(.)/) { $1 }
quoted = true
elsif scanner.scan(/'((?:\\'|[^'])*)'/)
value = scanner.captures.first.gsub(/\\(.)/) { $1 }
quoted = true
else
value = scanner.scan(/(\\ |[^ ])*/)
value = value.gsub(/\\ /) { " " }
quoted = false
end
[value, quoted]
end
def split_query
terms.map do |term|
if term.type == :metatag && !term.negated && !term.quoted
"#{term.name}:#{term.value}"
elsif term.type == :metatag && !term.negated && term.quoted
"#{term.name}:\"#{term.value}\""
elsif term.type == :metatag && term.negated && !term.quoted
"-#{term.name}:#{term.value}"
elsif term.type == :metatag && term.negated && term.quoted
"-#{term.name}:\"#{term.value}\""
elsif term.type == :tag && term.negated
"-#{term.name}"
elsif term.type == :tag && term.optional
"~#{term.name}"
elsif term.type == :tag
term.name
type, name, value = term.type, term.name, term.value
str = ""
str += "-" if term.negated
str += "~" if term.optional
if type == :tag
str += name
elsif type == :metatag && (term.quoted || value.include?(" "))
value = value.gsub(/\\/) { '\\\\' }
value = value.gsub(/"/) { '\\"' }
str += "#{name}:\"#{value}\""
elsif type == :metatag
str += "#{name}:#{value}"
end
str
end
end