Module: InboxBeam::Message
- Defined in:
- lib/inbox_beam/message.rb
Overview
Builds an RFC 5322 message ready to hand to IMAP APPEND. Zero dependencies — UTF-8 safe via base64 bodies and encoded-word headers.
Constant Summary collapse
- CRLF =
"\r\n"
Class Method Summary collapse
- .ascii?(value) ⇒ Boolean
-
.base64_body(content) ⇒ Object
Base64-encode a body and wrap at 76 columns per RFC 2045.
-
.build(from:, to:, subject:, text: nil, html: nil, date: nil) ⇒ String
The raw RFC 5322 message with CRLF line endings.
- .domain_of(address) ⇒ Object
-
.encode_header(value) ⇒ Object
Encode a header that may contain a display name: ‘名前 <addr>`.
-
.encode_word(value) ⇒ Object
RFC 2047 encoded-word for non-ASCII header values.
- .mime_part(content_type, content) ⇒ Object
- .rfc2822_date(time) ⇒ Object
Class Method Details
.ascii?(value) ⇒ Boolean
57 58 59 |
# File 'lib/inbox_beam/message.rb', line 57 def ascii?(value) value.ascii_only? end |
.base64_body(content) ⇒ Object
Base64-encode a body and wrap at 76 columns per RFC 2045.
80 81 82 |
# File 'lib/inbox_beam/message.rb', line 80 def base64_body(content) Base64.strict_encode64(content.encode("UTF-8")).scan(/.{1,76}/).join(CRLF) end |
.build(from:, to:, subject:, text: nil, html: nil, date: nil) ⇒ String
Returns the raw RFC 5322 message with CRLF line endings.
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 |
# File 'lib/inbox_beam/message.rb', line 21 def build(from:, to:, subject:, text: nil, html: nil, date: nil) date ||= Time.now = "<#{SecureRandom.hex(16)}@#{domain_of(from)}>" headers = [ "From: #{encode_header(from)}", "To: #{encode_header(to)}", "Subject: #{encode_word(subject)}", "Date: #{rfc2822_date(date)}", "Message-ID: #{}", "MIME-Version: 1.0" ] if html && text boundary = "inbox_beam_#{SecureRandom.hex(12)}" headers << %(Content-Type: multipart/alternative; boundary="#{boundary}") body = [ "--#{boundary}", mime_part("text/plain", text), "--#{boundary}", mime_part("text/html", html), "--#{boundary}--", "" ].join(CRLF) elsif html headers << "Content-Type: text/html; charset=UTF-8" << "Content-Transfer-Encoding: base64" body = base64_body(html) else headers << "Content-Type: text/plain; charset=UTF-8" << "Content-Transfer-Encoding: base64" body = base64_body(text.to_s) end raw = headers.join(CRLF) + CRLF + CRLF + body raw.gsub(/\r\n|\r|\n/, CRLF) end |
.domain_of(address) ⇒ Object
88 89 90 91 |
# File 'lib/inbox_beam/message.rb', line 88 def domain_of(address) m = address.match(/@([^>\s]+)/) m ? m[1] : "localhost" end |
.encode_header(value) ⇒ Object
Encode a header that may contain a display name: ‘名前 <addr>`.
69 70 71 72 73 74 75 76 77 |
# File 'lib/inbox_beam/message.rb', line 69 def encode_header(value) return value if ascii?(value) if (m = value.match(/\A(.*?)\s*<([^>]+)>\s*\z/)) "#{encode_word(m[1])} <#{m[2]}>" else encode_word(value) end end |
.encode_word(value) ⇒ Object
RFC 2047 encoded-word for non-ASCII header values.
62 63 64 65 66 |
# File 'lib/inbox_beam/message.rb', line 62 def encode_word(value) return value if ascii?(value) "=?UTF-8?B?#{Base64.strict_encode64(value.encode("UTF-8"))}?=" end |
.mime_part(content_type, content) ⇒ Object
93 94 95 96 97 98 99 100 |
# File 'lib/inbox_beam/message.rb', line 93 def mime_part(content_type, content) [ "Content-Type: #{content_type}; charset=UTF-8", "Content-Transfer-Encoding: base64", "", base64_body(content) ].join(CRLF) end |
.rfc2822_date(time) ⇒ Object
84 85 86 |
# File 'lib/inbox_beam/message.rb', line 84 def rfc2822_date(time) time.getutc.strftime("%a, %d %b %Y %H:%M:%S +0000") end |