mail 2.6.1 → 2.8.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 (188) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +150 -107
  4. data/lib/mail/attachments_list.rb +13 -10
  5. data/lib/mail/body.rb +104 -90
  6. data/lib/mail/check_delivery_params.rb +55 -10
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +79 -0
  9. data/lib/mail/elements/address.rb +96 -108
  10. data/lib/mail/elements/address_list.rb +13 -30
  11. data/lib/mail/elements/content_disposition_element.rb +10 -16
  12. data/lib/mail/elements/content_location_element.rb +9 -13
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +7 -11
  14. data/lib/mail/elements/content_type_element.rb +17 -23
  15. data/lib/mail/elements/date_time_element.rb +8 -15
  16. data/lib/mail/elements/envelope_from_element.rb +23 -23
  17. data/lib/mail/elements/message_ids_element.rb +22 -15
  18. data/lib/mail/elements/mime_version_element.rb +8 -15
  19. data/lib/mail/elements/phrase_list.rb +13 -10
  20. data/lib/mail/elements/received_element.rb +28 -19
  21. data/lib/mail/elements.rb +1 -0
  22. data/lib/mail/encodings/7bit.rb +10 -14
  23. data/lib/mail/encodings/8bit.rb +5 -18
  24. data/lib/mail/encodings/base64.rb +15 -10
  25. data/lib/mail/encodings/binary.rb +4 -22
  26. data/lib/mail/encodings/identity.rb +24 -0
  27. data/lib/mail/encodings/quoted_printable.rb +13 -7
  28. data/lib/mail/encodings/transfer_encoding.rb +47 -28
  29. data/lib/mail/encodings/unix_to_unix.rb +20 -0
  30. data/lib/mail/encodings.rb +102 -92
  31. data/lib/mail/envelope.rb +12 -14
  32. data/lib/mail/field.rb +121 -85
  33. data/lib/mail/field_list.rb +62 -8
  34. data/lib/mail/fields/bcc_field.rb +42 -48
  35. data/lib/mail/fields/cc_field.rb +29 -50
  36. data/lib/mail/fields/comments_field.rb +28 -37
  37. data/lib/mail/fields/common_address_field.rb +170 -0
  38. data/lib/mail/fields/common_date_field.rb +58 -0
  39. data/lib/mail/fields/common_field.rb +77 -0
  40. data/lib/mail/fields/common_message_id_field.rb +42 -0
  41. data/lib/mail/fields/content_description_field.rb +8 -14
  42. data/lib/mail/fields/content_disposition_field.rb +20 -44
  43. data/lib/mail/fields/content_id_field.rb +25 -51
  44. data/lib/mail/fields/content_location_field.rb +12 -25
  45. data/lib/mail/fields/content_transfer_encoding_field.rb +32 -31
  46. data/lib/mail/fields/content_type_field.rb +51 -80
  47. data/lib/mail/fields/date_field.rb +24 -52
  48. data/lib/mail/fields/from_field.rb +29 -50
  49. data/lib/mail/fields/in_reply_to_field.rb +39 -49
  50. data/lib/mail/fields/keywords_field.rb +19 -32
  51. data/lib/mail/fields/message_id_field.rb +26 -71
  52. data/lib/mail/fields/mime_version_field.rb +20 -30
  53. data/lib/mail/fields/named_structured_field.rb +11 -0
  54. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  55. data/lib/mail/fields/optional_field.rb +10 -7
  56. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
  57. data/lib/mail/fields/received_field.rb +44 -57
  58. data/lib/mail/fields/references_field.rb +36 -49
  59. data/lib/mail/fields/reply_to_field.rb +29 -50
  60. data/lib/mail/fields/resent_bcc_field.rb +29 -50
  61. data/lib/mail/fields/resent_cc_field.rb +29 -50
  62. data/lib/mail/fields/resent_date_field.rb +6 -30
  63. data/lib/mail/fields/resent_from_field.rb +29 -50
  64. data/lib/mail/fields/resent_message_id_field.rb +6 -29
  65. data/lib/mail/fields/resent_sender_field.rb +28 -57
  66. data/lib/mail/fields/resent_to_field.rb +29 -50
  67. data/lib/mail/fields/return_path_field.rb +51 -55
  68. data/lib/mail/fields/sender_field.rb +35 -56
  69. data/lib/mail/fields/structured_field.rb +4 -30
  70. data/lib/mail/fields/subject_field.rb +10 -11
  71. data/lib/mail/fields/to_field.rb +29 -50
  72. data/lib/mail/fields/unstructured_field.rb +36 -50
  73. data/lib/mail/fields.rb +1 -0
  74. data/lib/mail/header.rb +73 -110
  75. data/lib/mail/indifferent_hash.rb +1 -0
  76. data/lib/mail/mail.rb +6 -11
  77. data/lib/mail/matchers/attachment_matchers.rb +44 -0
  78. data/lib/mail/matchers/has_sent_mail.rb +53 -9
  79. data/lib/mail/message.rb +132 -136
  80. data/lib/mail/multibyte/chars.rb +24 -180
  81. data/lib/mail/multibyte/unicode.rb +31 -26
  82. data/lib/mail/multibyte/utils.rb +27 -43
  83. data/lib/mail/multibyte.rb +56 -16
  84. data/lib/mail/network/delivery_methods/exim.rb +9 -11
  85. data/lib/mail/network/delivery_methods/file_delivery.rb +14 -16
  86. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  87. data/lib/mail/network/delivery_methods/sendmail.rb +68 -24
  88. data/lib/mail/network/delivery_methods/smtp.rb +77 -54
  89. data/lib/mail/network/delivery_methods/smtp_connection.rb +5 -9
  90. data/lib/mail/network/delivery_methods/test_mailer.rb +9 -9
  91. data/lib/mail/network/retriever_methods/base.rb +9 -8
  92. data/lib/mail/network/retriever_methods/imap.rb +21 -7
  93. data/lib/mail/network/retriever_methods/pop3.rb +6 -3
  94. data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
  95. data/lib/mail/network.rb +2 -0
  96. data/lib/mail/parser_tools.rb +15 -0
  97. data/lib/mail/parsers/address_lists_parser.rb +33226 -116
  98. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  99. data/lib/mail/parsers/content_disposition_parser.rb +883 -49
  100. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  101. data/lib/mail/parsers/content_location_parser.rb +810 -23
  102. data/lib/mail/parsers/content_location_parser.rl +78 -0
  103. data/lib/mail/parsers/content_transfer_encoding_parser.rb +510 -21
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  105. data/lib/mail/parsers/content_type_parser.rb +1031 -47
  106. data/lib/mail/parsers/content_type_parser.rl +90 -0
  107. data/lib/mail/parsers/date_time_parser.rb +879 -24
  108. data/lib/mail/parsers/date_time_parser.rl +69 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +3670 -40
  110. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  111. data/lib/mail/parsers/message_ids_parser.rb +5147 -25
  112. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +498 -26
  114. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  115. data/lib/mail/parsers/phrase_lists_parser.rb +872 -21
  116. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  117. data/lib/mail/parsers/received_parser.rb +8777 -42
  118. data/lib/mail/parsers/received_parser.rl +91 -0
  119. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  120. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  121. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  122. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  123. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  124. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  125. data/lib/mail/parsers/rfc5322.rl +74 -0
  126. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  127. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  128. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  129. data/lib/mail/parsers.rb +12 -25
  130. data/lib/mail/part.rb +11 -12
  131. data/lib/mail/parts_list.rb +88 -14
  132. data/lib/mail/smtp_envelope.rb +57 -0
  133. data/lib/mail/utilities.rb +377 -40
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +8 -15
  136. data/lib/mail/yaml.rb +30 -0
  137. data/lib/mail.rb +9 -32
  138. metadata +138 -94
  139. data/CHANGELOG.rdoc +0 -752
  140. data/CONTRIBUTING.md +0 -60
  141. data/Dependencies.txt +0 -2
  142. data/Gemfile +0 -15
  143. data/Rakefile +0 -29
  144. data/TODO.rdoc +0 -9
  145. data/VERSION +0 -4
  146. data/lib/mail/core_extensions/nil.rb +0 -19
  147. data/lib/mail/core_extensions/object.rb +0 -13
  148. data/lib/mail/core_extensions/smtp.rb +0 -24
  149. data/lib/mail/core_extensions/string/access.rb +0 -145
  150. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  151. data/lib/mail/core_extensions/string.rb +0 -43
  152. data/lib/mail/fields/common/address_container.rb +0 -16
  153. data/lib/mail/fields/common/common_address.rb +0 -135
  154. data/lib/mail/fields/common/common_date.rb +0 -35
  155. data/lib/mail/fields/common/common_field.rb +0 -57
  156. data/lib/mail/fields/common/common_message_id.rb +0 -48
  157. data/lib/mail/multibyte/exceptions.rb +0 -8
  158. data/lib/mail/parsers/ragel/common.rl +0 -184
  159. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  160. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  161. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  162. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  163. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  164. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  165. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  166. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  167. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  168. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  169. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  170. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  171. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  172. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2129
  173. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  174. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  175. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  177. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  178. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  179. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  180. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  181. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  182. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  183. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  184. data/lib/mail/parsers/ragel/ruby.rb +0 -39
  185. data/lib/mail/parsers/ragel.rb +0 -17
  186. data/lib/mail/patterns.rb +0 -37
  187. data/lib/mail/version_specific/ruby_1_8.rb +0 -119
  188. data/lib/mail/version_specific/ruby_1_9.rb +0 -159
@@ -1,12 +1,41 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # Field List class provides an enhanced array that keeps a list of
5
6
  # email fields in order. And allows you to insert new fields without
6
7
  # having to worry about the order they will appear in.
7
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
8
21
 
9
- include Enumerable
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
10
39
 
11
40
  # Insert the field in sorted order.
12
41
  #
@@ -14,20 +43,45 @@ module Mail
14
43
  # Copyright (C) 2001-2013 Python Software Foundation.
15
44
  # Licensed under <http://docs.python.org/license.html>
16
45
  # From <http://hg.python.org/cpython/file/2.7/Lib/bisect.py>
17
- def <<( new_field )
18
- lo = 0
19
- hi = size
20
-
46
+ def insert_field(field)
47
+ lo, hi = 0, size
21
48
  while lo < hi
22
- mid = (lo + hi) / 2
23
- if new_field < self[mid]
49
+ mid = (lo + hi).div(2)
50
+ if field < self[mid]
24
51
  hi = mid
25
52
  else
26
53
  lo = mid + 1
27
54
  end
28
55
  end
29
56
 
30
- insert(lo, new_field)
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
31
85
  end
32
86
  end
33
87
  end
@@ -1,56 +1,50 @@
1
1
  # encoding: utf-8
2
- #
3
- # = Blind Carbon Copy Field
4
- #
5
- # The Bcc field inherits from StructuredField and handles the Bcc: header
6
- # field in the email.
7
- #
8
- # Sending bcc to a mail message will instantiate a Mail::Field object that
9
- # has a BccField as its field type. This includes all Mail::CommonAddress
10
- # module instance metods.
11
- #
12
- # Only one Bcc field can appear in a header, though it can have multiple
13
- # addresses and groups of addresses.
14
- #
15
- # == Examples:
16
- #
17
- # mail = Mail.new
18
- # mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
- # mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
21
- # mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
22
- # mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
23
- #
24
- # mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
25
- # mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
- # mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
- # mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
28
- #
29
- require 'mail/fields/common/common_address'
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/common_address_field'
30
4
 
31
5
  module Mail
32
- class BccField < StructuredField
33
-
34
- include Mail::CommonAddress
35
-
36
- FIELD_NAME = 'bcc'
37
- CAPITALIZED_FIELD = 'Bcc'
38
-
39
- def initialize(value = '', charset = 'utf-8')
40
- @charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
- self
6
+ # = Blind Carbon Copy Field
7
+ #
8
+ # The Bcc field inherits from StructuredField and handles the Bcc: header
9
+ # field in the email.
10
+ #
11
+ # Sending bcc to a mail message will instantiate a Mail::Field object that
12
+ # has a BccField as its field type. This includes all Mail::CommonAddress
13
+ # module instance metods.
14
+ #
15
+ # Only one Bcc field can appear in a header, though it can have multiple
16
+ # addresses and groups of addresses.
17
+ #
18
+ # == Examples:
19
+ #
20
+ # mail = Mail.new
21
+ # mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
22
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
23
+ # mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
24
+ # mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
25
+ # mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
26
+ #
27
+ # mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
28
+ # mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
29
+ # mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
30
+ # mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
31
+ class BccField < CommonAddressField #:nodoc:
32
+ NAME = 'Bcc'
33
+
34
+ attr_accessor :include_in_headers
35
+
36
+ def initialize(value = nil, charset = nil)
37
+ super
38
+ self.include_in_headers = false
44
39
  end
45
-
46
- # Bcc field should never be :encoded
40
+
41
+ # Bcc field should not be :encoded by default
47
42
  def encoded
48
- ''
49
- end
50
-
51
- def decoded
52
- do_decode
43
+ if include_in_headers
44
+ super
45
+ else
46
+ ''
47
+ end
53
48
  end
54
-
55
49
  end
56
50
  end
@@ -1,55 +1,34 @@
1
1
  # encoding: utf-8
2
- #
3
- # = Carbon Copy Field
4
- #
5
- # The Cc field inherits from StructuredField and handles the Cc: header
6
- # field in the email.
7
- #
8
- # Sending cc to a mail message will instantiate a Mail::Field object that
9
- # has a CcField as its field type. This includes all Mail::CommonAddress
10
- # module instance metods.
11
- #
12
- # Only one Cc field can appear in a header, though it can have multiple
13
- # addresses and groups of addresses.
14
- #
15
- # == Examples:
16
- #
17
- # mail = Mail.new
18
- # mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
- # mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
21
- # mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
22
- # mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
23
- #
24
- # mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
25
- # mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
- # mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
- # mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
28
- #
29
- require 'mail/fields/common/common_address'
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/common_address_field'
30
4
 
31
5
  module Mail
32
- class CcField < StructuredField
33
-
34
- include Mail::CommonAddress
35
-
36
- FIELD_NAME = 'cc'
37
- CAPITALIZED_FIELD = 'Cc'
38
-
39
- def initialize(value = nil, charset = 'utf-8')
40
- self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
- self
44
- end
45
-
46
- def encoded
47
- do_encode(CAPITALIZED_FIELD)
48
- end
49
-
50
- def decoded
51
- do_decode
52
- end
53
-
6
+ # = Carbon Copy Field
7
+ #
8
+ # The Cc field inherits from StructuredField and handles the Cc: header
9
+ # field in the email.
10
+ #
11
+ # Sending cc to a mail message will instantiate a Mail::Field object that
12
+ # has a CcField as its field type. This includes all Mail::CommonAddress
13
+ # module instance metods.
14
+ #
15
+ # Only one Cc field can appear in a header, though it can have multiple
16
+ # addresses and groups of addresses.
17
+ #
18
+ # == Examples:
19
+ #
20
+ # mail = Mail.new
21
+ # mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
22
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
23
+ # mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
24
+ # mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
25
+ # mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
26
+ #
27
+ # mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
28
+ # mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
29
+ # mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
30
+ # mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
31
+ class CcField < CommonAddressField #:nodoc:
32
+ NAME = 'Cc'
54
33
  end
55
34
  end
@@ -1,41 +1,32 @@
1
1
  # encoding: utf-8
2
- #
3
- # = Comments Field
4
- #
5
- # The Comments field inherits from UnstructuredField and handles the Comments:
6
- # header field in the email.
7
- #
8
- # Sending comments to a mail message will instantiate a Mail::Field object that
9
- # has a CommentsField as its field type.
10
- #
11
- # An email header can have as many comments fields as it wants. There is no upper
12
- # limit, the comments field is also optional (that is, no comment is needed)
13
- #
14
- # == Examples:
15
- #
16
- # mail = Mail.new
17
- # mail.comments = 'This is a comment'
18
- # mail.comments #=> 'This is a comment'
19
- # mail[:comments] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
20
- # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
21
- # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
22
- #
23
- # mail.comments = "This is another comment"
24
- # mail[:comments].map { |c| c.to_s }
25
- # #=> ['This is a comment', "This is another comment"]
26
- #
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/named_unstructured_field'
4
+
27
5
  module Mail
28
- class CommentsField < UnstructuredField
29
-
30
- FIELD_NAME = 'comments'
31
- CAPITALIZED_FIELD = 'Comments'
32
-
33
- def initialize(value = nil, charset = 'utf-8')
34
- @charset = charset
35
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value))
36
- self.parse
37
- self
38
- end
39
-
6
+ # = Comments Field
7
+ #
8
+ # The Comments field inherits from UnstructuredField and handles the Comments:
9
+ # header field in the email.
10
+ #
11
+ # Sending comments to a mail message will instantiate a Mail::Field object that
12
+ # has a CommentsField as its field type.
13
+ #
14
+ # An email header can have as many comments fields as it wants. There is no upper
15
+ # limit, the comments field is also optional (that is, no comment is needed)
16
+ #
17
+ # == Examples:
18
+ #
19
+ # mail = Mail.new
20
+ # mail.comments = 'This is a comment'
21
+ # mail.comments #=> 'This is a comment'
22
+ # mail[:comments] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
23
+ # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
24
+ # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
25
+ #
26
+ # mail.comments = "This is another comment"
27
+ # mail[:comments].map { |c| c.to_s }
28
+ # #=> ['This is a comment', "This is another comment"]
29
+ class CommentsField < NamedUnstructuredField #:nodoc:
30
+ NAME = 'Comments'
40
31
  end
41
32
  end
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/named_structured_field'
4
+
5
+ module Mail
6
+ class AddressContainer < Array #:nodoc:
7
+ def initialize(field, list = nil)
8
+ @field = field
9
+ super list if list
10
+ end
11
+
12
+ def <<(address)
13
+ @field << address
14
+ end
15
+ end
16
+
17
+ class CommonAddressField < NamedStructuredField #:nodoc:
18
+ def self.singular?
19
+ true
20
+ end
21
+
22
+ def initialize(value = nil, charset = nil)
23
+ super encode_if_needed(value, charset), charset
24
+ end
25
+
26
+ def element # :nodoc:
27
+ @element ||= AddressList.new(value)
28
+ end
29
+
30
+ # Allows you to iterate through each address object in the address_list
31
+ def each
32
+ element.addresses.each do |address|
33
+ yield(address)
34
+ end
35
+ end
36
+
37
+ def default
38
+ addresses
39
+ end
40
+
41
+ def address
42
+ addresses.first
43
+ end
44
+
45
+ # Returns the address string of all the addresses in the address list
46
+ def addresses
47
+ list = element.addresses.map { |a| a.address }
48
+ Mail::AddressContainer.new(self, list)
49
+ end
50
+
51
+ # Returns the formatted string of all the addresses in the address list
52
+ def formatted
53
+ list = element.addresses.map { |a| a.format }
54
+ Mail::AddressContainer.new(self, list)
55
+ end
56
+
57
+ # Returns the display name of all the addresses in the address list
58
+ def display_names
59
+ list = element.addresses.map { |a| a.display_name }
60
+ Mail::AddressContainer.new(self, list)
61
+ end
62
+
63
+ # Returns the actual address objects in the address list
64
+ def addrs
65
+ list = element.addresses
66
+ Mail::AddressContainer.new(self, list)
67
+ end
68
+
69
+ # Returns a hash of group name => address strings for the address list
70
+ def groups
71
+ element.addresses_grouped_by_group
72
+ end
73
+
74
+ # Returns the addresses that are part of groups
75
+ def group_addresses
76
+ decoded_group_addresses
77
+ end
78
+
79
+ # Returns a list of decoded group addresses
80
+ def decoded_group_addresses
81
+ groups.map { |k,v| v.map { |a| a.decoded } }.flatten
82
+ end
83
+
84
+ # Returns a list of encoded group addresses
85
+ def encoded_group_addresses
86
+ groups.map { |k,v| v.map { |a| a.encoded } }.flatten
87
+ end
88
+
89
+ # Returns the name of all the groups in a string
90
+ def group_names # :nodoc:
91
+ element.group_names
92
+ end
93
+
94
+ def <<(val)
95
+ case
96
+ when val.nil?
97
+ raise ArgumentError, "Need to pass an address to <<"
98
+ when Utilities.blank?(val)
99
+ self
100
+ else
101
+ self.value = [self.value, encode_if_needed(val)].reject { |a| Utilities.blank?(a) }.join(", ")
102
+ end
103
+ end
104
+
105
+ def encode_if_needed(val, val_charset = charset) #:nodoc:
106
+ case val
107
+ when nil
108
+ val
109
+
110
+ # Need to join arrays of addresses into a single value
111
+ when Array
112
+ val.compact.map { |a| encode_if_needed a, val_charset }.join(', ')
113
+
114
+ # Pass through UTF-8; encode non-UTF-8.
115
+ else
116
+ utf8_if_needed(val, val_charset) || Encodings.encode_non_usascii(val, val_charset)
117
+ end
118
+ end
119
+
120
+ private
121
+ if 'string'.respond_to?(:encoding)
122
+ # Pass through UTF-8 addresses
123
+ def utf8_if_needed(val, val_charset)
124
+ if val_charset =~ /\AUTF-?8\z/i
125
+ val
126
+ elsif val.encoding == Encoding::UTF_8
127
+ val
128
+ elsif (utf8 = val.dup.force_encoding(Encoding::UTF_8)).valid_encoding?
129
+ utf8
130
+ end
131
+ end
132
+ else
133
+ def utf8_if_needed(val, val_charset)
134
+ if val_charset =~ /\AUTF-?8\z/i
135
+ val
136
+ end
137
+ end
138
+ end
139
+
140
+ def do_encode
141
+ return '' if Utilities.blank?(value)
142
+ address_array = element.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
143
+ address_text = address_array.join(", \r\n\s")
144
+ group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
145
+ group_text = group_array.join(" \r\n\s")
146
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
147
+ "#{name}: #{return_array.join(", \r\n\s")}\r\n"
148
+ end
149
+
150
+ def do_decode
151
+ return nil if Utilities.blank?(value)
152
+ address_array = element.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
153
+ address_text = address_array.join(", ")
154
+ group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
155
+ group_text = group_array.join(" ")
156
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
157
+ return_array.join(", ")
158
+ end
159
+
160
+ def get_group_addresses(group_list)
161
+ if group_list.respond_to?(:addresses)
162
+ group_list.addresses.map do |address|
163
+ Mail::Address.new(address)
164
+ end
165
+ else
166
+ []
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,58 @@
1
+ require 'mail/fields/named_structured_field'
2
+ require 'mail/elements/date_time_element'
3
+ require 'mail/utilities'
4
+
5
+ module Mail
6
+ class CommonDateField < NamedStructuredField #:nodoc:
7
+ def self.singular?
8
+ true
9
+ end
10
+
11
+ def self.normalize_datetime(string)
12
+ if Utilities.blank?(string)
13
+ datetime = ::DateTime.now
14
+ else
15
+ stripped = string.to_s.gsub(/\(.*?\)/, '').squeeze(' ')
16
+ begin
17
+ datetime = ::DateTime.parse(stripped)
18
+ rescue ArgumentError => e
19
+ raise unless 'invalid date' == e.message
20
+ end
21
+ end
22
+
23
+ if datetime
24
+ datetime.strftime('%a, %d %b %Y %H:%M:%S %z')
25
+ else
26
+ string
27
+ end
28
+ end
29
+
30
+ def initialize(value = nil, charset = nil)
31
+ super self.class.normalize_datetime(value), charset
32
+ end
33
+
34
+ # Returns a date time object of the parsed date
35
+ def date_time
36
+ ::DateTime.parse("#{element.date_string} #{element.time_string}")
37
+ rescue ArgumentError => e
38
+ raise e unless e.message == 'invalid date'
39
+ end
40
+
41
+ def default
42
+ date_time
43
+ end
44
+
45
+ def element
46
+ @element ||= Mail::DateTimeElement.new(value)
47
+ end
48
+
49
+ private
50
+ def do_encode
51
+ "#{name}: #{value}\r\n"
52
+ end
53
+
54
+ def do_decode
55
+ value.to_s
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'mail/constants'
4
+
5
+ module Mail
6
+ class CommonField #:nodoc:
7
+ def self.singular?
8
+ false
9
+ end
10
+
11
+ def self.parse(*args)
12
+ new(*args).tap(&:parse)
13
+ end
14
+
15
+ attr_accessor :name
16
+ attr_reader :value
17
+ attr_accessor :charset
18
+ attr_reader :errors
19
+
20
+ def initialize(name = nil, value = nil, charset = nil)
21
+ @errors = []
22
+
23
+ self.name = name
24
+ self.value = value
25
+ self.charset = charset || 'utf-8'
26
+ end
27
+
28
+ def singular?
29
+ self.class.singular?
30
+ end
31
+
32
+ def value=(value)
33
+ @element = nil
34
+ @value = value.is_a?(Array) ? value : value.to_s
35
+ parse
36
+ end
37
+
38
+ def parse
39
+ tap(&:element)
40
+ end
41
+
42
+ def element
43
+ nil
44
+ end
45
+
46
+ def to_s
47
+ decoded.to_s
48
+ end
49
+
50
+ def default
51
+ decoded
52
+ end
53
+
54
+ def decoded
55
+ do_decode
56
+ end
57
+
58
+ def encoded
59
+ do_encode
60
+ end
61
+
62
+ def responsible_for?(field_name)
63
+ name.to_s.casecmp(field_name.to_s) == 0
64
+ end
65
+
66
+ private
67
+
68
+ FILENAME_RE = /\b(filename|name)=([^;"\r\n]+\s[^;"\r\n]+)/
69
+ def ensure_filename_quoted(value)
70
+ if value.is_a?(String)
71
+ value.sub FILENAME_RE, '\1="\2"'
72
+ else
73
+ value
74
+ end
75
+ end
76
+ end
77
+ end