Module: Gmail::ImapExtensions
- Defined in:
- lib/gmail/imap_extensions.rb
Constant Summary collapse
- LABELS_FLAG_REGEXP =
/\\([^\x80-\xff(){ \x00-\x1f\x7f%"\\]+)/n
Class Method Summary collapse
-
.add_unescape(klass = String) ⇒ Object
PNIRP.
- .patch_net_imap_response_parser(klass = Net::IMAP::ResponseParser) ⇒ Object
Class Method Details
.add_unescape(klass = String) ⇒ Object
PNIRP
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/gmail/imap_extensions.rb', line 120 def self.add_unescape(klass = String) klass.class_eval do # Add a method to string which unescapes special characters # We use a simple state machine to ensure that specials are not # themselves escaped def unescape unesc = '' special = false escapes = { '\\' => '\\', '"' => '"', 'n' => "\n", 't' => "\t", 'r' => "\r", 'f' => "\f", 'v' => "\v", '0' => "\0", 'a' => "\a" } self.each_char do |char| if special # If in special mode, add in the replaced special char if there's a match # Otherwise, add in the backslash and the current character unesc << (escapes.keys.include?(char) ? escapes[char] : "\\#{char}") special = false elsif char == '\\' # Toggle special mode if backslash is detected; otherwise just add character special = true else unesc << char end end unesc end end end |
.patch_net_imap_response_parser(klass = Net::IMAP::ResponseParser) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/gmail/imap_extensions.rb', line 5 def self.patch_net_imap_response_parser(klass = Net::IMAP::ResponseParser) # https://github.com/ruby/ruby/blob/4d426fc2e03078d583d5d573d4863415c3e3eb8d/lib/net/imap.rb#L2258 klass.class_eval do def msg_att(n = -1) match(Net::IMAP::ResponseParser::T_LPAR) attr = {} while true token = lookahead case token.symbol when Net::IMAP::ResponseParser::T_RPAR shift_token break when Net::IMAP::ResponseParser::T_SPACE shift_token next end case token.value when /\A(?:ENVELOPE)\z/ni name, val = envelope_data when /\A(?:FLAGS)\z/ni name, val = flags_data when /\A(?:INTERNALDATE)\z/ni name, val = internaldate_data when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni name, val = rfc822_text when /\A(?:RFC822\.SIZE)\z/ni name, val = rfc822_size when /\A(?:BODY(?:STRUCTURE)?)\z/ni name, val = body_data when /\A(?:UID)\z/ni name, val = uid_data # Gmail extension # Cargo-cult code warning: no idea why the regexp works - just copying a pattern when /\A(?:X-GM-LABELS)\z/ni name, val = x_gm_labels_data when /\A(?:X-GM-MSGID)\z/ni name, val = uid_data when /\A(?:X-GM-THRID)\z/ni name, val = uid_data # End Gmail extension else parse_error("unknown attribute `%s' for {%d}", token.value, n) end attr[name] = val end return attr end # Based on Net::IMAP#flags_data, but calling x_gm_labels_list to parse labels def x_gm_labels_data token = match(self.class::T_ATOM) name = token.value.upcase match(self.class::T_SPACE) return name, x_gm_label_list end # Based on Net::IMAP#flag_list with a modified Regexp # Labels are returned as escape-quoted strings # We extract the labels using a regexp which extracts any unescaped strings def x_gm_label_list if @str.index(/\(([^)]*)\)/ni, @pos) resp = extract_labels_response # We need to manually update the position of the regexp to prevent trip-ups @pos += resp.length - 1 # `resp` will look something like this: # ("\\Inbox" "\\Sent" "one's and two's" "some new label" Awesome Ni&APE-os) result = resp.gsub(/^\s*\(|\)+\s*$/, '').scan(/"([^"]*)"|([^\s"]+)/ni).flatten.compact.collect(&:unescape) result.map do |x| flag = x.scan(LABELS_FLAG_REGEXP) if flag.empty? x else flag.first.first.capitalize.untaint.intern end end else parse_error("invalid label list") end end # The way Gmail return tokens can cause issues with Net::IMAP's reader, # so we need to extract this section manually def extract_labels_response special, quoted = false, false index, paren_count = 0, 0 # Start parsing response string for the labels section, parentheses inclusive labels_header = "X-GM-LABELS (" start = @str.index(labels_header) + labels_header.length - 1 substr = @str[start..-1] substr.each_char do |char| index += 1 case char when '(' paren_count += 1 unless quoted when ')' paren_count -= 1 unless quoted break if paren_count.zero? when '"' quoted = !quoted unless special end special = (char == '\\' && !special) end substr[0..index] end end # class_eval # Add String#unescape add_unescape end |