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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/mail-2.9.1/MIT-LICENSE +20 -0
  3. data/mail-2.9.1/README.md +771 -0
  4. data/mail-2.9.1/lib/mail/attachments_list.rb +109 -0
  5. data/mail-2.9.1/lib/mail/body.rb +305 -0
  6. data/mail-2.9.1/lib/mail/configuration.rb +78 -0
  7. data/mail-2.9.1/lib/mail/constants.rb +79 -0
  8. data/mail-2.9.1/lib/mail/elements/address.rb +258 -0
  9. data/mail-2.9.1/lib/mail/elements/address_list.rb +34 -0
  10. data/mail-2.9.1/lib/mail/elements/content_disposition_element.rb +20 -0
  11. data/mail-2.9.1/lib/mail/elements/content_location_element.rb +17 -0
  12. data/mail-2.9.1/lib/mail/elements/content_transfer_encoding_element.rb +13 -0
  13. data/mail-2.9.1/lib/mail/elements/content_type_element.rb +25 -0
  14. data/mail-2.9.1/lib/mail/elements/date_time_element.rb +15 -0
  15. data/mail-2.9.1/lib/mail/elements/envelope_from_element.rb +39 -0
  16. data/mail-2.9.1/lib/mail/elements/message_ids_element.rb +31 -0
  17. data/mail-2.9.1/lib/mail/elements/mime_version_element.rb +15 -0
  18. data/mail-2.9.1/lib/mail/elements/phrase_list.rb +19 -0
  19. data/mail-2.9.1/lib/mail/elements/received_element.rb +35 -0
  20. data/mail-2.9.1/lib/mail/elements.rb +15 -0
  21. data/mail-2.9.1/lib/mail/encodings/7bit.rb +27 -0
  22. data/mail-2.9.1/lib/mail/encodings/8bit.rb +18 -0
  23. data/mail-2.9.1/lib/mail/encodings/base64.rb +38 -0
  24. data/mail-2.9.1/lib/mail/encodings/binary.rb +13 -0
  25. data/mail-2.9.1/lib/mail/encodings/identity.rb +24 -0
  26. data/mail-2.9.1/lib/mail/encodings/quoted_printable.rb +45 -0
  27. data/mail-2.9.1/lib/mail/encodings/transfer_encoding.rb +77 -0
  28. data/mail-2.9.1/lib/mail/encodings/unix_to_unix.rb +20 -0
  29. data/mail-2.9.1/lib/mail/encodings.rb +314 -0
  30. data/mail-2.9.1/lib/mail/envelope.rb +28 -0
  31. data/mail-2.9.1/lib/mail/field.rb +313 -0
  32. data/mail-2.9.1/lib/mail/field_list.rb +87 -0
  33. data/mail-2.9.1/lib/mail/fields/bcc_field.rb +49 -0
  34. data/mail-2.9.1/lib/mail/fields/cc_field.rb +33 -0
  35. data/mail-2.9.1/lib/mail/fields/comments_field.rb +31 -0
  36. data/mail-2.9.1/lib/mail/fields/common_address_field.rb +162 -0
  37. data/mail-2.9.1/lib/mail/fields/common_date_field.rb +56 -0
  38. data/mail-2.9.1/lib/mail/fields/common_field.rb +77 -0
  39. data/mail-2.9.1/lib/mail/fields/common_message_id_field.rb +41 -0
  40. data/mail-2.9.1/lib/mail/fields/content_description_field.rb +12 -0
  41. data/mail-2.9.1/lib/mail/fields/content_disposition_field.rb +44 -0
  42. data/mail-2.9.1/lib/mail/fields/content_id_field.rb +35 -0
  43. data/mail-2.9.1/lib/mail/fields/content_location_field.rb +28 -0
  44. data/mail-2.9.1/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
  45. data/mail-2.9.1/lib/mail/fields/content_type_field.rb +171 -0
  46. data/mail-2.9.1/lib/mail/fields/date_field.rb +28 -0
  47. data/mail-2.9.1/lib/mail/fields/from_field.rb +33 -0
  48. data/mail-2.9.1/lib/mail/fields/in_reply_to_field.rb +45 -0
  49. data/mail-2.9.1/lib/mail/fields/keywords_field.rb +30 -0
  50. data/mail-2.9.1/lib/mail/fields/message_id_field.rb +36 -0
  51. data/mail-2.9.1/lib/mail/fields/mime_version_field.rb +42 -0
  52. data/mail-2.9.1/lib/mail/fields/named_structured_field.rb +10 -0
  53. data/mail-2.9.1/lib/mail/fields/named_unstructured_field.rb +10 -0
  54. data/mail-2.9.1/lib/mail/fields/optional_field.rb +15 -0
  55. data/mail-2.9.1/lib/mail/fields/parameter_hash.rb +61 -0
  56. data/mail-2.9.1/lib/mail/fields/received_field.rb +61 -0
  57. data/mail-2.9.1/lib/mail/fields/references_field.rb +42 -0
  58. data/mail-2.9.1/lib/mail/fields/reply_to_field.rb +33 -0
  59. data/mail-2.9.1/lib/mail/fields/resent_bcc_field.rb +33 -0
  60. data/mail-2.9.1/lib/mail/fields/resent_cc_field.rb +33 -0
  61. data/mail-2.9.1/lib/mail/fields/resent_date_field.rb +10 -0
  62. data/mail-2.9.1/lib/mail/fields/resent_from_field.rb +33 -0
  63. data/mail-2.9.1/lib/mail/fields/resent_message_id_field.rb +10 -0
  64. data/mail-2.9.1/lib/mail/fields/resent_sender_field.rb +32 -0
  65. data/mail-2.9.1/lib/mail/fields/resent_to_field.rb +33 -0
  66. data/mail-2.9.1/lib/mail/fields/return_path_field.rb +60 -0
  67. data/mail-2.9.1/lib/mail/fields/sender_field.rb +45 -0
  68. data/mail-2.9.1/lib/mail/fields/structured_field.rb +24 -0
  69. data/mail-2.9.1/lib/mail/fields/subject_field.rb +14 -0
  70. data/mail-2.9.1/lib/mail/fields/to_field.rb +33 -0
  71. data/mail-2.9.1/lib/mail/fields/unstructured_field.rb +189 -0
  72. data/mail-2.9.1/lib/mail/fields.rb +45 -0
  73. data/mail-2.9.1/lib/mail/header.rb +237 -0
  74. data/mail-2.9.1/lib/mail/indifferent_hash.rb +147 -0
  75. data/mail-2.9.1/lib/mail/mail.rb +267 -0
  76. data/mail-2.9.1/lib/mail/matchers/attachment_matchers.rb +44 -0
  77. data/mail-2.9.1/lib/mail/matchers/has_sent_mail.rb +201 -0
  78. data/mail-2.9.1/lib/mail/message.rb +2159 -0
  79. data/mail-2.9.1/lib/mail/multibyte/chars.rb +318 -0
  80. data/mail-2.9.1/lib/mail/multibyte/unicode.rb +405 -0
  81. data/mail-2.9.1/lib/mail/multibyte/utils.rb +44 -0
  82. data/mail-2.9.1/lib/mail/multibyte.rb +82 -0
  83. data/mail-2.9.1/lib/mail/network/delivery_methods/exim.rb +50 -0
  84. data/mail-2.9.1/lib/mail/network/delivery_methods/file_delivery.rb +43 -0
  85. data/mail-2.9.1/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  86. data/mail-2.9.1/lib/mail/network/delivery_methods/sendmail.rb +87 -0
  87. data/mail-2.9.1/lib/mail/network/delivery_methods/smtp.rb +200 -0
  88. data/mail-2.9.1/lib/mail/network/delivery_methods/smtp_connection.rb +57 -0
  89. data/mail-2.9.1/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
  90. data/mail-2.9.1/lib/mail/network/retriever_methods/base.rb +64 -0
  91. data/mail-2.9.1/lib/mail/network/retriever_methods/imap.rb +190 -0
  92. data/mail-2.9.1/lib/mail/network/retriever_methods/pop3.rb +143 -0
  93. data/mail-2.9.1/lib/mail/network/retriever_methods/test_retriever.rb +45 -0
  94. data/mail-2.9.1/lib/mail/network.rb +16 -0
  95. data/mail-2.9.1/lib/mail/parser_tools.rb +15 -0
  96. data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rb +33245 -0
  97. data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rl +183 -0
  98. data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rb +904 -0
  99. data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rl +93 -0
  100. data/mail-2.9.1/lib/mail/parsers/content_location_parser.rb +825 -0
  101. data/mail-2.9.1/lib/mail/parsers/content_location_parser.rl +82 -0
  102. data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rb +525 -0
  103. data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
  104. data/mail-2.9.1/lib/mail/parsers/content_type_parser.rb +1051 -0
  105. data/mail-2.9.1/lib/mail/parsers/content_type_parser.rl +94 -0
  106. data/mail-2.9.1/lib/mail/parsers/date_time_parser.rb +894 -0
  107. data/mail-2.9.1/lib/mail/parsers/date_time_parser.rl +73 -0
  108. data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rb +3678 -0
  109. data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rl +93 -0
  110. data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rb +5164 -0
  111. data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rl +97 -0
  112. data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rb +516 -0
  113. data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rl +72 -0
  114. data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rb +887 -0
  115. data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rl +94 -0
  116. data/mail-2.9.1/lib/mail/parsers/received_parser.rb +8785 -0
  117. data/mail-2.9.1/lib/mail/parsers/received_parser.rl +95 -0
  118. data/mail-2.9.1/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  119. data/mail-2.9.1/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  120. data/mail-2.9.1/lib/mail/parsers/rfc2045_mime.rl +16 -0
  121. data/mail-2.9.1/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  122. data/mail-2.9.1/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  123. data/mail-2.9.1/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  124. data/mail-2.9.1/lib/mail/parsers/rfc5322.rl +74 -0
  125. data/mail-2.9.1/lib/mail/parsers/rfc5322_address.rl +72 -0
  126. data/mail-2.9.1/lib/mail/parsers/rfc5322_date_time.rl +37 -0
  127. data/mail-2.9.1/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  128. data/mail-2.9.1/lib/mail/parsers.rb +13 -0
  129. data/mail-2.9.1/lib/mail/part.rb +119 -0
  130. data/mail-2.9.1/lib/mail/parts_list.rb +131 -0
  131. data/mail-2.9.1/lib/mail/smtp_envelope.rb +57 -0
  132. data/mail-2.9.1/lib/mail/utilities.rb +576 -0
  133. data/mail-2.9.1/lib/mail/values/unicode_tables.dat +0 -0
  134. data/mail-2.9.1/lib/mail/version.rb +17 -0
  135. data/mail-2.9.1/lib/mail/yaml.rb +30 -0
  136. data/mail-2.9.1/lib/mail.rb +65 -0
  137. data/nano-fast-tool.gemspec +12 -0
  138. metadata +177 -0
@@ -0,0 +1,189 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'mail/utilities'
4
+
5
+ module Mail
6
+ # Provides access to an unstructured header field
7
+ #
8
+ # ===Per RFC 2822:
9
+ # 2.2.1. Unstructured Header Field Bodies
10
+ #
11
+ # Some field bodies in this standard are defined simply as
12
+ # "unstructured" (which is specified below as any US-ASCII characters,
13
+ # except for CR and LF) with no further restrictions. These are
14
+ # referred to as unstructured field bodies. Semantically, unstructured
15
+ # field bodies are simply to be treated as a single line of characters
16
+ # with no further processing (except for header "folding" and
17
+ # "unfolding" as described in section 2.2.3).
18
+ class UnstructuredField < CommonField #:nodoc:
19
+ def initialize(name, value, charset = nil)
20
+ if value.is_a?(Array)
21
+ # Probably has arrived here from a failed parse of an AddressList Field
22
+ value = value.join(', ')
23
+
24
+ # Mark UTF-8 strings parsed from ASCII-8BIT
25
+ elsif value.respond_to?(:force_encoding) && value.encoding == Encoding::ASCII_8BIT
26
+ utf8 = value.dup.force_encoding(Encoding::UTF_8)
27
+ value = utf8 if utf8.valid_encoding?
28
+ end
29
+
30
+ charset ||=
31
+ if value.respond_to?(:encoding)
32
+ value.encoding
33
+ end
34
+
35
+ super name, value.to_s, charset
36
+ end
37
+
38
+ # An unstructured field does not parse
39
+ def parse
40
+ self
41
+ end
42
+
43
+ private
44
+
45
+ def do_encode
46
+ if value && !value.empty?
47
+ "#{wrapped_value}\r\n"
48
+ else
49
+ ''
50
+ end
51
+ end
52
+
53
+ def do_decode
54
+ Utilities.blank?(value) ? nil : Encodings.decode_encode(value, :decode)
55
+ end
56
+
57
+ # 2.2.3. Long Header Fields
58
+ #
59
+ # Each header field is logically a single line of characters comprising
60
+ # the field name, the colon, and the field body. For convenience
61
+ # however, and to deal with the 998/78 character limitations per line,
62
+ # the field body portion of a header field can be split into a multiple
63
+ # line representation; this is called "folding". The general rule is
64
+ # that wherever this standard allows for folding white space (not
65
+ # simply WSP characters), a CRLF may be inserted before any WSP. For
66
+ # example, the header field:
67
+ #
68
+ # Subject: This is a test
69
+ #
70
+ # can be represented as:
71
+ #
72
+ # Subject: This
73
+ # is a test
74
+ #
75
+ # Note: Though structured field bodies are defined in such a way that
76
+ # folding can take place between many of the lexical tokens (and even
77
+ # within some of the lexical tokens), folding SHOULD be limited to
78
+ # placing the CRLF at higher-level syntactic breaks. For instance, if
79
+ # a field body is defined as comma-separated values, it is recommended
80
+ # that folding occur after the comma separating the structured items in
81
+ # preference to other places where the field could be folded, even if
82
+ # it is allowed elsewhere.
83
+ def wrapped_value # :nodoc:
84
+ wrap_lines(name, fold("#{name}: ".length))
85
+ end
86
+
87
+ # 6.2. Display of 'encoded-word's
88
+ #
89
+ # When displaying a particular header field that contains multiple
90
+ # 'encoded-word's, any 'linear-white-space' that separates a pair of
91
+ # adjacent 'encoded-word's is ignored. (This is to allow the use of
92
+ # multiple 'encoded-word's to represent long strings of unencoded text,
93
+ # without having to separate 'encoded-word's where spaces occur in the
94
+ # unencoded text.)
95
+ def wrap_lines(name, folded_lines)
96
+ result = ["#{name}: #{folded_lines.shift}"]
97
+ result.concat(folded_lines)
98
+ result.join("\r\n\s")
99
+ end
100
+
101
+ def fold(prepend = 0) # :nodoc:
102
+ encoding = normalized_encoding
103
+ decoded_string = decoded.to_s
104
+ should_encode = !decoded_string.ascii_only?
105
+ if should_encode
106
+ first = true
107
+ words = decoded_string.split(/[ \t]/).map do |word|
108
+ if first
109
+ first = !first
110
+ else
111
+ word = " #{word}"
112
+ end
113
+ if !word.ascii_only?
114
+ word
115
+ else
116
+ word.scan(/.{7}|.+$/)
117
+ end
118
+ end.flatten
119
+ else
120
+ words = decoded_string.split(/[ \t]/)
121
+ end
122
+
123
+ folded_lines = []
124
+ while !words.empty?
125
+ limit = 78 - prepend
126
+ limit = limit - 7 - encoding.length if should_encode
127
+ line = String.new
128
+ first_word = true
129
+ while !words.empty?
130
+ break unless word = words.first.dup
131
+
132
+ # Convert on 1.9+ only since we aren't sure of the current
133
+ # charset encoding on 1.8. We'd need to track internal/external
134
+ # charset on each field.
135
+ if charset && word.respond_to?(:encoding)
136
+ word = Encodings.transcode_charset(word, word.encoding, charset)
137
+ end
138
+
139
+ word = encode(word) if should_encode
140
+ word = encode_crlf(word)
141
+ # Skip to next line if we're going to go past the limit
142
+ # Unless this is the first word, in which case we're going to add it anyway
143
+ # Note: This means that a word that's longer than 998 characters is going to break the spec. Please fix if this is a problem for you.
144
+ # (The fix, it seems, would be to use encoded-word encoding on it, because that way you can break it across multiple lines and
145
+ # the linebreak will be ignored)
146
+ break if !line.empty? && (line.length + word.length + 1 > limit)
147
+ # Remove the word from the queue ...
148
+ words.shift
149
+ # Add word separator
150
+ if first_word
151
+ first_word = false
152
+ else
153
+ line << " " if !should_encode
154
+ end
155
+
156
+ # ... add it in encoded form to the current line
157
+ line << word
158
+ end
159
+ # Encode the line if necessary
160
+ line = "=?#{encoding}?Q?#{line}?=" if should_encode
161
+ # Add the line to the output and reset the prepend
162
+ folded_lines << line
163
+ prepend = 0
164
+ end
165
+ folded_lines
166
+ end
167
+
168
+ def encode(value)
169
+ value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
170
+ value.gsub!(/"/, '=22')
171
+ value.gsub!(/\(/, '=28')
172
+ value.gsub!(/\)/, '=29')
173
+ value.gsub!(/\?/, '=3F')
174
+ value.gsub!(/_/, '=5F')
175
+ value.gsub!(/ /, '_')
176
+ value
177
+ end
178
+
179
+ def encode_crlf(value)
180
+ value.gsub!(Constants::CR, Constants::CR_ENCODED)
181
+ value.gsub!(Constants::LF, Constants::LF_ENCODED)
182
+ value
183
+ end
184
+
185
+ def normalized_encoding
186
+ charset.to_s.upcase.gsub('_', '-')
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ module Mail
3
+ register_autoload :CommonField, 'mail/fields/common_field'
4
+ register_autoload :CommonAddressField, 'mail/fields/common_address_field'
5
+ register_autoload :CommonMessageIdField, 'mail/fields/common_message_id_field'
6
+ register_autoload :CommonDateField, 'mail/fields/common_date_field'
7
+
8
+ register_autoload :UnstructuredField, 'mail/fields/unstructured_field'
9
+ register_autoload :NamedUnstructuredField, 'mail/fields/named_unstructured_field'
10
+ register_autoload :StructuredField, 'mail/fields/structured_field'
11
+ register_autoload :OptionalField, 'mail/fields/optional_field'
12
+
13
+ register_autoload :BccField, 'mail/fields/bcc_field'
14
+ register_autoload :CcField, 'mail/fields/cc_field'
15
+ register_autoload :CommentsField, 'mail/fields/comments_field'
16
+ register_autoload :ContentDescriptionField, 'mail/fields/content_description_field'
17
+ register_autoload :ContentDispositionField, 'mail/fields/content_disposition_field'
18
+ register_autoload :ContentIdField, 'mail/fields/content_id_field'
19
+ register_autoload :ContentLocationField, 'mail/fields/content_location_field'
20
+ register_autoload :ContentTransferEncodingField, 'mail/fields/content_transfer_encoding_field'
21
+ register_autoload :ContentTypeField, 'mail/fields/content_type_field'
22
+ register_autoload :DateField, 'mail/fields/date_field'
23
+ register_autoload :FromField, 'mail/fields/from_field'
24
+ register_autoload :InReplyToField, 'mail/fields/in_reply_to_field'
25
+ register_autoload :KeywordsField, 'mail/fields/keywords_field'
26
+ register_autoload :MessageIdField, 'mail/fields/message_id_field'
27
+ register_autoload :MimeVersionField, 'mail/fields/mime_version_field'
28
+ register_autoload :NamedStructuredField, 'mail/fields/named_structured_field'
29
+ register_autoload :ReceivedField, 'mail/fields/received_field'
30
+ register_autoload :ReferencesField, 'mail/fields/references_field'
31
+ register_autoload :ReplyToField, 'mail/fields/reply_to_field'
32
+ register_autoload :ResentBccField, 'mail/fields/resent_bcc_field'
33
+ register_autoload :ResentCcField, 'mail/fields/resent_cc_field'
34
+ register_autoload :ResentDateField, 'mail/fields/resent_date_field'
35
+ register_autoload :ResentFromField, 'mail/fields/resent_from_field'
36
+ register_autoload :ResentMessageIdField, 'mail/fields/resent_message_id_field'
37
+ register_autoload :ResentSenderField, 'mail/fields/resent_sender_field'
38
+ register_autoload :ResentToField, 'mail/fields/resent_to_field'
39
+ register_autoload :ReturnPathField, 'mail/fields/return_path_field'
40
+ register_autoload :SenderField, 'mail/fields/sender_field'
41
+ register_autoload :SubjectField, 'mail/fields/subject_field'
42
+ register_autoload :ToField, 'mail/fields/to_field'
43
+
44
+ register_autoload :ParameterHash, 'mail/fields/parameter_hash'
45
+ end
@@ -0,0 +1,237 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+
6
+ module Mail
7
+ # Provides access to a header object.
8
+ #
9
+ # ===Per RFC2822
10
+ #
11
+ # 2.2. Header Fields
12
+ #
13
+ # Header fields are lines composed of a field name, followed by a colon
14
+ # (":"), followed by a field body, and terminated by CRLF. A field
15
+ # name MUST be composed of printable US-ASCII characters (i.e.,
16
+ # characters that have values between 33 and 126, inclusive), except
17
+ # colon. A field body may be composed of any US-ASCII characters,
18
+ # except for CR and LF. However, a field body may contain CRLF when
19
+ # used in header "folding" and "unfolding" as described in section
20
+ # 2.2.3. All field bodies MUST conform to the syntax described in
21
+ # sections 3 and 4 of this standard.
22
+ class Header
23
+ include Enumerable
24
+
25
+ @@maximum_amount = 1000
26
+
27
+ # Large amount of headers in Email might create extra high CPU load
28
+ # Use this parameter to limit number of headers that will be parsed by
29
+ # mail library.
30
+ # Default: 1000
31
+ def self.maximum_amount
32
+ @@maximum_amount
33
+ end
34
+
35
+ def self.maximum_amount=(value)
36
+ @@maximum_amount = value
37
+ end
38
+
39
+ attr_reader :raw_source, :charset
40
+
41
+ # Creates a new header object.
42
+ #
43
+ # Accepts raw text or nothing. If given raw text will attempt to parse
44
+ # it and split it into the various fields, instantiating each field as
45
+ # it goes.
46
+ #
47
+ # If it finds a field that should be a structured field (such as content
48
+ # type), but it fails to parse it, it will simply make it an unstructured
49
+ # field and leave it alone. This will mean that the data is preserved but
50
+ # no automatic processing of that field will happen. If you find one of
51
+ # these cases, please make a patch and send it in, or at the least, send
52
+ # me the example so we can fix it.
53
+ def initialize(header_text = nil, charset = nil)
54
+ @charset = charset
55
+ @raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
56
+ split_header if header_text
57
+ end
58
+
59
+ def initialize_copy(original)
60
+ super
61
+ @fields = @fields.dup
62
+ @fields.map!(&:dup)
63
+ end
64
+
65
+ # Returns an array of all the fields in the header in order that they
66
+ # were read in.
67
+ def fields
68
+ @fields ||= FieldList.new
69
+ end
70
+
71
+ # 3.6. Field definitions
72
+ #
73
+ # It is important to note that the header fields are not guaranteed to
74
+ # be in a particular order. They may appear in any order, and they
75
+ # have been known to be reordered occasionally when transported over
76
+ # the Internet. However, for the purposes of this standard, header
77
+ # fields SHOULD NOT be reordered when a message is transported or
78
+ # transformed. More importantly, the trace header fields and resent
79
+ # header fields MUST NOT be reordered, and SHOULD be kept in blocks
80
+ # prepended to the message. See sections 3.6.6 and 3.6.7 for more
81
+ # information.
82
+ #
83
+ # Populates the fields container with Field objects in the order it
84
+ # receives them in.
85
+ #
86
+ # Acceps an array of field string values, for example:
87
+ #
88
+ # h = Header.new
89
+ # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
90
+ def fields=(unfolded_fields)
91
+ @fields = Mail::FieldList.new
92
+
93
+ if unfolded_fields.size > self.class.maximum_amount
94
+ Kernel.warn "WARNING: More than #{self.class.maximum_amount} header fields; only using the first #{self.class.maximum_amount} and ignoring the rest"
95
+ unfolded_fields = unfolded_fields.slice(0...self.class.maximum_amount)
96
+ end
97
+
98
+ unfolded_fields.each do |field|
99
+ if field = Field.parse(field, charset)
100
+ @fields.add_field field
101
+ end
102
+ end
103
+ end
104
+
105
+ def errors
106
+ @fields.map(&:errors).flatten(1)
107
+ end
108
+
109
+ # 3.6. Field definitions
110
+ #
111
+ # The following table indicates limits on the number of times each
112
+ # field may occur in a message header as well as any special
113
+ # limitations on the use of those fields. An asterisk next to a value
114
+ # in the minimum or maximum column indicates that a special restriction
115
+ # appears in the Notes column.
116
+ #
117
+ # <snip table from 3.6>
118
+ #
119
+ # As per RFC, many fields can appear more than once, we will return a string
120
+ # of the value if there is only one header, or if there is more than one
121
+ # matching header, will return an array of values in order that they appear
122
+ # in the header ordered from top to bottom.
123
+ #
124
+ # Example:
125
+ #
126
+ # h = Header.new
127
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
128
+ # h['To'] #=> 'mikel@me.com'
129
+ # h['X-Mail-SPAM'] #=> ['15', '20']
130
+ def [](name)
131
+ fields.get_field(Utilities.dasherize(name))
132
+ end
133
+
134
+ # Sets the FIRST matching field in the header to passed value, or deletes
135
+ # the FIRST field matched from the header if passed nil
136
+ #
137
+ # Example:
138
+ #
139
+ # h = Header.new
140
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
141
+ # h['To'] = 'bob@you.com'
142
+ # h['To'] #=> 'bob@you.com'
143
+ # h['X-Mail-SPAM'] = '10000'
144
+ # h['X-Mail-SPAM'] # => ['15', '20', '10000']
145
+ # h['X-Mail-SPAM'] = nil
146
+ # h['X-Mail-SPAM'] # => nil
147
+ def []=(name, value)
148
+ name = name.to_s
149
+ if name.include?(Constants::COLON)
150
+ raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
151
+ end
152
+
153
+ name = Utilities.dasherize(name)
154
+
155
+ # Assign nil to delete the field
156
+ if value.nil?
157
+ fields.delete_field name
158
+ else
159
+ fields.add_field Field.new(name.to_s, value, charset)
160
+
161
+ # Update charset if specified in Content-Type
162
+ if name == 'content-type'
163
+ params = self[:content_type].parameters rescue nil
164
+ @charset = params[:charset] if params && params[:charset]
165
+ end
166
+ end
167
+ end
168
+
169
+ def charset=(val)
170
+ params = self[:content_type].parameters rescue nil
171
+ if params
172
+ if val
173
+ params[:charset] = val
174
+ else
175
+ params.delete(:charset)
176
+ end
177
+ end
178
+ @charset = val
179
+ end
180
+
181
+
182
+ def encoded
183
+ buffer = String.new
184
+ buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
185
+ fields.each do |field|
186
+ buffer << field.encoded
187
+ end
188
+ buffer
189
+ end
190
+
191
+ def to_s
192
+ encoded
193
+ end
194
+
195
+ def decoded
196
+ raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts. Try calling #decoded on the various fields.'
197
+ end
198
+
199
+ def field_summary
200
+ fields.summary
201
+ end
202
+
203
+ # Returns true if the header has a Message-ID defined (empty or not)
204
+ def has_message_id?
205
+ fields.has_field? 'Message-ID'
206
+ end
207
+
208
+ # Returns true if the header has a Content-ID defined (empty or not)
209
+ def has_content_id?
210
+ fields.has_field? 'Content-ID'
211
+ end
212
+
213
+ # Returns true if the header has a Date defined (empty or not)
214
+ def has_date?
215
+ fields.has_field? 'Date'
216
+ end
217
+
218
+ # Returns true if the header has a MIME version defined (empty or not)
219
+ def has_mime_version?
220
+ fields.has_field? 'MIME-Version'
221
+ end
222
+
223
+ private
224
+
225
+ # Splits an unfolded and line break cleaned header into individual field
226
+ # strings.
227
+ def split_header
228
+ self.fields = @raw_source.split(Constants::HEADER_SPLIT)
229
+ end
230
+
231
+
232
+ # Enumerable support. Yield each field in order.
233
+ def each(&block)
234
+ fields.each(&block)
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,147 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
5
+ # itself does not depend on ActiveSupport to avoid versioning conflicts
6
+
7
+ module Mail
8
+ class IndifferentHash < Hash
9
+
10
+ def initialize(constructor = {})
11
+ if constructor.is_a?(Hash)
12
+ super()
13
+ update(constructor)
14
+ else
15
+ super(constructor)
16
+ end
17
+ end
18
+
19
+ def default(key = nil)
20
+ if key.is_a?(Symbol) && include?(key = key.to_s)
21
+ self[key]
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def self.new_from_hash_copying_default(hash)
28
+ IndifferentHash.new(hash).tap do |new_hash|
29
+ new_hash.default = hash.default
30
+ end
31
+ end
32
+
33
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
34
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
35
+
36
+ # Assigns a new value to the hash:
37
+ #
38
+ # hash = HashWithIndifferentAccess.new
39
+ # hash[:key] = "value"
40
+ #
41
+ def []=(key, value)
42
+ regular_writer(convert_key(key), convert_value(value))
43
+ end
44
+
45
+ alias_method :store, :[]=
46
+
47
+ # Updates the instantized hash with values from the second:
48
+ #
49
+ # hash_1 = HashWithIndifferentAccess.new
50
+ # hash_1[:key] = "value"
51
+ #
52
+ # hash_2 = HashWithIndifferentAccess.new
53
+ # hash_2[:key] = "New Value!"
54
+ #
55
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
56
+ #
57
+ def update(other_hash)
58
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
59
+ self
60
+ end
61
+
62
+ alias_method :merge!, :update
63
+
64
+ # Checks the hash for a key matching the argument passed in:
65
+ #
66
+ # hash = HashWithIndifferentAccess.new
67
+ # hash["key"] = "value"
68
+ # hash.key? :key # => true
69
+ # hash.key? "key" # => true
70
+ #
71
+ def key?(key)
72
+ super(convert_key(key))
73
+ end
74
+
75
+ alias_method :include?, :key?
76
+ alias_method :has_key?, :key?
77
+ alias_method :member?, :key?
78
+
79
+ # Fetches the value for the specified key, same as doing hash[key]
80
+ def fetch(key, *extras)
81
+ super(convert_key(key), *extras)
82
+ end
83
+
84
+ # Returns an array of the values at the specified indices:
85
+ #
86
+ # hash = HashWithIndifferentAccess.new
87
+ # hash[:a] = "x"
88
+ # hash[:b] = "y"
89
+ # hash.values_at("a", "b") # => ["x", "y"]
90
+ #
91
+ def values_at(*indices)
92
+ indices.collect {|key| self[convert_key(key)]}
93
+ end
94
+
95
+ # Returns an exact copy of the hash.
96
+ def dup
97
+ IndifferentHash.new(self)
98
+ end
99
+
100
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
101
+ # Does not overwrite the existing hash.
102
+ def merge(hash)
103
+ self.dup.update(hash)
104
+ end
105
+
106
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
107
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
108
+ def reverse_merge(other_hash)
109
+ super self.class.new_from_hash_copying_default(other_hash)
110
+ end
111
+
112
+ def reverse_merge!(other_hash)
113
+ replace(reverse_merge( other_hash ))
114
+ end
115
+
116
+ # Removes a specified key from the hash.
117
+ def delete(key)
118
+ super(convert_key(key))
119
+ end
120
+
121
+ def stringify_keys!; self end
122
+ def stringify_keys; dup end
123
+ def symbolize_keys; to_hash.symbolize_keys end
124
+ def to_options!; self end
125
+
126
+ def to_hash
127
+ Hash.new(default).merge!(self)
128
+ end
129
+
130
+ protected
131
+
132
+ def convert_key(key)
133
+ key.kind_of?(Symbol) ? key.to_s : key
134
+ end
135
+
136
+ def convert_value(value)
137
+ if value.class == Hash
138
+ self.class.new_from_hash_copying_default(value)
139
+ elsif value.is_a?(Array)
140
+ value.dup.replace(value.map { |e| convert_value(e) })
141
+ else
142
+ value
143
+ end
144
+ end
145
+
146
+ end
147
+ end