class RIMS::Protocol::SearchParser
Attributes
charset[R]
Public Class Methods
new(mail_store, folder, charset_aliases: RFC822::DEFAULT_CHARSET_ALIASES, charset_convert_options: nil)
click to toggle source
# File lib/rims/protocol/parser.rb, line 280 def initialize(mail_store, folder, charset_aliases: RFC822::DEFAULT_CHARSET_ALIASES, charset_convert_options: nil) @mail_store = mail_store @folder = folder @charset_aliases = charset_aliases @charset_convert_options = charset_convert_options || {} @charset = nil @mail_cache = Hash.new{|hash, uid| if (msg_txt = @mail_store.msg_text(@folder.mbox_id, uid)) then hash[uid] = RFC822::Message.new(msg_txt, charset_aliases: @charset_aliases) end } end
Public Instance Methods
charset=(new_charset)
click to toggle source
# File lib/rims/protocol/parser.rb, line 300 def charset=(new_charset) charset_encoding = @charset_aliases[new_charset] || Encoding.find(new_charset) if (charset_encoding.dummy?) then # same error type as `Encoding.find' raise ArgumentError, "not a searchable charset: #{new_charset}" end @charset = charset_encoding end
parse(search_key)
click to toggle source
# File lib/rims/protocol/parser.rb, line 773 def parse(search_key) cond = parse_cached(search_key) proc{|msg| found = cond.call(msg) @mail_cache.clear found } end
parse_msg_flag_disabled(name)
click to toggle source
# File lib/rims/protocol/parser.rb, line 354 def parse_msg_flag_disabled(name) proc{|next_cond| proc{|msg| (! @mail_store.msg_flag(@folder.mbox_id, msg.uid, name)) && next_cond.call(msg) } } end
Private Instance Methods
compile_search_regexp(search_string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 326 def compile_search_regexp(search_string) Regexp.new(Regexp.quote(search_string), true) end
encode_charset(string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 317 def encode_charset(string) if (string.encoding == @charset) then string else string.encode(@charset, **@charset_convert_options) end end
end_of_cond()
click to toggle source
# File lib/rims/protocol/parser.rb, line 331 def end_of_cond proc{|msg| true } end
fetch_next_node(search_key)
click to toggle source
# File lib/rims/protocol/parser.rb, line 637 def fetch_next_node(search_key) if (search_key.empty?) then raise SyntaxError, 'unexpected end of search key.' end op = search_key.shift op = op.upcase if (op.is_a? String) case (op) when 'ALL' factory = parse_all when 'ANSWERED' factory = parse_msg_flag_enabled('answered') when 'BCC' search_string = shift_string_value('BCC', search_key) factory = parse_search_header('bcc', search_string) when 'BEFORE' search_date = shift_date_value('BEFORE', search_key) factory = parse_internal_date(search_date) {|d, boundary| d < boundary } when 'BODY' search_string = shift_string_value('BODY', search_key) factory = parse_body(search_string) when 'CC' search_string = shift_string_value('CC', search_key) factory = parse_search_header('cc', search_string) when 'DELETED' factory = parse_msg_flag_enabled('deleted') when 'DRAFT' factory = parse_msg_flag_enabled('draft') when 'FLAGGED' factory = parse_msg_flag_enabled('flagged') when 'FROM' search_string = shift_string_value('FROM', search_key) factory = parse_search_header('from', search_string) when 'HEADER' header_name = shift_string_value('HEADER', search_key) search_string = shift_string_value('HEADER', search_key) factory = parse_search_header(header_name, search_string) when 'KEYWORD' search_string = shift_string_value('KEYWORD', search_key) factory = parse_keyword(search_string) when 'LARGER' octet_size = shift_octet_size_value('LARGER', search_key) factory = parse_mail_bytesize(octet_size) {|size, boundary| size > boundary } when 'NEW' factory = parse_new when 'NOT' next_node = fetch_next_node(search_key) factory = parse_not(next_node) when 'OLD' factory = parse_old when 'ON' search_date = shift_date_value('ON', search_key) factory = parse_internal_date(search_date) {|d, boundary| d == boundary } when 'OR' next_node1 = fetch_next_node(search_key) next_node2 = fetch_next_node(search_key) factory = parse_or(next_node1, next_node2) when 'RECENT' factory = parse_msg_flag_enabled('recent') when 'SEEN' factory = parse_msg_flag_enabled('seen') when 'SENTBEFORE' search_date = shift_date_value('SENTBEFORE', search_key) factory = parse_mail_date(search_date) {|d, boundary| d < boundary } when 'SENTON' search_date = shift_date_value('SENTON', search_key) factory = parse_mail_date(search_date) {|d, boundary| d == boundary } when 'SENTSINCE' search_date = shift_date_value('SENTSINCE', search_key) factory = parse_mail_date(search_date) {|d, boundary| d > boundary } when 'SINCE' search_date = shift_date_value('SINCE', search_key) factory = parse_internal_date(search_date) {|d, boundary| d > boundary } when 'SMALLER' octet_size = shift_octet_size_value('SMALLER', search_key) factory = parse_mail_bytesize(octet_size) {|size, boundary| size < boundary } when 'SUBJECT' search_string = shift_string_value('SUBJECT', search_key) factory = parse_search_header('subject', search_string) when 'TEXT' search_string = shift_string_value('TEXT', search_key) factory = parse_text(search_string) when 'TO' search_string = shift_string_value('TO', search_key) factory = parse_search_header('to', search_string) when 'UID' mset_string = shift_string_value('UID', search_key) msg_set = @folder.parse_msg_set(mset_string, uid: true) factory = parse_uid(msg_set) when 'UNANSWERED' factory = parse_msg_flag_disabled('answered') when 'UNDELETED' factory = parse_msg_flag_disabled('deleted') when 'UNDRAFT' factory = parse_msg_flag_disabled('draft') when 'UNFLAGGED' factory = parse_msg_flag_disabled('flagged') when 'UNKEYWORD' search_string = shift_string_value('UNKEYWORD', search_key) factory = parse_unkeyword(search_string) when 'UNSEEN' factory = parse_msg_flag_disabled('seen') when String begin msg_set = @folder.parse_msg_set(op, uid: false) factory = parse_msg_set(msg_set) rescue MessageSetSyntaxError raise SyntaxError, "unknown search key: #{op}" end when Array case (op[0]) when :group factory = parse_group(op[1..-1]) else raise SyntaxError, "unknown search key: #{op}" end else raise SyntaxError, "unknown search key: #{op}" end factory end
force_charset(string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 309 def force_charset(string) string = string.dup string.force_encoding(@charset) string.valid_encoding? or raise SyntaxError, "invalid #{@charset} string: #{string.inspect}" string end
get_mail(msg)
click to toggle source
# File lib/rims/protocol/parser.rb, line 293 def get_mail(msg) @mail_cache[msg.uid] or raise "not found a mail: #{msg.uid}" end
parse_all()
click to toggle source
# File lib/rims/protocol/parser.rb, line 336 def parse_all proc{|next_cond| proc{|msg| next_cond.call(msg) } } end
parse_body(search_string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 428 def parse_body(search_string) if (@charset) search_string = force_charset(search_string) search_regexp = compile_search_regexp(search_string) search_body = proc{|mail| if (mail.text? || mail.messge?) then search_regexp.match? encode_charset(mail.mime_charset_body_text) elsif (mail.multipart?) then mail.parts.any?{|next_mail| search_body.call(next_mail) } else false end } else search_string = search_string.b search_regexp = compile_search_regexp(search_string) search_body = proc{|mail| if (mail.text? || mail.message?)then search_regexp.match? mail.mime_binary_body_string elsif (mail.multipart?) then mail.parts.any?{|next_mail| search_body.call(next_mail) } else false end } end proc{|next_cond| proc{|msg| if (mail = get_mail(msg)) then search_body.call(mail) && next_cond.call(msg) else false end } } end
parse_cached(search_key)
click to toggle source
# File lib/rims/protocol/parser.rb, line 762 def parse_cached(search_key) unless (search_key.empty?) then search_key = search_key.dup factory = fetch_next_node(search_key) _cond = factory.call(parse_cached(search_key)) else _cond = end_of_cond end end
parse_group(search_key)
click to toggle source
# File lib/rims/protocol/parser.rb, line 587 def parse_group(search_key) group_cond = parse_cached(search_key) proc{|next_cond| proc{|msg| group_cond.call(msg) && next_cond.call(msg) } } end
parse_internal_date(search_time) { |mail_date, boundary| ... }
click to toggle source
# File lib/rims/protocol/parser.rb, line 395 def parse_internal_date(search_time) # :yields: mail_date, boundary d = search_time.to_date proc{|next_cond| proc{|msg| yield(@mail_store.msg_date(@folder.mbox_id, msg.uid).utc.to_date, d) && next_cond.call(msg) } } end
parse_keyword(search_string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 471 def parse_keyword(search_string) proc{|next_cond| proc{|msg| false } } end
parse_mail_bytesize(octet_size) { |mail_bytesize, boundary| ... }
click to toggle source
# File lib/rims/protocol/parser.rb, line 419 def parse_mail_bytesize(octet_size) # :yields: mail_bytesize, boundary proc{|next_cond| proc{|msg| yield(@mail_store.msg_text(@folder.mbox_id, msg.uid).bytesize, octet_size) && next_cond.call(msg) } } end
parse_mail_date(search_time) { |internal_date, boundary| ... }
click to toggle source
# File lib/rims/protocol/parser.rb, line 405 def parse_mail_date(search_time) # :yields: internal_date, boundary d = search_time.to_date proc{|next_cond| proc{|msg| if (mail_datetime = get_mail(msg).date) then yield(mail_datetime.getutc.to_date, d) && next_cond.call(msg) else false end } } end
parse_msg_flag_enabled(name)
click to toggle source
# File lib/rims/protocol/parser.rb, line 345 def parse_msg_flag_enabled(name) proc{|next_cond| proc{|msg| @mail_store.msg_flag(@folder.mbox_id, msg.uid, name) && next_cond.call(msg) } } end
parse_msg_set(msg_set)
click to toggle source
# File lib/rims/protocol/parser.rb, line 578 def parse_msg_set(msg_set) proc{|next_cond| proc{|msg| (msg_set.include? msg.num) && next_cond.call(msg) } } end
parse_new()
click to toggle source
# File lib/rims/protocol/parser.rb, line 480 def parse_new proc{|next_cond| proc{|msg| @mail_store.msg_flag(@folder.mbox_id, msg.uid, 'recent') && \ (! @mail_store.msg_flag(@folder.mbox_id, msg.uid, 'seen')) && next_cond.call(msg) } } end
parse_not(next_node)
click to toggle source
# File lib/rims/protocol/parser.rb, line 490 def parse_not(next_node) operand = next_node.call(end_of_cond) proc{|next_cond| proc{|msg| (! operand.call(msg)) && next_cond.call(msg) } } end
parse_old()
click to toggle source
# File lib/rims/protocol/parser.rb, line 500 def parse_old proc{|next_cond| proc{|msg| (! @mail_store.msg_flag(@folder.mbox_id, msg.uid, 'recent')) && next_cond.call(msg) } } end
parse_or(next_node1, next_node2)
click to toggle source
# File lib/rims/protocol/parser.rb, line 509 def parse_or(next_node1, next_node2) operand1 = next_node1.call(end_of_cond) operand2 = next_node2.call(end_of_cond) proc{|next_cond| proc{|msg| (operand1.call(msg) || operand2.call(msg)) && next_cond.call(msg) } } end
parse_search_header(field_name, search_string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 363 def parse_search_header(field_name, search_string) if (@charset) then search_string = force_charset(search_string) search_regexp = compile_search_regexp(search_string) search_header = proc{|mail| mail.mime_decoded_header_field_value_list(field_name, @charset, charset_convert_options: @charset_convert_options).any?{|field_value| search_regexp.match? field_value } } else search_string = search_string.b search_regexp = compile_search_regexp(search_string) search_header = proc{|mail| mail.header.field_value_list(field_name).any?{|field_value| search_regexp.match? field_value } } end proc{|next_cond| proc{|msg| mail = get_mail(msg) if (mail.header.key? field_name) then search_header.call(mail) && next_cond.call(msg) else false end } } end
parse_text(search_string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 520 def parse_text(search_string) if (@charset) then search_string = force_charset(search_string) search_regexp = compile_search_regexp(search_string) search_text = proc{|mail| if (search_regexp.match? mail.mime_decoded_header_text(@charset, charset_convert_options: @charset_convert_options)) then true elsif (mail.text? || mail.message?) then search_regexp.match? encode_charset(mail.mime_charset_body_text) elsif (mail.multipart?) then mail.parts.any?{|next_mail| search_text.call(next_mail) } else false end } else search_string = search_string.b search_regexp = compile_search_regexp(search_string) search_text = proc{|mail| if (search_regexp.match? mail.header.raw_source) then true elsif (mail.text? || mail.message?) then search_regexp.match? mail.mime_binary_body_string elsif (mail.multipart?) then mail.parts.any?{|next_mail| search_text.call(next_mail) } else false end } end proc{|next_cond| proc{|msg| mail = get_mail(msg) search_text.call(mail) && next_cond.call(msg) } } end
parse_uid(msg_set)
click to toggle source
# File lib/rims/protocol/parser.rb, line 564 def parse_uid(msg_set) proc{|next_cond| proc{|msg| (msg_set.include? msg.uid) && next_cond.call(msg) } } end
parse_unkeyword(search_string)
click to toggle source
# File lib/rims/protocol/parser.rb, line 573 def parse_unkeyword(search_string) parse_all end
shift_date_value(operation_name, search_key)
click to toggle source
# File lib/rims/protocol/parser.rb, line 609 def shift_date_value(operation_name, search_key) unless (search_date_string = search_key.shift) then raise SyntaxError, "need for a search date of #{operation_name}." end unless (search_date_string.is_a? String) then raise SyntaxError, "#{operation_name} search date string expected as <String> but was <#{search_date_string.class}>." end begin Time.parse(search_date_string) rescue ArgumentError raise SyntaxError, "#{operation_name} search date is invalid: #{search_date_string}" end end
shift_octet_size_value(operation_name, search_key)
click to toggle source
# File lib/rims/protocol/parser.rb, line 625 def shift_octet_size_value(operation_name, search_key) unless (octet_size_string = search_key.shift) then raise SyntaxError, "need for a octet size of #{operation_name}." end unless ((octet_size_string.is_a? String) && (octet_size_string =~ /\A \d+ \z/x)) then raise SyntaxError, "#{operation_name} octet size is expected as numeric string but was <#{octet_size_string}>." end octet_size_string.to_i end
shift_string_value(operation_name, search_key)
click to toggle source
# File lib/rims/protocol/parser.rb, line 597 def shift_string_value(operation_name, search_key) unless (search_string = search_key.shift) then raise SyntaxError, "need for a search string of #{operation_name}." end unless (search_string.is_a? String) then raise SyntaxError, "#{operation_name} search string expected as <String> but was <#{search_string.class}>." end search_string end