nano-fast-tool 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/mail-2.9.1/MIT-LICENSE +20 -0
- data/mail-2.9.1/README.md +771 -0
- data/mail-2.9.1/lib/mail/attachments_list.rb +109 -0
- data/mail-2.9.1/lib/mail/body.rb +305 -0
- data/mail-2.9.1/lib/mail/configuration.rb +78 -0
- data/mail-2.9.1/lib/mail/constants.rb +79 -0
- data/mail-2.9.1/lib/mail/elements/address.rb +258 -0
- data/mail-2.9.1/lib/mail/elements/address_list.rb +34 -0
- data/mail-2.9.1/lib/mail/elements/content_disposition_element.rb +20 -0
- data/mail-2.9.1/lib/mail/elements/content_location_element.rb +17 -0
- data/mail-2.9.1/lib/mail/elements/content_transfer_encoding_element.rb +13 -0
- data/mail-2.9.1/lib/mail/elements/content_type_element.rb +25 -0
- data/mail-2.9.1/lib/mail/elements/date_time_element.rb +15 -0
- data/mail-2.9.1/lib/mail/elements/envelope_from_element.rb +39 -0
- data/mail-2.9.1/lib/mail/elements/message_ids_element.rb +31 -0
- data/mail-2.9.1/lib/mail/elements/mime_version_element.rb +15 -0
- data/mail-2.9.1/lib/mail/elements/phrase_list.rb +19 -0
- data/mail-2.9.1/lib/mail/elements/received_element.rb +35 -0
- data/mail-2.9.1/lib/mail/elements.rb +15 -0
- data/mail-2.9.1/lib/mail/encodings/7bit.rb +27 -0
- data/mail-2.9.1/lib/mail/encodings/8bit.rb +18 -0
- data/mail-2.9.1/lib/mail/encodings/base64.rb +38 -0
- data/mail-2.9.1/lib/mail/encodings/binary.rb +13 -0
- data/mail-2.9.1/lib/mail/encodings/identity.rb +24 -0
- data/mail-2.9.1/lib/mail/encodings/quoted_printable.rb +45 -0
- data/mail-2.9.1/lib/mail/encodings/transfer_encoding.rb +77 -0
- data/mail-2.9.1/lib/mail/encodings/unix_to_unix.rb +20 -0
- data/mail-2.9.1/lib/mail/encodings.rb +314 -0
- data/mail-2.9.1/lib/mail/envelope.rb +28 -0
- data/mail-2.9.1/lib/mail/field.rb +313 -0
- data/mail-2.9.1/lib/mail/field_list.rb +87 -0
- data/mail-2.9.1/lib/mail/fields/bcc_field.rb +49 -0
- data/mail-2.9.1/lib/mail/fields/cc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/comments_field.rb +31 -0
- data/mail-2.9.1/lib/mail/fields/common_address_field.rb +162 -0
- data/mail-2.9.1/lib/mail/fields/common_date_field.rb +56 -0
- data/mail-2.9.1/lib/mail/fields/common_field.rb +77 -0
- data/mail-2.9.1/lib/mail/fields/common_message_id_field.rb +41 -0
- data/mail-2.9.1/lib/mail/fields/content_description_field.rb +12 -0
- data/mail-2.9.1/lib/mail/fields/content_disposition_field.rb +44 -0
- data/mail-2.9.1/lib/mail/fields/content_id_field.rb +35 -0
- data/mail-2.9.1/lib/mail/fields/content_location_field.rb +28 -0
- data/mail-2.9.1/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
- data/mail-2.9.1/lib/mail/fields/content_type_field.rb +171 -0
- data/mail-2.9.1/lib/mail/fields/date_field.rb +28 -0
- data/mail-2.9.1/lib/mail/fields/from_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/in_reply_to_field.rb +45 -0
- data/mail-2.9.1/lib/mail/fields/keywords_field.rb +30 -0
- data/mail-2.9.1/lib/mail/fields/message_id_field.rb +36 -0
- data/mail-2.9.1/lib/mail/fields/mime_version_field.rb +42 -0
- data/mail-2.9.1/lib/mail/fields/named_structured_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/named_unstructured_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/optional_field.rb +15 -0
- data/mail-2.9.1/lib/mail/fields/parameter_hash.rb +61 -0
- data/mail-2.9.1/lib/mail/fields/received_field.rb +61 -0
- data/mail-2.9.1/lib/mail/fields/references_field.rb +42 -0
- data/mail-2.9.1/lib/mail/fields/reply_to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_bcc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_cc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_date_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/resent_from_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_message_id_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/resent_sender_field.rb +32 -0
- data/mail-2.9.1/lib/mail/fields/resent_to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/return_path_field.rb +60 -0
- data/mail-2.9.1/lib/mail/fields/sender_field.rb +45 -0
- data/mail-2.9.1/lib/mail/fields/structured_field.rb +24 -0
- data/mail-2.9.1/lib/mail/fields/subject_field.rb +14 -0
- data/mail-2.9.1/lib/mail/fields/to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/unstructured_field.rb +189 -0
- data/mail-2.9.1/lib/mail/fields.rb +45 -0
- data/mail-2.9.1/lib/mail/header.rb +237 -0
- data/mail-2.9.1/lib/mail/indifferent_hash.rb +147 -0
- data/mail-2.9.1/lib/mail/mail.rb +267 -0
- data/mail-2.9.1/lib/mail/matchers/attachment_matchers.rb +44 -0
- data/mail-2.9.1/lib/mail/matchers/has_sent_mail.rb +201 -0
- data/mail-2.9.1/lib/mail/message.rb +2159 -0
- data/mail-2.9.1/lib/mail/multibyte/chars.rb +318 -0
- data/mail-2.9.1/lib/mail/multibyte/unicode.rb +405 -0
- data/mail-2.9.1/lib/mail/multibyte/utils.rb +44 -0
- data/mail-2.9.1/lib/mail/multibyte.rb +82 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/exim.rb +50 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/file_delivery.rb +43 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/sendmail.rb +87 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/smtp.rb +200 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/smtp_connection.rb +57 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/base.rb +64 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/imap.rb +190 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/pop3.rb +143 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/test_retriever.rb +45 -0
- data/mail-2.9.1/lib/mail/network.rb +16 -0
- data/mail-2.9.1/lib/mail/parser_tools.rb +15 -0
- data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rb +33245 -0
- data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rl +183 -0
- data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rb +904 -0
- data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rl +93 -0
- data/mail-2.9.1/lib/mail/parsers/content_location_parser.rb +825 -0
- data/mail-2.9.1/lib/mail/parsers/content_location_parser.rl +82 -0
- data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rb +525 -0
- data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
- data/mail-2.9.1/lib/mail/parsers/content_type_parser.rb +1051 -0
- data/mail-2.9.1/lib/mail/parsers/content_type_parser.rl +94 -0
- data/mail-2.9.1/lib/mail/parsers/date_time_parser.rb +894 -0
- data/mail-2.9.1/lib/mail/parsers/date_time_parser.rl +73 -0
- data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rb +3678 -0
- data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rl +93 -0
- data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rb +5164 -0
- data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rl +97 -0
- data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rb +516 -0
- data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rl +72 -0
- data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rb +887 -0
- data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rl +94 -0
- data/mail-2.9.1/lib/mail/parsers/received_parser.rb +8785 -0
- data/mail-2.9.1/lib/mail/parsers/received_parser.rl +95 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/mail-2.9.1/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322.rl +74 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_date_time.rl +37 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/mail-2.9.1/lib/mail/parsers.rb +13 -0
- data/mail-2.9.1/lib/mail/part.rb +119 -0
- data/mail-2.9.1/lib/mail/parts_list.rb +131 -0
- data/mail-2.9.1/lib/mail/smtp_envelope.rb +57 -0
- data/mail-2.9.1/lib/mail/utilities.rb +576 -0
- data/mail-2.9.1/lib/mail/values/unicode_tables.dat +0 -0
- data/mail-2.9.1/lib/mail/version.rb +17 -0
- data/mail-2.9.1/lib/mail/yaml.rb +30 -0
- data/mail-2.9.1/lib/mail.rb +65 -0
- data/nano-fast-tool.gemspec +12 -0
- metadata +177 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'mail/fields'
|
|
3
|
+
require 'mail/constants'
|
|
4
|
+
|
|
5
|
+
# encoding: utf-8
|
|
6
|
+
module Mail
|
|
7
|
+
# Provides a single class to call to create a new structured or unstructured
|
|
8
|
+
# field. Works out per RFC what field of field it is being given and returns
|
|
9
|
+
# the correct field of class back on new.
|
|
10
|
+
#
|
|
11
|
+
# ===Per RFC 2822
|
|
12
|
+
#
|
|
13
|
+
# 2.2. Header Fields
|
|
14
|
+
#
|
|
15
|
+
# Header fields are lines composed of a field name, followed by a colon
|
|
16
|
+
# (":"), followed by a field body, and terminated by CRLF. A field
|
|
17
|
+
# name MUST be composed of printable US-ASCII characters (i.e.,
|
|
18
|
+
# characters that have values between 33 and 126, inclusive), except
|
|
19
|
+
# colon. A field body may be composed of any US-ASCII characters,
|
|
20
|
+
# except for CR and LF. However, a field body may contain CRLF when
|
|
21
|
+
# used in header "folding" and "unfolding" as described in section
|
|
22
|
+
# 2.2.3. All field bodies MUST conform to the syntax described in
|
|
23
|
+
# sections 3 and 4 of this standard.
|
|
24
|
+
#
|
|
25
|
+
class Field
|
|
26
|
+
include Comparable
|
|
27
|
+
|
|
28
|
+
STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
|
|
29
|
+
content-id content-location content-transfer-encoding
|
|
30
|
+
content-type date from in-reply-to keywords message-id
|
|
31
|
+
mime-version received references reply-to
|
|
32
|
+
resent-bcc resent-cc resent-date resent-from
|
|
33
|
+
resent-message-id resent-sender resent-to
|
|
34
|
+
return-path sender to ]
|
|
35
|
+
|
|
36
|
+
KNOWN_FIELDS = STRUCTURED_FIELDS + ['comments', 'subject']
|
|
37
|
+
|
|
38
|
+
FIELDS_MAP = {
|
|
39
|
+
"to" => "ToField",
|
|
40
|
+
"cc" => "CcField",
|
|
41
|
+
"bcc" => "BccField",
|
|
42
|
+
"message-id" => "MessageIdField",
|
|
43
|
+
"in-reply-to" => "InReplyToField",
|
|
44
|
+
"references" => "ReferencesField",
|
|
45
|
+
"subject" => "SubjectField",
|
|
46
|
+
"comments" => "CommentsField",
|
|
47
|
+
"keywords" => "KeywordsField",
|
|
48
|
+
"date" => "DateField",
|
|
49
|
+
"from" => "FromField",
|
|
50
|
+
"sender" => "SenderField",
|
|
51
|
+
"reply-to" => "ReplyToField",
|
|
52
|
+
"resent-date" => "ResentDateField",
|
|
53
|
+
"resent-from" => "ResentFromField",
|
|
54
|
+
"resent-sender" => "ResentSenderField",
|
|
55
|
+
"resent-to" => "ResentToField",
|
|
56
|
+
"resent-cc" => "ResentCcField",
|
|
57
|
+
"resent-bcc" => "ResentBccField",
|
|
58
|
+
"resent-message-id" => "ResentMessageIdField",
|
|
59
|
+
"return-path" => "ReturnPathField",
|
|
60
|
+
"received" => "ReceivedField",
|
|
61
|
+
"mime-version" => "MimeVersionField",
|
|
62
|
+
"content-transfer-encoding" => "ContentTransferEncodingField",
|
|
63
|
+
"content-description" => "ContentDescriptionField",
|
|
64
|
+
"content-disposition" => "ContentDispositionField",
|
|
65
|
+
"content-type" => "ContentTypeField",
|
|
66
|
+
"content-id" => "ContentIdField",
|
|
67
|
+
"content-location" => "ContentLocationField",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
FIELD_NAME_MAP = {
|
|
71
|
+
"to" => "To",
|
|
72
|
+
"cc" => "Cc",
|
|
73
|
+
"bcc" => "Bcc",
|
|
74
|
+
"message-id" => "Message-ID",
|
|
75
|
+
"in-reply-to" => "In-Reply-To",
|
|
76
|
+
"references" => "References",
|
|
77
|
+
"subject" => "Subject",
|
|
78
|
+
"comments" => "Comments",
|
|
79
|
+
"keywords" => "Keywords",
|
|
80
|
+
"date" => "Date",
|
|
81
|
+
"from" => "From",
|
|
82
|
+
"sender" => "Sender",
|
|
83
|
+
"reply-to" => "Reply-To",
|
|
84
|
+
"resent-date" => "Resent-Date",
|
|
85
|
+
"resent-from" => "Resent-From",
|
|
86
|
+
"resent-sender" => "Resent-Sender",
|
|
87
|
+
"resent-to" => "Resent-To",
|
|
88
|
+
"resent-cc" => "Resent-Cc",
|
|
89
|
+
"resent-bcc" => "Resent-Bcc",
|
|
90
|
+
"resent-message-id" => "Resent-Message-ID",
|
|
91
|
+
"return-path" => "Return-Path",
|
|
92
|
+
"received" => "Received",
|
|
93
|
+
"mime-version" => "MIME-Version",
|
|
94
|
+
"content-transfer-encoding" => "Content-Transfer-Encoding",
|
|
95
|
+
"content-description" => "Content-Description",
|
|
96
|
+
"content-disposition" => "Content-Disposition",
|
|
97
|
+
"content-type" => "Content-Type",
|
|
98
|
+
"content-id" => "Content-ID",
|
|
99
|
+
"content-location" => "Content-Location",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Generic Field Exception
|
|
103
|
+
class FieldError < StandardError
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Raised when a parsing error has occurred (ie, a StructuredField has tried
|
|
107
|
+
# to parse a field that is invalid or improperly written)
|
|
108
|
+
class ParseError < FieldError #:nodoc:
|
|
109
|
+
attr_accessor :element, :value, :reason
|
|
110
|
+
|
|
111
|
+
def initialize(element, value, reason)
|
|
112
|
+
@element = element
|
|
113
|
+
@value = to_utf8(value)
|
|
114
|
+
@reason = to_utf8(reason)
|
|
115
|
+
super("#{@element} can not parse |#{@value}|: #{@reason}")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
def to_utf8(text)
|
|
120
|
+
if text.respond_to?(:force_encoding)
|
|
121
|
+
text.dup.force_encoding(Encoding::UTF_8)
|
|
122
|
+
else
|
|
123
|
+
text
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class NilParseError < ParseError #:nodoc:
|
|
129
|
+
def initialize(element)
|
|
130
|
+
super element, nil, 'nil is invalid'
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class IncompleteParseError < ParseError #:nodoc:
|
|
135
|
+
def initialize(element, original_text, unparsed_index)
|
|
136
|
+
parsed_text = to_utf8(original_text[0...unparsed_index])
|
|
137
|
+
super element, original_text, "Only able to parse up to #{parsed_text.inspect}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Raised when attempting to set a structured field's contents to an invalid syntax
|
|
142
|
+
class SyntaxError < FieldError #:nodoc:
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
class << self
|
|
146
|
+
# Parse a field from a raw header line:
|
|
147
|
+
#
|
|
148
|
+
# Mail::Field.parse("field-name: field data")
|
|
149
|
+
# # => #<Mail::Field …>
|
|
150
|
+
def parse(field, charset = 'utf-8')
|
|
151
|
+
name, value = split(field)
|
|
152
|
+
if name && value
|
|
153
|
+
new name, value, charset
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def split(raw_field) #:nodoc:
|
|
158
|
+
if raw_field.index(Constants::COLON)
|
|
159
|
+
name, value = raw_field.split(Constants::COLON, 2)
|
|
160
|
+
name.rstrip!
|
|
161
|
+
if /\A#{Constants::FIELD_NAME}\z/.match?(name)
|
|
162
|
+
[ name.rstrip, value.strip ]
|
|
163
|
+
else
|
|
164
|
+
Kernel.warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: invalid header name syntax: #{name.inspect}"
|
|
165
|
+
nil
|
|
166
|
+
end
|
|
167
|
+
else
|
|
168
|
+
raw_field.strip
|
|
169
|
+
end
|
|
170
|
+
rescue => error
|
|
171
|
+
warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: #{error.class}: #{error.message}"
|
|
172
|
+
nil
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def field_class_for(name) #:nodoc:
|
|
176
|
+
class_name = FIELDS_MAP[name.to_s.downcase]
|
|
177
|
+
Mail.const_get(class_name) if class_name
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
attr_reader :unparsed_value
|
|
182
|
+
|
|
183
|
+
# Create a field by name and optional value:
|
|
184
|
+
#
|
|
185
|
+
# Mail::Field.new("field-name", "value")
|
|
186
|
+
# # => #<Mail::Field …>
|
|
187
|
+
#
|
|
188
|
+
# Values that aren't strings or arrays are coerced to Strings with `#to_s`.
|
|
189
|
+
#
|
|
190
|
+
# Mail::Field.new("field-name", 1234)
|
|
191
|
+
# # => #<Mail::Field …>
|
|
192
|
+
#
|
|
193
|
+
# Mail::Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
|
|
194
|
+
# # => #<Mail::Field …>
|
|
195
|
+
def initialize(name, value = nil, charset = 'utf-8')
|
|
196
|
+
case
|
|
197
|
+
when name.index(Constants::COLON)
|
|
198
|
+
raise ArgumentError, 'Passing an unparsed header field to Mail::Field.new is not supported in Mail 2.8.0+. Use Mail::Field.parse instead.'
|
|
199
|
+
when Utilities.blank?(value)
|
|
200
|
+
@name = name
|
|
201
|
+
@unparsed_value = nil
|
|
202
|
+
@charset = charset
|
|
203
|
+
else
|
|
204
|
+
@name = name
|
|
205
|
+
@unparsed_value = value
|
|
206
|
+
@charset = charset
|
|
207
|
+
end
|
|
208
|
+
klass = self.class.field_class_for(@name)
|
|
209
|
+
@name = klass ? klass::NAME : @name
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def field=(field)
|
|
213
|
+
@field = field
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def field
|
|
217
|
+
@field ||= create_field(@name, @unparsed_value, @charset)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def name
|
|
221
|
+
@name
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def value
|
|
225
|
+
field.value
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def value=(val)
|
|
229
|
+
@field = create_field(name, val, @charset)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def to_s
|
|
233
|
+
field.to_s
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def inspect
|
|
237
|
+
"#<#{self.class.name} 0x#{(object_id * 2).to_s(16)} #{instance_variables.map do |ivar|
|
|
238
|
+
"#{ivar}=#{instance_variable_get(ivar).inspect}"
|
|
239
|
+
end.join(" ")}>"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def same(other)
|
|
243
|
+
other.kind_of?(self.class) && Utilities.match_to_s(other.name, name)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def ==(other)
|
|
247
|
+
same(other) && Utilities.match_to_s(other.value, value)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def responsible_for?(field_name)
|
|
251
|
+
name.to_s.casecmp(field_name.to_s) == 0
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def <=>(other)
|
|
255
|
+
field_order_id <=> other.field_order_id
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def field_order_id
|
|
259
|
+
@field_order_id ||= FIELD_ORDER_LOOKUP.fetch(self.name.to_s.downcase, 100)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def method_missing(name, *args, &block)
|
|
263
|
+
field.send(name, *args, &block)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def respond_to_missing?(method_name, include_private)
|
|
267
|
+
field.respond_to?(method_name, include_private) || super
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
FIELD_ORDER_LOOKUP = Hash[%w[
|
|
271
|
+
return-path received
|
|
272
|
+
resent-date resent-from resent-sender resent-to
|
|
273
|
+
resent-cc resent-bcc resent-message-id
|
|
274
|
+
date from sender reply-to to cc bcc
|
|
275
|
+
message-id in-reply-to references
|
|
276
|
+
subject comments keywords
|
|
277
|
+
mime-version content-type content-transfer-encoding
|
|
278
|
+
content-location content-disposition content-description
|
|
279
|
+
].each_with_index.to_a]
|
|
280
|
+
|
|
281
|
+
private
|
|
282
|
+
|
|
283
|
+
def create_field(name, value, charset)
|
|
284
|
+
parse_field(name, value, charset)
|
|
285
|
+
rescue Mail::Field::ParseError => e
|
|
286
|
+
field = Mail::UnstructuredField.new(name, value)
|
|
287
|
+
field.errors << [name, value, e]
|
|
288
|
+
field
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def parse_field(name, value, charset)
|
|
292
|
+
value = unfold(value) if value.is_a?(String)
|
|
293
|
+
|
|
294
|
+
if klass = self.class.field_class_for(name)
|
|
295
|
+
klass.parse(value, charset)
|
|
296
|
+
else
|
|
297
|
+
OptionalField.parse(name, value, charset)
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# 2.2.3. Long Header Fields
|
|
302
|
+
#
|
|
303
|
+
# The process of moving from this folded multiple-line representation
|
|
304
|
+
# of a header field to its single line representation is called
|
|
305
|
+
# "unfolding". Unfolding is accomplished by simply removing any CRLF
|
|
306
|
+
# that is immediately followed by WSP. Each header field should be
|
|
307
|
+
# treated in its unfolded form for further syntactic and semantic
|
|
308
|
+
# evaluation.
|
|
309
|
+
def unfold(string)
|
|
310
|
+
string.gsub(Constants::UNFOLD_WS, '\1')
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
module Mail
|
|
4
|
+
|
|
5
|
+
# Field List class provides an enhanced array that keeps a list of
|
|
6
|
+
# email fields in order. And allows you to insert new fields without
|
|
7
|
+
# having to worry about the order they will appear in.
|
|
8
|
+
class FieldList < Array
|
|
9
|
+
def has_field?(field_name)
|
|
10
|
+
any? { |f| f.responsible_for? field_name }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_field(field_name)
|
|
14
|
+
fields = select_fields(field_name)
|
|
15
|
+
case fields.size
|
|
16
|
+
when 0; nil
|
|
17
|
+
when 1; fields.first
|
|
18
|
+
else fields
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_field(field)
|
|
23
|
+
if field.singular?
|
|
24
|
+
replace_field field
|
|
25
|
+
else
|
|
26
|
+
insert_field field
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
alias_method :<<, :add_field
|
|
30
|
+
|
|
31
|
+
def replace_field(field)
|
|
32
|
+
if first_offset = index { |f| f.responsible_for? field.name }
|
|
33
|
+
delete_field field.name
|
|
34
|
+
insert first_offset, field
|
|
35
|
+
else
|
|
36
|
+
insert_field field
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Insert the field in sorted order.
|
|
41
|
+
#
|
|
42
|
+
# Heavily based on bisect.insort from Python, which is:
|
|
43
|
+
# Copyright (C) 2001-2013 Python Software Foundation.
|
|
44
|
+
# Licensed under <http://docs.python.org/license.html>
|
|
45
|
+
# From <http://hg.python.org/cpython/file/2.7/Lib/bisect.py>
|
|
46
|
+
def insert_field(field)
|
|
47
|
+
lo, hi = 0, size
|
|
48
|
+
while lo < hi
|
|
49
|
+
mid = (lo + hi).div(2)
|
|
50
|
+
if field < self[mid]
|
|
51
|
+
hi = mid
|
|
52
|
+
else
|
|
53
|
+
lo = mid + 1
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
insert lo, field
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def delete_field(name)
|
|
61
|
+
delete_if { |f| f.responsible_for? name }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def summary
|
|
65
|
+
map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def select_fields(field_name)
|
|
71
|
+
fields = select { |f| f.responsible_for? field_name }
|
|
72
|
+
if fields.size > 1 && singular?(field_name)
|
|
73
|
+
Array(fields.detect { |f| f.errors.size == 0 } || fields.first)
|
|
74
|
+
else
|
|
75
|
+
fields
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def singular?(field_name)
|
|
80
|
+
if klass = Mail::Field.field_class_for(field_name)
|
|
81
|
+
klass.singular?
|
|
82
|
+
else
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
# = Blind Carbon Copy Field
|
|
6
|
+
#
|
|
7
|
+
# The Bcc field inherits from StructuredField and handles the Bcc: header
|
|
8
|
+
# field in the email.
|
|
9
|
+
#
|
|
10
|
+
# Sending bcc to a mail message will instantiate a Mail::Field object that
|
|
11
|
+
# has a BccField as its field type. This includes all Mail::CommonAddress
|
|
12
|
+
# module instance metods.
|
|
13
|
+
#
|
|
14
|
+
# Only one Bcc field can appear in a header, though it can have multiple
|
|
15
|
+
# addresses and groups of addresses.
|
|
16
|
+
#
|
|
17
|
+
# == Examples:
|
|
18
|
+
#
|
|
19
|
+
# mail = Mail.new
|
|
20
|
+
# mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
|
21
|
+
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
|
22
|
+
# mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
|
|
23
|
+
# mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
|
|
24
|
+
# mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
|
|
25
|
+
#
|
|
26
|
+
# mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
|
|
27
|
+
# mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
|
28
|
+
# mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
|
29
|
+
# mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
|
|
30
|
+
class BccField < CommonAddressField #:nodoc:
|
|
31
|
+
NAME = 'Bcc'
|
|
32
|
+
|
|
33
|
+
attr_accessor :include_in_headers
|
|
34
|
+
|
|
35
|
+
def initialize(value = nil, charset = nil)
|
|
36
|
+
super
|
|
37
|
+
self.include_in_headers = false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Bcc field should not be :encoded by default
|
|
41
|
+
def encoded
|
|
42
|
+
if include_in_headers
|
|
43
|
+
super
|
|
44
|
+
else
|
|
45
|
+
''
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
# = Carbon Copy Field
|
|
6
|
+
#
|
|
7
|
+
# The Cc field inherits from StructuredField and handles the Cc: header
|
|
8
|
+
# field in the email.
|
|
9
|
+
#
|
|
10
|
+
# Sending cc to a mail message will instantiate a Mail::Field object that
|
|
11
|
+
# has a CcField as its field type. This includes all Mail::CommonAddress
|
|
12
|
+
# module instance metods.
|
|
13
|
+
#
|
|
14
|
+
# Only one Cc field can appear in a header, though it can have multiple
|
|
15
|
+
# addresses and groups of addresses.
|
|
16
|
+
#
|
|
17
|
+
# == Examples:
|
|
18
|
+
#
|
|
19
|
+
# mail = Mail.new
|
|
20
|
+
# mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
|
21
|
+
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
|
22
|
+
# mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
|
|
23
|
+
# mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
|
|
24
|
+
# mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
|
|
25
|
+
#
|
|
26
|
+
# mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
|
|
27
|
+
# mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
|
28
|
+
# mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
|
29
|
+
# mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
|
|
30
|
+
class CcField < CommonAddressField #:nodoc:
|
|
31
|
+
NAME = 'Cc'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
# = Comments Field
|
|
6
|
+
#
|
|
7
|
+
# The Comments field inherits from UnstructuredField and handles the Comments:
|
|
8
|
+
# header field in the email.
|
|
9
|
+
#
|
|
10
|
+
# Sending comments to a mail message will instantiate a Mail::Field object that
|
|
11
|
+
# has a CommentsField as its field type.
|
|
12
|
+
#
|
|
13
|
+
# An email header can have as many comments fields as it wants. There is no upper
|
|
14
|
+
# limit, the comments field is also optional (that is, no comment is needed)
|
|
15
|
+
#
|
|
16
|
+
# == Examples:
|
|
17
|
+
#
|
|
18
|
+
# mail = Mail.new
|
|
19
|
+
# mail.comments = 'This is a comment'
|
|
20
|
+
# mail.comments #=> 'This is a comment'
|
|
21
|
+
# mail[:comments] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
|
|
22
|
+
# mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
|
|
23
|
+
# mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
|
|
24
|
+
#
|
|
25
|
+
# mail.comments = "This is another comment"
|
|
26
|
+
# mail[:comments].map { |c| c.to_s }
|
|
27
|
+
# #=> ['This is a comment', "This is another comment"]
|
|
28
|
+
class CommentsField < NamedUnstructuredField #:nodoc:
|
|
29
|
+
NAME = 'Comments'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
class AddressContainer < Array #:nodoc:
|
|
6
|
+
def initialize(field, list = nil)
|
|
7
|
+
@field = field
|
|
8
|
+
super list if list
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def <<(address)
|
|
12
|
+
@field << address
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class CommonAddressField < NamedStructuredField #:nodoc:
|
|
17
|
+
def self.singular?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(value = nil, charset = nil)
|
|
22
|
+
super encode_if_needed(value, charset), charset
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def element # :nodoc:
|
|
26
|
+
@element ||= AddressList.new(value)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Allows you to iterate through each address object in the address_list
|
|
30
|
+
def each
|
|
31
|
+
element.addresses.each do |address|
|
|
32
|
+
yield(address)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def default
|
|
37
|
+
addresses
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def address
|
|
41
|
+
addresses.first
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the address string of all the addresses in the address list
|
|
45
|
+
def addresses
|
|
46
|
+
list = element.addresses.map { |a| a.address }
|
|
47
|
+
Mail::AddressContainer.new(self, list)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns the formatted string of all the addresses in the address list
|
|
51
|
+
def formatted
|
|
52
|
+
list = element.addresses.map { |a| a.format }
|
|
53
|
+
Mail::AddressContainer.new(self, list)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Returns the display name of all the addresses in the address list
|
|
57
|
+
def display_names
|
|
58
|
+
list = element.addresses.map { |a| a.display_name }
|
|
59
|
+
Mail::AddressContainer.new(self, list)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns the actual address objects in the address list
|
|
63
|
+
def addrs
|
|
64
|
+
list = element.addresses
|
|
65
|
+
Mail::AddressContainer.new(self, list)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns a hash of group name => address strings for the address list
|
|
69
|
+
def groups
|
|
70
|
+
element.addresses_grouped_by_group
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Returns the addresses that are part of groups
|
|
74
|
+
def group_addresses
|
|
75
|
+
decoded_group_addresses
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns a list of decoded group addresses
|
|
79
|
+
def decoded_group_addresses
|
|
80
|
+
groups.map { |k,v| v.map { |a| a.decoded } }.flatten
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns a list of encoded group addresses
|
|
84
|
+
def encoded_group_addresses
|
|
85
|
+
groups.map { |k,v| v.map { |a| a.encoded } }.flatten
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Returns the name of all the groups in a string
|
|
89
|
+
def group_names # :nodoc:
|
|
90
|
+
element.group_names
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def <<(val)
|
|
94
|
+
case
|
|
95
|
+
when val.nil?
|
|
96
|
+
raise ArgumentError, "Need to pass an address to <<"
|
|
97
|
+
when Utilities.blank?(val)
|
|
98
|
+
self
|
|
99
|
+
else
|
|
100
|
+
self.value = [self.value, encode_if_needed(val)].reject { |a| Utilities.blank?(a) }.join(", ")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def encode_if_needed(val, val_charset = charset) #:nodoc:
|
|
105
|
+
case val
|
|
106
|
+
when nil
|
|
107
|
+
val
|
|
108
|
+
|
|
109
|
+
# Need to join arrays of addresses into a single value
|
|
110
|
+
when Array
|
|
111
|
+
val.compact.map { |a| encode_if_needed a, val_charset }.join(', ')
|
|
112
|
+
|
|
113
|
+
# Pass through UTF-8; encode non-UTF-8.
|
|
114
|
+
else
|
|
115
|
+
utf8_if_needed(val, val_charset) || Encodings.encode_non_usascii(val, val_charset)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
# Pass through UTF-8 addresses
|
|
122
|
+
def utf8_if_needed(val, val_charset)
|
|
123
|
+
if /\AUTF-?8\z/i.match?(val_charset)
|
|
124
|
+
val
|
|
125
|
+
elsif val.encoding == Encoding::UTF_8
|
|
126
|
+
val
|
|
127
|
+
elsif (utf8 = val.dup.force_encoding(Encoding::UTF_8)).valid_encoding?
|
|
128
|
+
utf8
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def do_encode
|
|
133
|
+
return '' if Utilities.blank?(value)
|
|
134
|
+
address_array = element.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
|
|
135
|
+
address_text = address_array.join(", \r\n\s")
|
|
136
|
+
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
|
|
137
|
+
group_text = group_array.join(" \r\n\s")
|
|
138
|
+
return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
|
|
139
|
+
"#{name}: #{return_array.join(", \r\n\s")}\r\n"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def do_decode
|
|
143
|
+
return nil if Utilities.blank?(value)
|
|
144
|
+
address_array = element.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
|
|
145
|
+
address_text = address_array.join(", ")
|
|
146
|
+
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
|
|
147
|
+
group_text = group_array.join(" ")
|
|
148
|
+
return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
|
|
149
|
+
return_array.join(", ")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def get_group_addresses(group_list)
|
|
153
|
+
if group_list.respond_to?(:addresses)
|
|
154
|
+
group_list.addresses.map do |address|
|
|
155
|
+
Mail::Address.new(address)
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
[]
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|