mail 2.6.1 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +92 -80
  4. data/lib/mail/attachments_list.rb +11 -5
  5. data/lib/mail/body.rb +81 -44
  6. data/lib/mail/check_delivery_params.rb +50 -10
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  9. data/lib/mail/core_extensions/smtp.rb +20 -16
  10. data/lib/mail/core_extensions/string.rb +1 -27
  11. data/lib/mail/elements/address.rb +81 -93
  12. data/lib/mail/elements/address_list.rb +12 -29
  13. data/lib/mail/elements/content_disposition_element.rb +9 -15
  14. data/lib/mail/elements/content_location_element.rb +8 -12
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -10
  16. data/lib/mail/elements/content_type_element.rb +9 -19
  17. data/lib/mail/elements/date_time_element.rb +7 -14
  18. data/lib/mail/elements/envelope_from_element.rb +15 -21
  19. data/lib/mail/elements/message_ids_element.rb +12 -14
  20. data/lib/mail/elements/mime_version_element.rb +7 -14
  21. data/lib/mail/elements/phrase_list.rb +7 -9
  22. data/lib/mail/elements/received_element.rb +10 -15
  23. data/lib/mail/elements.rb +1 -0
  24. data/lib/mail/encodings/7bit.rb +6 -15
  25. data/lib/mail/encodings/8bit.rb +5 -18
  26. data/lib/mail/encodings/base64.rb +15 -10
  27. data/lib/mail/encodings/binary.rb +4 -22
  28. data/lib/mail/encodings/identity.rb +24 -0
  29. data/lib/mail/encodings/quoted_printable.rb +13 -7
  30. data/lib/mail/encodings/transfer_encoding.rb +47 -28
  31. data/lib/mail/encodings/unix_to_unix.rb +20 -0
  32. data/lib/mail/encodings.rb +121 -82
  33. data/lib/mail/envelope.rb +2 -1
  34. data/lib/mail/field.rb +114 -62
  35. data/lib/mail/field_list.rb +2 -1
  36. data/lib/mail/fields/bcc_field.rb +17 -5
  37. data/lib/mail/fields/cc_field.rb +2 -2
  38. data/lib/mail/fields/comments_field.rb +2 -1
  39. data/lib/mail/fields/common/address_container.rb +3 -2
  40. data/lib/mail/fields/common/common_address.rb +40 -14
  41. data/lib/mail/fields/common/common_date.rb +2 -1
  42. data/lib/mail/fields/common/common_field.rb +6 -11
  43. data/lib/mail/fields/common/common_message_id.rb +3 -2
  44. data/lib/mail/fields/common/parameter_hash.rb +5 -4
  45. data/lib/mail/fields/content_description_field.rb +2 -1
  46. data/lib/mail/fields/content_disposition_field.rb +14 -13
  47. data/lib/mail/fields/content_id_field.rb +5 -4
  48. data/lib/mail/fields/content_location_field.rb +3 -2
  49. data/lib/mail/fields/content_transfer_encoding_field.rb +3 -2
  50. data/lib/mail/fields/content_type_field.rb +7 -11
  51. data/lib/mail/fields/date_field.rb +4 -4
  52. data/lib/mail/fields/from_field.rb +2 -2
  53. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  54. data/lib/mail/fields/keywords_field.rb +3 -3
  55. data/lib/mail/fields/message_id_field.rb +3 -2
  56. data/lib/mail/fields/mime_version_field.rb +4 -3
  57. data/lib/mail/fields/optional_field.rb +5 -1
  58. data/lib/mail/fields/received_field.rb +5 -4
  59. data/lib/mail/fields/references_field.rb +2 -1
  60. data/lib/mail/fields/reply_to_field.rb +2 -2
  61. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  62. data/lib/mail/fields/resent_cc_field.rb +2 -2
  63. data/lib/mail/fields/resent_date_field.rb +2 -2
  64. data/lib/mail/fields/resent_from_field.rb +2 -2
  65. data/lib/mail/fields/resent_message_id_field.rb +2 -1
  66. data/lib/mail/fields/resent_sender_field.rb +2 -2
  67. data/lib/mail/fields/resent_to_field.rb +2 -2
  68. data/lib/mail/fields/return_path_field.rb +2 -2
  69. data/lib/mail/fields/sender_field.rb +2 -2
  70. data/lib/mail/fields/structured_field.rb +1 -0
  71. data/lib/mail/fields/subject_field.rb +2 -1
  72. data/lib/mail/fields/to_field.rb +2 -2
  73. data/lib/mail/fields/unstructured_field.rb +28 -10
  74. data/lib/mail/fields.rb +1 -0
  75. data/lib/mail/header.rb +18 -14
  76. data/lib/mail/indifferent_hash.rb +1 -0
  77. data/lib/mail/mail.rb +6 -11
  78. data/lib/mail/matchers/attachment_matchers.rb +29 -0
  79. data/lib/mail/matchers/has_sent_mail.rb +53 -9
  80. data/lib/mail/message.rb +99 -89
  81. data/lib/mail/multibyte/chars.rb +32 -30
  82. data/lib/mail/multibyte/unicode.rb +31 -26
  83. data/lib/mail/multibyte/utils.rb +1 -0
  84. data/lib/mail/multibyte.rb +65 -15
  85. data/lib/mail/network/delivery_methods/exim.rb +7 -10
  86. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -8
  87. data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
  88. data/lib/mail/network/delivery_methods/sendmail.rb +17 -11
  89. data/lib/mail/network/delivery_methods/smtp.rb +60 -53
  90. data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -6
  91. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -8
  92. data/lib/mail/network/retriever_methods/base.rb +1 -0
  93. data/lib/mail/network/retriever_methods/imap.rb +19 -5
  94. data/lib/mail/network/retriever_methods/pop3.rb +4 -1
  95. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  96. data/lib/mail/network.rb +2 -0
  97. data/lib/mail/parser_tools.rb +15 -0
  98. data/lib/mail/parsers/address_lists_parser.rb +33208 -104
  99. data/lib/mail/parsers/address_lists_parser.rl +172 -0
  100. data/lib/mail/parsers/content_disposition_parser.rb +877 -49
  101. data/lib/mail/parsers/content_disposition_parser.rl +82 -0
  102. data/lib/mail/parsers/content_location_parser.rb +804 -23
  103. data/lib/mail/parsers/content_location_parser.rl +71 -0
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rb +502 -19
  105. data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
  106. data/lib/mail/parsers/content_type_parser.rb +1024 -46
  107. data/lib/mail/parsers/content_type_parser.rl +83 -0
  108. data/lib/mail/parsers/date_time_parser.rb +872 -23
  109. data/lib/mail/parsers/date_time_parser.rl +62 -0
  110. data/lib/mail/parsers/envelope_from_parser.rb +3570 -34
  111. data/lib/mail/parsers/envelope_from_parser.rl +82 -0
  112. data/lib/mail/parsers/message_ids_parser.rb +2840 -25
  113. data/lib/mail/parsers/message_ids_parser.rl +82 -0
  114. data/lib/mail/parsers/mime_version_parser.rb +492 -26
  115. data/lib/mail/parsers/mime_version_parser.rl +61 -0
  116. data/lib/mail/parsers/phrase_lists_parser.rb +862 -17
  117. data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
  118. data/lib/mail/parsers/received_parser.rb +8765 -36
  119. data/lib/mail/parsers/received_parser.rl +84 -0
  120. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  121. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  122. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  123. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  124. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  125. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  126. data/lib/mail/parsers/rfc5322.rl +59 -0
  127. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  128. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  129. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  130. data/lib/mail/parsers.rb +17 -24
  131. data/lib/mail/part.rb +8 -5
  132. data/lib/mail/parts_list.rb +31 -14
  133. data/lib/mail/utilities.rb +112 -13
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +8 -15
  136. data/lib/mail/version_specific/ruby_1_8.rb +52 -8
  137. data/lib/mail/version_specific/ruby_1_9.rb +143 -24
  138. data/lib/mail.rb +8 -14
  139. metadata +71 -81
  140. data/CHANGELOG.rdoc +0 -752
  141. data/CONTRIBUTING.md +0 -60
  142. data/Dependencies.txt +0 -2
  143. data/Gemfile +0 -15
  144. data/Rakefile +0 -29
  145. data/TODO.rdoc +0 -9
  146. data/VERSION +0 -4
  147. data/lib/mail/core_extensions/nil.rb +0 -19
  148. data/lib/mail/core_extensions/object.rb +0 -13
  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/multibyte/exceptions.rb +0 -8
  152. data/lib/mail/parsers/ragel/common.rl +0 -184
  153. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  155. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  157. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  159. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  161. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  163. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  165. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2129
  167. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  169. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  171. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  173. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  174. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  176. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  177. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  178. data/lib/mail/parsers/ragel/ruby.rb +0 -39
  179. data/lib/mail/parsers/ragel.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b36c7dad6398232438e90270f9814eb04aeda138
4
- data.tar.gz: 7dbe4c27bd7cab8569892fc5d6a676718aaf755b
2
+ SHA256:
3
+ metadata.gz: 2e806bc9b7c6c6df8de9c9dc80b4cf3a099bad3153f568d0ee4d9287cd34a014
4
+ data.tar.gz: 182ed03957858cce70691eb79543f13d53e4ddf43684bf50bc011e904d3a84ae
5
5
  SHA512:
6
- metadata.gz: 0628c04e7acf3cf0c3a801aff33a6008fe338ebd9a96a1d2b290ffbd98af1c287c0bdd958eda0ec77f495f498880dd69aaf8325da77e2f7d6ddb9f50396b1635
7
- data.tar.gz: 1d813d0f8b64feb0a5a0561e7d031d795b54bf3d0160c02fcd35a9f11e04875dd6ea05a67b5a565b3d67486ce8941a14ac2d1362a288032eb3863641c533fece
6
+ metadata.gz: 3c42702e6529565ae63c486fef492b89a19eb394fb6fe5aaf004862257ef3d840579a3cabb95dd2075ddbc617e077110684d8f47052e51d0435b94ef4a2373c3
7
+ data.tar.gz: 31278e7d48271cfec8541d1094fedd1cc62578a6d9f18e0eafd09ebcac4e5edf3e78a3b902aca3e97d7416d37b0f0c07ba2dde2f5462d6317058552bbc0dbfe7
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2013 Mikel Lindsaar
1
+ Copyright (c) 2009-2016 Mikel Lindsaar
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
- Mail [![Build Status](https://travis-ci.org/mikel/mail.png?branch=master)](https://travis-ci.org/mikel/mail)
2
- ====
1
+ # Mail [![Build Status](https://travis-ci.org/mikel/mail.png?branch=master)](https://travis-ci.org/mikel/mail)
3
2
 
4
- Introduction
5
- ------------
3
+ ## Introduction
6
4
 
7
5
  Mail is an internet library for Ruby that is designed to handle emails
8
6
  generation, parsing and sending in a simple, rubyesque manner.
@@ -15,7 +13,7 @@ Built from my experience with TMail, it is designed to be a pure ruby
15
13
  implementation that makes generating, sending and parsing emails a no
16
14
  brainer.
17
15
 
18
- It is also designed form the ground up to work with the more modern versions
16
+ It is also designed from the ground up to work with the more modern versions
19
17
  of Ruby. This is because Ruby > 1.9 handles text encodings much more wonderfully
20
18
  than Ruby 1.8.x and so these features have been taken full advantage of in this
21
19
  library allowing Mail to handle a lot more messages more cleanly than TMail.
@@ -25,8 +23,7 @@ Finally, Mail has been designed with a very simple object oriented system
25
23
  that really opens up the email messages you are parsing, if you know what
26
24
  you are doing, you can fiddle with every last bit of your email directly.
27
25
 
28
- Donations
29
- -------------
26
+ ## Donations
30
27
 
31
28
  Mail has been downloaded millions of times, by people around the world, in fact,
32
29
  it represents more than 1% of *all* gems downloaded.
@@ -38,52 +35,48 @@ me a nice email :)
38
35
 
39
36
  <a href='http://www.pledgie.com/campaigns/8790'><img alt='Click here to lend your support to: mail and make a donation at www.pledgie.com !' src='http://www.pledgie.com/campaigns/8790.png?skin_name=chrome' border='0' /></a>
40
37
 
38
+ # Contents
39
+ * [Compatibility](#compatibility)
40
+ * [Discussion](#discussion)
41
+ * [Current Capabilities of Mail](#current-capabilities-of-mail)
42
+ * [Roadmap](#roadmap)
43
+ * [Testing Policy](#testing-policy)
44
+ * [API Policy](#api-policy)
45
+ * [Installation](#installation)
46
+ * [Encodings](#encodings)
47
+ * [Contributing](#contributing)
48
+ * [Usage](#usage)
49
+ * [Core Extensions](#core-extensions)
50
+ * [Excerpts from TREC Span Corpus 2005](#excerpts-from-trec-span-corpus-2005)
51
+ * [License](#license)
41
52
 
42
- Compatibility
43
- -------------
53
+ ## Compatibility
44
54
 
45
- Every Mail commit is tested by Travis on the [following platforms](https://github.com/mikel/mail/blob/master/.travis.yml)
55
+ Mail supports Ruby 1.8.7+, including JRuby and Rubinius.
46
56
 
47
- * ruby-1.8.7-p374 [ i686 ]
48
- * ruby-1.9.2-p320 [ x86_64 ]
49
- * ruby-1.9.3-p327 [ x86_64 ]
50
- * ruby-2.0.0-p451 [ x86_64 ]
51
- * ruby-2.1.2 [ x86_64 ]
52
- * ruby-head [ x86_64 ]
53
- * jruby [ x86_64 ]
54
- * jruby-head [ x86_64 ]
55
- * rbx-2 [ x86_64 ]
57
+ Every Mail commit is tested by Travis on [all supported Ruby versions](https://github.com/mikel/mail/blob/master/.travis.yml).
56
58
 
57
- Testing a specific mime type (needed for 1.8.7 for example) can be done manually with:
58
-
59
- ```sh
60
- BUNDLE_GEMFILE=gemfiles/mime_types_1.16.gemfile (bundle check || bundle) && rake
61
- ```
62
-
63
- Discussion
64
- ----------
59
+ ## Discussion
65
60
 
66
61
  If you want to discuss mail with like minded individuals, please subscribe to
67
62
  the [Google Group](http://groups.google.com/group/mail-ruby).
68
63
 
69
- Current Capabilities of Mail
70
- ----------------------------
64
+ ## Current Capabilities of Mail
71
65
 
72
- * RFC2822 Support, Reading and Writing
66
+ * RFC5322 Support, Reading and Writing
67
+ * RFC6532 Support, reading UTF-8 headers
73
68
  * RFC2045-2049 Support for multipart emails
74
69
  * Support for creating multipart alternate emails
75
70
  * Support for reading multipart/report emails &amp; getting details from such
76
- * Support for multibyte emails - needs quite a lot of work and testing
77
71
  * Wrappers for File, Net/POP3, Net/SMTP
78
- * Auto encoding of non US-ASCII header fields
79
- * Auto encoding of non US-ASCII bodies
72
+ * Auto-encoding of non-US-ASCII bodies and header fields
80
73
 
81
- Mail is RFC2822 compliant now, that is, it can parse and generate valid US-ASCII
82
- emails. There are a few obsoleted syntax emails that it will have problems with, but
83
- it also is quite robust, meaning, if it finds something it doesn't understand it will
84
- not crash, instead, it will skip the problem and keep parsing. In the case of a header
85
- it doesn't understand, it will initialise the header as an optional unstructured
86
- field and continue parsing.
74
+ Mail is RFC5322 and RFC6532 compliant now, that is, it can parse US-ASCII and UTF-8
75
+ emails and generate US-ASCII emails. There are a few obsoleted syntax emails that
76
+ it will have problems with, but it also is quite robust, meaning, if it finds something
77
+ it doesn't understand it will not crash, instead, it will skip the problem and keep
78
+ parsing. In the case of a header it doesn't understand, it will initialise the header
79
+ as an optional unstructured field and continue parsing.
87
80
 
88
81
  This means Mail won't (ever) crunch your data (I think).
89
82
 
@@ -91,16 +84,14 @@ You can also create MIME emails. There are helper methods for making a
91
84
  multipart/alternate email for text/plain and text/html (the most common pair)
92
85
  and you can manually create any other type of MIME email.
93
86
 
94
- Roadmap
95
- -------
87
+ ## Roadmap
96
88
 
97
89
  Next TODO:
98
90
 
99
91
  * Improve MIME support for character sets in headers, currently works, mostly, needs
100
92
  refinement.
101
93
 
102
- Testing Policy
103
- --------------
94
+ ## Testing Policy
104
95
 
105
96
  Basically... we do BDD on Mail. No method gets written in Mail without a
106
97
  corresponding or covering spec. We expect as a minimum 100% coverage
@@ -110,23 +101,22 @@ the gem gets released.
110
101
 
111
102
  It also means you can be sure Mail will behave correctly.
112
103
 
113
- API Policy
114
- ----------
104
+ Note: If you care about core extensions (aka "monkey-patching"), please read the Core Extensions section near the end of this README.
105
+
106
+ ## API Policy
115
107
 
116
108
  No API removals within a single point release. All removals to be deprecated with
117
109
  warnings for at least one MINOR point release before removal.
118
110
 
119
111
  Also, all private or protected methods to be declared as such - though this is still I/P.
120
112
 
121
- Installation
122
- ------------
113
+ ## Installation
123
114
 
124
115
  Installation is fairly simple, I host mail on rubygems, so you can just do:
125
116
 
126
117
  # gem install mail
127
118
 
128
- Encodings
129
- ---------
119
+ ## Encodings
130
120
 
131
121
  If you didn't know, handling encodings in Emails is not as straight forward as you
132
122
  would hope.
@@ -158,13 +148,11 @@ I have tried to simplify it some:
158
148
  provide encoded parameter values when you call the parameter names through the
159
149
  <code>object.parameters['<parameter_name>']</code> method call.
160
150
 
161
- Contributing
162
- ------------
151
+ ## Contributing
163
152
 
164
153
  Please do! Contributing is easy in Mail. Please read the CONTRIBUTING.md document for more info
165
154
 
166
- Usage
167
- -----
155
+ ## Usage
168
156
 
169
157
  All major mail functions should be able to happen from the Mail module.
170
158
  So, you should be able to just <code>require 'mail'</code> to get started.
@@ -232,7 +220,7 @@ what you are doing.
232
220
  ### Sending an email:
233
221
 
234
222
  Mail defaults to sending via SMTP to local host port 25. If you have a
235
- sendmail or postfix daemon running on on this port, sending email is as
223
+ sendmail or postfix daemon running on this port, sending email is as
236
224
  easy as:
237
225
 
238
226
  ```ruby
@@ -292,7 +280,17 @@ mail.delivery_method :exim, :location => "/usr/bin/exim"
292
280
  mail.deliver
293
281
  ```
294
282
 
295
- ### Getting emails from a pop server:
283
+ Mail may be "delivered" to a logfile, too, for development and testing:
284
+
285
+ ```ruby
286
+ # Delivers by logging the encoded message to $stdout
287
+ mail.delivery_method :logger
288
+
289
+ # Delivers to an existing logger at :debug severity
290
+ mail.delivery_method :logger, logger: other_logger, severity: :debug
291
+ ```
292
+
293
+ ### Getting Emails from a POP Server:
296
294
 
297
295
  You can configure Mail to receive email using <code>retriever_method</code>
298
296
  within <code>Mail.defaults</code>:
@@ -345,7 +343,7 @@ mail.cc #=> 'sam@test.lindsaar.net'
345
343
  mail.subject #=> "This is the subject"
346
344
  mail.date.to_s #=> '21 Nov 1997 09:55:06 -0600'
347
345
  mail.message_id #=> '<4D6AA7EB.6490534@xxx.xxx>'
348
- mail.body.decoded #=> 'This is the body of the email...
346
+ mail.decoded #=> 'This is the body of the email...
349
347
  ```
350
348
 
351
349
  Many more methods available.
@@ -371,7 +369,7 @@ is another message which can have many or no parts.
371
369
  A message will only have parts if it is a multipart/mixed or multipart/related
372
370
  content type and has a boundary defined.
373
371
 
374
- ### Testing and extracting attachments
372
+ ### Testing and Extracting Attachments
375
373
  ```ruby
376
374
  mail.attachments.each do | attachment |
377
375
  # Attachments is an AttachmentsList object containing a
@@ -380,14 +378,14 @@ mail.attachments.each do | attachment |
380
378
  # extracting images for example...
381
379
  filename = attachment.filename
382
380
  begin
383
- File.open(images_dir + filename, "w+b", 0644) {|f| f.write attachment.body.decoded}
381
+ File.open(images_dir + filename, "w+b", 0644) {|f| f.write attachment.decoded}
384
382
  rescue => e
385
383
  puts "Unable to save data for #{filename} because #{e.message}"
386
384
  end
387
385
  end
388
386
  end
389
387
  ```
390
- ### Writing and sending a multipart/alternative (html and text) email
388
+ ### Writing and Sending a Multipart/Alternative (HTML and Text) Email
391
389
 
392
390
  Mail makes some basic assumptions and makes doing the common thing as
393
391
  simple as possible.... (asking a lot from a mail library)
@@ -453,7 +451,7 @@ Mail assumes that if your text in the body is only us-ascii, that your
453
451
  transfer encoding is 7bit and it is text/plain. You can override this
454
452
  by explicitly declaring it.
455
453
 
456
- ### Making Multipart/Alternate, without a block
454
+ ### Making Multipart/Alternate, Without a Block
457
455
 
458
456
  You don't have to use a block with the text and html part included, you
459
457
  can just do it declaratively. However, you need to add Mail::Parts to
@@ -481,7 +479,7 @@ mail.html_part = html_part
481
479
 
482
480
  Results in the same email as done using the block form
483
481
 
484
- ### Getting error reports from an email:
482
+ ### Getting Error Reports from an Email:
485
483
 
486
484
  ```ruby
487
485
  @mail = Mail.read('/path/to/bounce_message.eml')
@@ -525,7 +523,6 @@ than mail (this should be rarely needed)
525
523
 
526
524
  ```ruby
527
525
  @mail = Mail.new
528
- file_data = File.read('path/to/myfile.pdf')
529
526
  @mail.attachments['myfile.pdf'] = { :mime_type => 'application/x-pdf',
530
527
  :content => File.read('path/to/myfile.pdf') }
531
528
  @mail.parts.first.mime_type #=> 'application/x-pdf'
@@ -558,8 +555,7 @@ end
558
555
  ```
559
556
  See "Testing and extracting attachments" above for more details.
560
557
 
561
- Using Mail with Testing or Spec'ing Libraries
562
- ---------------------------------------------
558
+ ## Using Mail with Testing or Spec'ing Libraries
563
559
 
564
560
  If mail is part of your system, you'll need a way to test it without actually
565
561
  sending emails, the TestMailer can do this for you.
@@ -588,7 +584,7 @@ Mail::TestMailer.deliveries.clear
588
584
  => []
589
585
  ```
590
586
 
591
- There is also a set of RSpec matchers stolen fr^H^H^H^H^H^H^H^H inspired by Shoulda's ActionMailer matchers (you'll want to set <code>delivery_method</code> as above too):
587
+ There is also a set of RSpec matchers stolen/inspired by Shoulda's ActionMailer matchers (you'll want to set <code>delivery_method</code> as above too):
592
588
 
593
589
  ```ruby
594
590
  Mail.defaults do
@@ -609,36 +605,53 @@ describe "sending an email" do
609
605
  end
610
606
  end
611
607
 
612
- it { should have_sent_email } # passes if any email at all was sent
608
+ it { is_expected.to have_sent_email } # passes if any email at all was sent
613
609
 
614
- it { should have_sent_email.from('you@you.com') }
615
- it { should have_sent_email.to('mike1@me.com') }
610
+ it { is_expected.to have_sent_email.from('you@you.com') }
611
+ it { is_expected.to have_sent_email.to('mike1@me.com') }
616
612
 
617
613
  # can specify a list of recipients...
618
- it { should have_sent_email.to(['mike1@me.com', 'mike2@me.com']) }
614
+ it { is_expected.to have_sent_email.to(['mike1@me.com', 'mike2@me.com']) }
619
615
 
620
616
  # ...or chain recipients together
621
- it { should have_sent_email.to('mike1@me.com').to('mike2@me.com') }
617
+ it { is_expected.to have_sent_email.to('mike1@me.com').to('mike2@me.com') }
622
618
 
623
- it { should have_sent_email.with_subject('testing') }
619
+ it { is_expected.to have_sent_email.with_subject('testing') }
624
620
 
625
- it { should have_sent_email.with_body('hello') }
621
+ it { is_expected.to have_sent_email.with_body('hello') }
626
622
 
627
623
  # Can match subject or body with a regex
628
624
  # (or anything that responds_to? :match)
629
625
 
630
- it { should have_sent_email.matching_subject(/test(ing)?/) }
631
- it { should have_sent_email.matching_body(/h(a|e)llo/) }
626
+ it { is_expected.to have_sent_email.matching_subject(/test(ing)?/) }
627
+ it { is_expected.to have_sent_email.matching_body(/h(a|e)llo/) }
632
628
 
633
629
  # Can chain together modifiers
634
630
  # Note that apart from recipients, repeating a modifier overwrites old value.
635
631
 
636
- it { should have_sent_email.from('you@you.com').to('mike1@me.com').matching_body(/hell/)
632
+ it { is_expected.to have_sent_email.from('you@you.com').to('mike1@me.com').matching_body(/hell/)
633
+
634
+ # test for attachments
635
+
636
+ # ... by specific attachment
637
+ it { is_expected.to have_sent_email.with_attachments(my_attachment) }
638
+
639
+ # ... or any attachment
640
+ it { is_expected.to have_sent_email.with_attachments(any_attachment) }
641
+
642
+ # ... by array of attachments
643
+ it { is_expected.to have_sent_email.with_attachments([my_attachment1, my_attachment2]) } #note that order is important
644
+
645
+ #... by presence
646
+ it { is_expected.to have_sent_email.with_any_attachments }
647
+
648
+ #... or by absence
649
+ it { is_expected.to have_sent_email.with_no_attachments }
650
+
637
651
  end
638
652
  ```
639
653
 
640
- Excerpts from TREC Spam Corpus 2005
641
- -----------------------------------
654
+ ## Excerpts from TREC Spam Corpus 2005
642
655
 
643
656
  The spec fixture files in spec/fixtures/emails/from_trec_2005 are from the
644
657
  2005 TREC Public Spam Corpus. They remain copyrighted under the terms of
@@ -656,12 +669,11 @@ They are used as allowed by 'Permitted Uses, Clause 3':
656
669
 
657
670
  -- http://plg.uwaterloo.ca/~gvcormac/treccorpus/
658
671
 
659
- License
660
- -------
672
+ ## License
661
673
 
662
674
  (The MIT License)
663
675
 
664
- Copyright (c) 2009-2013 Mikel Lindsaar
676
+ Copyright (c) 2009-2016 Mikel Lindsaar
665
677
 
666
678
  Permission is hereby granted, free of charge, to any person obtaining
667
679
  a copy of this software and associated documentation files (the
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Mail
2
3
  class AttachmentsList < Array
3
4
 
@@ -5,8 +6,8 @@ module Mail
5
6
  @parts_list = parts_list
6
7
  @content_disposition_type = 'attachment'
7
8
  parts_list.map { |p|
8
- if p.content_type == "message/rfc822"
9
- Mail.new(p.body).attachments
9
+ if p.mime_type == 'message/rfc822'
10
+ Mail.new(p.body.encoded).attachments
10
11
  elsif p.parts.empty?
11
12
  p if p.attachment?
12
13
  else
@@ -29,7 +30,7 @@ module Mail
29
30
  # mail.attachments['test.png'].filename #=> 'test.png'
30
31
  # mail.attachments[1].filename #=> 'test.jpg'
31
32
  def [](index_value)
32
- if index_value.is_a?(Fixnum)
33
+ if index_value.is_a?(Integer)
33
34
  self.fetch(index_value)
34
35
  else
35
36
  self.select { |a| a.filename == index_value }.first
@@ -43,6 +44,9 @@ module Mail
43
44
  :content_disposition => "#{@content_disposition_type}; filename=\"#{encoded_name}\"" }
44
45
 
45
46
  if value.is_a?(Hash)
47
+ if path = value.delete(:filename)
48
+ value[:content] ||= File.open(path, 'rb') { |f| f.read }
49
+ end
46
50
 
47
51
  default_values[:body] = value.delete(:content) if value[:content]
48
52
 
@@ -59,7 +63,7 @@ module Mail
59
63
 
60
64
  if value[:mime_type]
61
65
  default_values[:content_type] = value.delete(:mime_type)
62
- @mime_type = MIME::Types[default_values[:content_type]].first
66
+ @mime_type = MiniMime.lookup_by_content_type(default_values[:content_type])
63
67
  default_values[:content_transfer_encoding] ||= guess_encoding
64
68
  end
65
69
 
@@ -71,6 +75,7 @@ module Mail
71
75
 
72
76
  if hash[:body].respond_to? :force_encoding and hash[:body].respond_to? :valid_encoding?
73
77
  if not hash[:body].valid_encoding? and default_values[:content_transfer_encoding].downcase == "binary"
78
+ hash[:body] = hash[:body].dup if hash[:body].frozen?
74
79
  hash[:body].force_encoding("BINARY")
75
80
  end
76
81
  end
@@ -97,7 +102,8 @@ module Mail
97
102
  filename = filename.encode(Encoding::UTF_8) if filename.respond_to?(:encode)
98
103
  end
99
104
 
100
- @mime_type = MIME::Types.type_for(filename).first
105
+ @mime_type = MiniMime.lookup_by_filename(filename)
106
+ @mime_type && @mime_type.content_type
101
107
  end
102
108
 
103
109
  end
data/lib/mail/body.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # = Body
@@ -31,24 +32,24 @@ module Mail
31
32
  @preamble = nil
32
33
  @epilogue = nil
33
34
  @charset = nil
34
- @part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
35
+ @part_sort_order = [ "text/plain", "text/enriched", "text/html", "multipart/alternative" ]
35
36
  @parts = Mail::PartsList.new
36
- if string.blank?
37
+ if Utilities.blank?(string)
37
38
  @raw_source = ''
38
39
  else
39
40
  # Do join first incase we have been given an Array in Ruby 1.9
40
41
  if string.respond_to?(:join)
41
- @raw_source = string.join('')
42
+ @raw_source = ::Mail::Utilities.to_crlf(string.join(''))
42
43
  elsif string.respond_to?(:to_s)
43
- @raw_source = string.to_s
44
+ @raw_source = ::Mail::Utilities.to_crlf(string.to_s)
44
45
  else
45
46
  raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
46
47
  end
47
48
  end
48
- @encoding = (only_us_ascii? ? '7bit' : '8bit')
49
+ @encoding = default_encoding
49
50
  set_charset
50
51
  end
51
-
52
+
52
53
  # Matches this body with another body. Also matches the decoded value of this
53
54
  # body with a string.
54
55
  #
@@ -114,8 +115,8 @@ module Mail
114
115
  end
115
116
 
116
117
  # Allows you to set the sort order of the parts, overriding the default sort order.
117
- # Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content
118
- # type coming after.
118
+ # Defaults to 'text/plain', then 'text/enriched', then 'text/html', then 'multipart/alternative'
119
+ # with any other content type coming after.
119
120
  def set_sort_order(order)
120
121
  @part_sort_order = order
121
122
  end
@@ -137,41 +138,45 @@ module Mail
137
138
  def raw_source
138
139
  @raw_source
139
140
  end
140
-
141
- def get_best_encoding(target)
142
- target_encoding = Mail::Encodings.get_encoding(target)
143
- target_encoding.get_best_compatible(encoding, raw_source)
141
+
142
+ def negotiate_best_encoding(message_encoding, allowed_encodings = nil)
143
+ Mail::Encodings::TransferEncoding.negotiate(message_encoding, encoding, raw_source, allowed_encodings)
144
144
  end
145
-
145
+
146
146
  # Returns a body encoded using transfer_encoding. Multipart always uses an
147
147
  # identiy encoding (i.e. no encoding).
148
148
  # Calling this directly is not a good idea, but supported for compatibility
149
149
  # TODO: Validate that preamble and epilogue are valid for requested encoding
150
- def encoded(transfer_encoding = '8bit')
150
+ def encoded(transfer_encoding = nil)
151
151
  if multipart?
152
152
  self.sort_parts!
153
153
  encoded_parts = parts.map { |p| p.encoded }
154
154
  ([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
155
155
  else
156
- be = get_best_encoding(transfer_encoding)
157
- dec = Mail::Encodings::get_encoding(encoding)
158
- enc = Mail::Encodings::get_encoding(be)
159
- if transfer_encoding == encoding and dec.nil?
160
- # Cannot decode, so skip normalization
161
- raw_source
156
+ dec = Mail::Encodings.get_encoding(encoding)
157
+ enc =
158
+ if Utilities.blank?(transfer_encoding)
159
+ dec
160
+ else
161
+ negotiate_best_encoding(transfer_encoding)
162
+ end
163
+
164
+ if dec.nil?
165
+ # Cannot decode, so skip normalization
166
+ raw_source
162
167
  else
163
- # Decode then encode to normalize and allow transforming
164
- # from base64 to Q-P and vice versa
165
- decoded = dec.decode(raw_source)
166
- if defined?(Encoding) && charset && charset != "US-ASCII"
167
- decoded.encode!(charset)
168
- decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
169
- end
170
- enc.encode(decoded)
168
+ # Decode then encode to normalize and allow transforming
169
+ # from base64 to Q-P and vice versa
170
+ decoded = dec.decode(raw_source)
171
+ if defined?(Encoding) && charset && charset != "US-ASCII"
172
+ decoded = decoded.encode(charset)
173
+ decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
174
+ end
175
+ enc.encode(decoded)
171
176
  end
172
177
  end
173
178
  end
174
-
179
+
175
180
  def decoded
176
181
  if !Encodings.defined?(encoding)
177
182
  raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
@@ -199,13 +204,14 @@ module Mail
199
204
  @encoding
200
205
  end
201
206
  end
202
-
207
+
203
208
  def encoding=( val )
204
- @encoding = if val == "text" || val.blank?
205
- (only_us_ascii? ? '7bit' : '8bit')
206
- else
209
+ @encoding =
210
+ if val == "text" || Utilities.blank?(val)
211
+ default_encoding
212
+ else
207
213
  val
208
- end
214
+ end
209
215
  end
210
216
 
211
217
  # Returns the preamble (any text that is before the first MIME boundary)
@@ -254,27 +260,58 @@ module Mail
254
260
  @parts = Mail::PartsList.new[val]
255
261
  end
256
262
  end
257
-
263
+
258
264
  def split!(boundary)
259
265
  self.boundary = boundary
260
- parts = raw_source.split(/(?:\A|\r\n)--#{Regexp.escape(boundary || "")}(?=(?:--)?\s*$)/)
266
+ parts = extract_parts
267
+
261
268
  # Make the preamble equal to the preamble (if any)
262
269
  self.preamble = parts[0].to_s.strip
263
270
  # Make the epilogue equal to the epilogue (if any)
264
- self.epilogue = parts[-1].to_s.sub('--', '').strip
271
+ self.epilogue = parts[-1].to_s.strip
265
272
  parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
266
273
  self
267
274
  end
268
-
269
- def only_us_ascii?
270
- !(raw_source =~ /[^\x01-\x7f]/)
275
+
276
+ def ascii_only?
277
+ unless defined? @ascii_only
278
+ @ascii_only = raw_source.ascii_only?
279
+ end
280
+ @ascii_only
271
281
  end
272
-
282
+
273
283
  def empty?
274
284
  !!raw_source.to_s.empty?
275
285
  end
276
-
286
+
287
+ def default_encoding
288
+ ascii_only? ? '7bit' : '8bit'
289
+ end
290
+
277
291
  private
292
+
293
+ # split parts by boundary, ignore first part if empty, append final part when closing boundary was missing
294
+ def extract_parts
295
+ parts_regex = /
296
+ (?: # non-capturing group
297
+ \A | # start of string OR
298
+ \r\n # line break
299
+ )
300
+ (
301
+ --#{Regexp.escape(boundary || "")} # boundary delimiter
302
+ (?:--)? # with non-capturing optional closing
303
+ )
304
+ (?=\s*$) # lookahead matching zero or more spaces followed by line-ending
305
+ /x
306
+ parts = raw_source.split(parts_regex).each_slice(2).to_a
307
+ parts.each_with_index { |(part, _), index| parts.delete_at(index) if index > 0 && Utilities.blank?(part) }
308
+
309
+ if parts.size > 1
310
+ final_separator = parts[-2][1]
311
+ parts << [""] if final_separator != "--#{boundary}--"
312
+ end
313
+ parts.map(&:first)
314
+ end
278
315
 
279
316
  def crlf_boundary
280
317
  "\r\n--#{boundary}\r\n"
@@ -283,9 +320,9 @@ module Mail
283
320
  def end_boundary
284
321
  "\r\n--#{boundary}--\r\n"
285
322
  end
286
-
323
+
287
324
  def set_charset
288
- only_us_ascii? ? @charset = 'US-ASCII' : @charset = nil
325
+ @charset = ascii_only? ? 'US-ASCII' : nil
289
326
  end
290
327
  end
291
328
  end