Class: Astrotrain::Message
- Inherits:
-
Object
- Object
- Astrotrain::Message
- Defined in:
- lib/astrotrain/message.rb
Overview
Wrapper around a TMail object
Constant Summary collapse
- EMAIL_REGEX =
/[\w\.\_\%\+\-]+[^\s\.]@[\w\-\_\.]+/
Class Attribute Summary collapse
-
.recipient_header_order ⇒ Object
Returns the value of attribute recipient_header_order.
-
.skipped_headers ⇒ Object
Returns the value of attribute skipped_headers.
Instance Attribute Summary collapse
-
#mail ⇒ Object
readonly
Reference to the internal Mail object that parsed the raw email.
-
#path ⇒ Object
readonly
Refebrence to the original file that this Mail came from.
Class Method Summary collapse
-
.parse(raw) ⇒ Object
Public: Parses the raw email headers into a Astrotrain::Message instance.
-
.read(path) ⇒ Object
Public: Parses the raw email headers into a Astrotrain::Message instance.
-
.unescape(s) ⇒ Object
Stolen from Rack/Camping, remove the “+” => “ ” translation.
Instance Method Summary collapse
-
#address_list_for(emails, retried = false) ⇒ Object
Uses Mail::AddressList to parse the given comma separated emails.
-
#attachments ⇒ Object
Public: Gets the attachments in the email.
-
#body ⇒ Object
Public: Gets the plain/text body of the email.
-
#cc ⇒ Object
Public: Unquotes and converts the Cc header to UTF-8.
-
#convert_to_utf8(s) ⇒ Object
Converts a given String to UTF-8.
-
#from ⇒ Object
(also: #sender)
Public: Unquotes and converts the From header to UTF-8.
-
#headers ⇒ Object
Public: Builds a hash of headers, skipping the keys specified in #skipped_headers.
-
#html ⇒ Object
Public: Gets the html body of the email.
-
#initialize(mail, path = nil) ⇒ Message
constructor
A new instance of Message.
-
#message_id ⇒ Object
Public: Gets the unique message-id for the email, with the surrounding ‘<` and `>` parsed out.
-
#process_message_body ⇒ Object
Parses the mail’s parts, assembling the plain/HTML Strings, as well as any attachments.
-
#recipients(order = nil) ⇒ Object
Public: Gets the recipients of an email using the To/Delivered-To/X-Original-To headers.
-
#recipients_from_body ⇒ Object
Parses out all email addresses from the body of the email.
-
#recipients_from_delivered_to ⇒ Object
Parses the ‘Delivered-To’ header for email address.
-
#recipients_from_original_to ⇒ Object
Parses the ‘X-Original-To’ header for email address.
-
#recipients_from_to ⇒ Object
Parses the ‘To’ header for email address.
-
#scan_parts(message) ⇒ Object
Recursive method to scan all the parts of the given part.
-
#subject ⇒ Object
Public: Unquotes and converts the Subject header to UTF-8.
-
#to ⇒ Object
Public: Unquotes and converts the To header to UTF-8.
-
#unquoted_address_header(key) ⇒ Object
Parses the given header for email addresses.
-
#unquoted_header(key) ⇒ Object
Parses the quoted header values: ‘=?…?=`.
Constructor Details
#initialize(mail, path = nil) ⇒ Message
Returns a new instance of Message.
48 49 50 51 52 53 |
# File 'lib/astrotrain/message.rb', line 48 def initialize(mail, path = nil) @body = @html = = nil @path = path @mail = mail @recipients = {} end |
Class Attribute Details
.recipient_header_order ⇒ Object
Returns the value of attribute recipient_header_order.
18 19 20 |
# File 'lib/astrotrain/message.rb', line 18 def recipient_header_order @recipient_header_order end |
.skipped_headers ⇒ Object
Returns the value of attribute skipped_headers.
18 19 20 |
# File 'lib/astrotrain/message.rb', line 18 def skipped_headers @skipped_headers end |
Instance Attribute Details
#mail ⇒ Object (readonly)
Reference to the internal Mail object that parsed the raw email.
12 13 14 |
# File 'lib/astrotrain/message.rb', line 12 def mail @mail end |
#path ⇒ Object (readonly)
Refebrence to the original file that this Mail came from.
15 16 17 |
# File 'lib/astrotrain/message.rb', line 15 def path @path end |
Class Method Details
.parse(raw) ⇒ Object
Public: Parses the raw email headers into a Astrotrain::Message instance.
raw - String of the email content
Returns Astrotrain::Message instance.
44 45 46 |
# File 'lib/astrotrain/message.rb', line 44 def self.parse(raw) new(::Mail.new(raw)) end |
.read(path) ⇒ Object
Public: Parses the raw email headers into a Astrotrain::Message instance.
path - String path to the file.
Returns Astrotrain::Message instance.
35 36 37 |
# File 'lib/astrotrain/message.rb', line 35 def self.read(path) new(::Mail.read(path), path) end |
.unescape(s) ⇒ Object
Stolen from Rack/Camping, remove the “+” => “ ” translation
249 250 251 252 253 254 255 256 |
# File 'lib/astrotrain/message.rb', line 249 def self.unescape(s) s.gsub!(/((?:%[0-9a-fA-F]{2})+)/n) do original = $1.dup replacement = [$1.delete('%')].pack('H*') replacement.as_utf8.valid? ? replacement : original end s end |
Instance Method Details
#address_list_for(emails, retried = false) ⇒ Object
Uses Mail::AddressList to parse the given comma separated emails.
emails - Array of String email headers.
Example: ["[email protected], Bar <[email protected]>", ...]
Returns Array of Mail::Address objects
234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/astrotrain/message.rb', line 234 def address_list_for(emails, retried = false) emails = emails * ", " list = Mail::AddressList.new(self.class.unescape(emails)) addrs = list.addresses.each { |a| a.decoded } addrs.uniq! addrs rescue Mail::Field::ParseError if retried raise else address_list_for(emails.scan(EMAIL_REGEX), true) end end |
#attachments ⇒ Object
Public: Gets the attachments in the email.
Returns Array of Astrotrain::Attachment objects.
136 137 138 139 |
# File 'lib/astrotrain/message.rb', line 136 def if ! end |
#body ⇒ Object
Public: Gets the plain/text body of the email.
Returns String
120 121 122 123 |
# File 'lib/astrotrain/message.rb', line 120 def body if !@body @body end |
#cc ⇒ Object
Public: Unquotes and converts the Cc header to UTF-8.
Returns Array of Mail::Address objects
98 99 100 |
# File 'lib/astrotrain/message.rb', line 98 def cc @cc ||= unquoted_address_header(:cc) end |
#convert_to_utf8(s) ⇒ Object
Converts a given String to UTF-8. If the message has no charset assigned, we’ll attempt to detect it then convert it to UTF-8.
s - unconverted String in the wrong character set
Returns converted String.
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/astrotrain/message.rb', line 311 def convert_to_utf8(s) # If this string is already valid UTF-8 just hand it back if (!@mail.charset || @mail.charset == 'UTF-8') && s.as_utf8.valid? s.force_encoding("UTF-8") if s.respond_to?(:force_encoding) return s end # First lets try to detect the encoding if the message didn't specify if !@mail.charset && detection = CharlockHolmes::EncodingDetector.detect(s) @mail.charset = detection[:encoding] end # if the encoding was already set or we just detected it AND it's not already # set to UTF-8 - try to transcode the body into UTF-8 if @mail.charset && @mail.charset != 'UTF-8' if s.size > 0 s = CharlockHolmes::Converter.convert s, @mail.charset, 'UTF-8' else s = "" end end # By the time we get here, `s` is either UTF-8 or we need to force it to be # But, even if it's UTF-8 we could be in the case where the charset on the # message was set to UTF-8 but is in fact invalid. # So for either case, we want to make sure the output is valid UTF-8 - even # if it means mutating the invalid string. # Also we're not reusing the String::UTF8 version of `s` from above here # because by this point, it may be a new string. s.as_utf8.clean.as_raw end |
#from ⇒ Object Also known as: sender
Public: Unquotes and converts the From header to UTF-8.
Returns Array of Mail::Address objects
83 84 85 |
# File 'lib/astrotrain/message.rb', line 83 def from @from ||= unquoted_address_header(:from) end |
#headers ⇒ Object
Public: Builds a hash of headers, skipping the keys specified in #skipped_headers. If header values cannot be parsed, the original raw value is provided.
Returns Hash of the headers with String keys and values.
146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/astrotrain/message.rb', line 146 def headers @headers ||= begin @mail.header.fields.inject({}) do |memo, field| name = field.name.downcase.to_s next memo if self.class.skipped_headers.include?(name) header = unquoted_header(name) memo.update(name => self.class.unescape(header)) end end end |
#html ⇒ Object
Public: Gets the html body of the email.
Returns String
128 129 130 131 |
# File 'lib/astrotrain/message.rb', line 128 def html if !@html @html end |
#message_id ⇒ Object
Public: Gets the unique message-id for the email, with the surrounding ‘<` and `>` parsed out.
Returns String
113 114 115 |
# File 'lib/astrotrain/message.rb', line 113 def @mail. end |
#process_message_body ⇒ Object
Parses the mail’s parts, assembling the plain/HTML Strings, as well as any attachments.
Returns nothing.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/astrotrain/message.rb', line 262 def = [] if @mail.multipart? @body, @html = [], [] scan_parts(@mail) @body = @body.join("\n") @html = @html.join("\n") else if @mail.content_type =~ /text\/html/ @html = @mail.body.to_s @body = '' else @body = @mail.body.to_s @html = '' end end @body = convert_to_utf8(@body) @html = convert_to_utf8(@html) end |
#recipients(order = nil) ⇒ Object
Public: Gets the recipients of an email using the To/Delivered-To/X-Original-To headers. It’s not always straightforward which email we want when dealing with filters and forward rules.
order - Array of email header names that specifies the order that the
list of recipient emails is assembled. Valid strings are:
'original_to', 'delivered_to', and 'to'.
Returns Array of possible recipients.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/astrotrain/message.rb', line 64 def recipients(order = nil) if !@recipients.key?(order) order = Array(order) order = self.class.recipient_header_order if order.empty? recipients = [] emails = order.inject([]) do |memo, key| memo.push *send("recipients_from_#{key}") end @recipients[order] = emails.map! { |em| em.address } @recipients[order].uniq! end @recipients[order] end |
#recipients_from_body ⇒ Object
Parses out all email addresses from the body of the email.
Returns Array of Mail::Address objects
184 185 186 187 188 189 190 191 |
# File 'lib/astrotrain/message.rb', line 184 def recipients_from_body @recipients_from_body ||= begin emails_from_body = body.scan(EMAIL_REGEX) address_list_for(emails_from_body, true) rescue Mail::Field::ParseError [] end end |
#recipients_from_delivered_to ⇒ Object
Parses the ‘Delivered-To’ header for email address.
Returns Array of Mail::Address objects
170 171 172 |
# File 'lib/astrotrain/message.rb', line 170 def recipients_from_delivered_to @recipients_from_delivered_to ||= unquoted_address_header('delivered-to') end |
#recipients_from_original_to ⇒ Object
Parses the ‘X-Original-To’ header for email address.
Returns Array of Mail::Address objects
177 178 179 |
# File 'lib/astrotrain/message.rb', line 177 def recipients_from_original_to @recipients_from_original_to ||= unquoted_address_header('x-original-to') end |
#recipients_from_to ⇒ Object
Parses the ‘To’ header for email address.
Returns Array of Mail::Address objects
163 164 165 |
# File 'lib/astrotrain/message.rb', line 163 def recipients_from_to to end |
#scan_parts(message) ⇒ Object
Recursive method to scan all the parts of the given part.
Returns nothing.
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/astrotrain/message.rb', line 286 def scan_parts() .parts.each do |part| if part.multipart? scan_parts(part) else case part.content_type when /text\/plain/ @body << part.body.to_s when /text\/html/ @html << part.body.to_s else att = Attachment.new(part) << att if att.attached? end end end end |
#subject ⇒ Object
Public: Unquotes and converts the Subject header to UTF-8.
Returns String
105 106 107 |
# File 'lib/astrotrain/message.rb', line 105 def subject @mail.subject end |
#to ⇒ Object
Public: Unquotes and converts the To header to UTF-8.
Returns Array of Mail::Address objects
91 92 93 |
# File 'lib/astrotrain/message.rb', line 91 def to @to ||= unquoted_address_header(:to) end |
#unquoted_address_header(key) ⇒ Object
Parses the given header for email addresses. Handles the case where some keys return arrays if there are multiple values.
key - String or Symbol header name
Returns Array of Mail::Address objects
215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/astrotrain/message.rb', line 215 def unquoted_address_header(key) if header = @mail[key] emails = if header.respond_to?(:value) [header.value] else header.map { |h| h.value } end address_list_for(emails) else [] end end |
#unquoted_header(key) ⇒ Object
Parses the quoted header values: ‘=?…?=`.
key - String or Symbol header name
Returns unquoted String.
198 199 200 201 202 203 204 205 206 207 |
# File 'lib/astrotrain/message.rb', line 198 def unquoted_header(key) if header = @mail[key] value = header.respond_to?(:map) ? header.map { |h| h.value }.join("\n") : header.value Mail::Encodings.value_decode(value) else '' end end |