kbaum-mail 2.1.2.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 (114) hide show
  1. data/CHANGELOG.rdoc +289 -0
  2. data/README.rdoc +575 -0
  3. data/Rakefile +72 -0
  4. data/TODO.rdoc +19 -0
  5. data/lib/mail.rb +113 -0
  6. data/lib/mail/attachments_list.rb +76 -0
  7. data/lib/mail/body.rb +243 -0
  8. data/lib/mail/configuration.rb +69 -0
  9. data/lib/mail/core_extensions/nil.rb +11 -0
  10. data/lib/mail/core_extensions/string.rb +19 -0
  11. data/lib/mail/elements/address.rb +306 -0
  12. data/lib/mail/elements/address_list.rb +74 -0
  13. data/lib/mail/elements/content_disposition_element.rb +30 -0
  14. data/lib/mail/elements/content_location_element.rb +25 -0
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +21 -0
  16. data/lib/mail/elements/content_type_element.rb +35 -0
  17. data/lib/mail/elements/date_time_element.rb +26 -0
  18. data/lib/mail/elements/envelope_from_element.rb +34 -0
  19. data/lib/mail/elements/message_ids_element.rb +29 -0
  20. data/lib/mail/elements/mime_version_element.rb +26 -0
  21. data/lib/mail/elements/phrase_list.rb +21 -0
  22. data/lib/mail/elements/received_element.rb +30 -0
  23. data/lib/mail/encodings/base64.rb +18 -0
  24. data/lib/mail/encodings/encodings.rb +201 -0
  25. data/lib/mail/encodings/quoted_printable.rb +26 -0
  26. data/lib/mail/envelope.rb +35 -0
  27. data/lib/mail/field.rb +219 -0
  28. data/lib/mail/field_list.rb +33 -0
  29. data/lib/mail/fields/bcc_field.rb +53 -0
  30. data/lib/mail/fields/cc_field.rb +52 -0
  31. data/lib/mail/fields/comments_field.rb +41 -0
  32. data/lib/mail/fields/common/address_container.rb +16 -0
  33. data/lib/mail/fields/common/common_address.rb +128 -0
  34. data/lib/mail/fields/common/common_date.rb +51 -0
  35. data/lib/mail/fields/common/common_field.rb +64 -0
  36. data/lib/mail/fields/common/common_message_id.rb +57 -0
  37. data/lib/mail/fields/common/parameter_hash.rb +39 -0
  38. data/lib/mail/fields/content_description_field.rb +19 -0
  39. data/lib/mail/fields/content_disposition_field.rb +60 -0
  40. data/lib/mail/fields/content_id_field.rb +63 -0
  41. data/lib/mail/fields/content_location_field.rb +42 -0
  42. data/lib/mail/fields/content_transfer_encoding_field.rb +45 -0
  43. data/lib/mail/fields/content_type_field.rb +175 -0
  44. data/lib/mail/fields/date_field.rb +53 -0
  45. data/lib/mail/fields/from_field.rb +53 -0
  46. data/lib/mail/fields/in_reply_to_field.rb +52 -0
  47. data/lib/mail/fields/keywords_field.rb +43 -0
  48. data/lib/mail/fields/message_id_field.rb +80 -0
  49. data/lib/mail/fields/mime_version_field.rb +54 -0
  50. data/lib/mail/fields/optional_field.rb +11 -0
  51. data/lib/mail/fields/received_field.rb +62 -0
  52. data/lib/mail/fields/references_field.rb +53 -0
  53. data/lib/mail/fields/reply_to_field.rb +53 -0
  54. data/lib/mail/fields/resent_bcc_field.rb +53 -0
  55. data/lib/mail/fields/resent_cc_field.rb +53 -0
  56. data/lib/mail/fields/resent_date_field.rb +33 -0
  57. data/lib/mail/fields/resent_from_field.rb +53 -0
  58. data/lib/mail/fields/resent_message_id_field.rb +32 -0
  59. data/lib/mail/fields/resent_sender_field.rb +60 -0
  60. data/lib/mail/fields/resent_to_field.rb +53 -0
  61. data/lib/mail/fields/return_path_field.rb +62 -0
  62. data/lib/mail/fields/sender_field.rb +65 -0
  63. data/lib/mail/fields/structured_field.rb +36 -0
  64. data/lib/mail/fields/subject_field.rb +15 -0
  65. data/lib/mail/fields/to_field.rb +53 -0
  66. data/lib/mail/fields/unstructured_field.rb +117 -0
  67. data/lib/mail/header.rb +235 -0
  68. data/lib/mail/mail.rb +194 -0
  69. data/lib/mail/message.rb +1780 -0
  70. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  71. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  72. data/lib/mail/network/delivery_methods/smtp.rb +110 -0
  73. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  74. data/lib/mail/network/retriever_methods/imap.rb +31 -0
  75. data/lib/mail/network/retriever_methods/pop3.rb +149 -0
  76. data/lib/mail/parsers/address_lists.rb +61 -0
  77. data/lib/mail/parsers/address_lists.treetop +19 -0
  78. data/lib/mail/parsers/content_disposition.rb +369 -0
  79. data/lib/mail/parsers/content_disposition.treetop +46 -0
  80. data/lib/mail/parsers/content_location.rb +133 -0
  81. data/lib/mail/parsers/content_location.treetop +20 -0
  82. data/lib/mail/parsers/content_transfer_encoding.rb +179 -0
  83. data/lib/mail/parsers/content_transfer_encoding.treetop +25 -0
  84. data/lib/mail/parsers/content_type.rb +512 -0
  85. data/lib/mail/parsers/content_type.treetop +58 -0
  86. data/lib/mail/parsers/date_time.rb +111 -0
  87. data/lib/mail/parsers/date_time.treetop +11 -0
  88. data/lib/mail/parsers/envelope_from.rb +188 -0
  89. data/lib/mail/parsers/envelope_from.treetop +32 -0
  90. data/lib/mail/parsers/message_ids.rb +42 -0
  91. data/lib/mail/parsers/message_ids.treetop +15 -0
  92. data/lib/mail/parsers/mime_version.rb +141 -0
  93. data/lib/mail/parsers/mime_version.treetop +19 -0
  94. data/lib/mail/parsers/phrase_lists.rb +42 -0
  95. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  96. data/lib/mail/parsers/received.rb +68 -0
  97. data/lib/mail/parsers/received.treetop +11 -0
  98. data/lib/mail/parsers/rfc2045.rb +406 -0
  99. data/lib/mail/parsers/rfc2045.treetop +35 -0
  100. data/lib/mail/parsers/rfc2822.rb +5081 -0
  101. data/lib/mail/parsers/rfc2822.treetop +410 -0
  102. data/lib/mail/parsers/rfc2822_obsolete.rb +3607 -0
  103. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  104. data/lib/mail/part.rb +82 -0
  105. data/lib/mail/parts_list.rb +34 -0
  106. data/lib/mail/patterns.rb +43 -0
  107. data/lib/mail/utilities.rb +163 -0
  108. data/lib/mail/vendor/treetop.rb +4 -0
  109. data/lib/mail/version.rb +10 -0
  110. data/lib/mail/version_specific/ruby_1_8.rb +84 -0
  111. data/lib/mail/version_specific/ruby_1_9.rb +77 -0
  112. data/lib/tasks/corpus.rake +125 -0
  113. data/lib/tasks/treetop.rake +10 -0
  114. metadata +188 -0
@@ -0,0 +1,235 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+
4
+ # Provides access to a header object.
5
+ #
6
+ # ===Per RFC2822
7
+ #
8
+ # 2.2. Header Fields
9
+ #
10
+ # Header fields are lines composed of a field name, followed by a colon
11
+ # (":"), followed by a field body, and terminated by CRLF. A field
12
+ # name MUST be composed of printable US-ASCII characters (i.e.,
13
+ # characters that have values between 33 and 126, inclusive), except
14
+ # colon. A field body may be composed of any US-ASCII characters,
15
+ # except for CR and LF. However, a field body may contain CRLF when
16
+ # used in header "folding" and "unfolding" as described in section
17
+ # 2.2.3. All field bodies MUST conform to the syntax described in
18
+ # sections 3 and 4 of this standard.
19
+ class Header
20
+ include Patterns
21
+ include Utilities
22
+ include Enumerable
23
+
24
+ # Creates a new header object.
25
+ #
26
+ # Accepts raw text or nothing. If given raw text will attempt to parse
27
+ # it and split it into the various fields, instantiating each field as
28
+ # it goes.
29
+ #
30
+ # If it finds a field that should be a structured field (such as content
31
+ # type), but it fails to parse it, it will simply make it an unstructured
32
+ # field and leave it alone. This will mean that the data is preserved but
33
+ # no automatic processing of that field will happen. If you find one of
34
+ # these cases, please make a patch and send it in, or at the least, send
35
+ # me the example so we can fix it.
36
+ def initialize(header_text = nil)
37
+ self.raw_source = header_text.to_crlf
38
+ split_header if header_text
39
+ end
40
+
41
+ # The preserved raw source of the header as you passed it in, untouched
42
+ # for your Regexing glory.
43
+ def raw_source
44
+ @raw_source
45
+ end
46
+
47
+ # Returns an array of all the fields in the header in order that they
48
+ # were read in.
49
+ def fields
50
+ @fields ||= FieldList.new
51
+ end
52
+
53
+ # 3.6. Field definitions
54
+ #
55
+ # It is important to note that the header fields are not guaranteed to
56
+ # be in a particular order. They may appear in any order, and they
57
+ # have been known to be reordered occasionally when transported over
58
+ # the Internet. However, for the purposes of this standard, header
59
+ # fields SHOULD NOT be reordered when a message is transported or
60
+ # transformed. More importantly, the trace header fields and resent
61
+ # header fields MUST NOT be reordered, and SHOULD be kept in blocks
62
+ # prepended to the message. See sections 3.6.6 and 3.6.7 for more
63
+ # information.
64
+ #
65
+ # Populates the fields container with Field objects in the order it
66
+ # receives them in.
67
+ #
68
+ # Acceps an array of field string values, for example:
69
+ #
70
+ # h = Header.new
71
+ # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
72
+ def fields=(unfolded_fields)
73
+ @fields = Mail::FieldList.new
74
+ unfolded_fields.each do |field|
75
+
76
+ field = Field.new(field)
77
+ selected = select_field_for(field.name)
78
+
79
+ if selected.any? && limited_field?(field.name)
80
+ selected.first.update(field.name, field.value)
81
+ else
82
+ @fields << field
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ # 3.6. Field definitions
89
+ #
90
+ # The following table indicates limits on the number of times each
91
+ # field may occur in a message header as well as any special
92
+ # limitations on the use of those fields. An asterisk next to a value
93
+ # in the minimum or maximum column indicates that a special restriction
94
+ # appears in the Notes column.
95
+ #
96
+ # <snip table from 3.6>
97
+ #
98
+ # As per RFC, many fields can appear more than once, we will return a string
99
+ # of the value if there is only one header, or if there is more than one
100
+ # matching header, will return an array of values in order that they appear
101
+ # in the header ordered from top to bottom.
102
+ #
103
+ # Example:
104
+ #
105
+ # h = Header.new
106
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
107
+ # h['To'] #=> 'mikel@me.com'
108
+ # h['X-Mail-SPAM'] #=> ['15', '20']
109
+ def [](name)
110
+ name = dasherize(name)
111
+ selected = select_field_for(name)
112
+ case
113
+ when selected.length > 1
114
+ selected.map { |f| f }
115
+ when !selected.blank?
116
+ selected.first
117
+ else
118
+ nil
119
+ end
120
+ end
121
+
122
+ # Sets the FIRST matching field in the header to passed value, or deletes
123
+ # the FIRST field matched from the header if passed nil
124
+ #
125
+ # Example:
126
+ #
127
+ # h = Header.new
128
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
129
+ # h['To'] = 'bob@you.com'
130
+ # h['To'] #=> 'bob@you.com'
131
+ # h['X-Mail-SPAM'] = '10000'
132
+ # h['X-Mail-SPAM'] # => ['15', '20', '10000']
133
+ # h['X-Mail-SPAM'] = nil
134
+ # h['X-Mail-SPAM'] # => nil
135
+ def []=(name, value)
136
+ name = dasherize(name)
137
+ selected = select_field_for(name)
138
+
139
+ case
140
+ # User wants to delete the field
141
+ when !selected.blank? && value == nil
142
+ fields.delete_if { |f| selected.include?(f) }
143
+
144
+ # User wants to change the field
145
+ when !selected.blank? && limited_field?(name)
146
+ selected.first.update(name, value)
147
+
148
+ # User wants to create the field
149
+ else
150
+ # Need to insert in correct order for trace fields
151
+ self.fields << Field.new(name.to_s, value)
152
+ end
153
+ end
154
+
155
+ LIMITED_FIELDS = %w[ orig-date from sender reply-to to cc bcc
156
+ message-id in-reply-to references subject
157
+ return-path content-type mime-version
158
+ content-transfer-encoding content-description
159
+ content-id content-disposition content-location]
160
+
161
+ def encoded
162
+ buffer = ''
163
+ fields.each do |field|
164
+ buffer << field.encoded
165
+ end
166
+ buffer
167
+ end
168
+
169
+ def to_s
170
+ encoded
171
+ end
172
+
173
+ def decoded
174
+ raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts, try calling #decoded on the various fields.'
175
+ end
176
+
177
+ # Returns true if the header has a Message-ID defined (empty or not)
178
+ def has_message_id?
179
+ !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
180
+ end
181
+
182
+ # Returns true if the header has a Content-ID defined (empty or not)
183
+ def has_content_id?
184
+ !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
185
+ end
186
+
187
+ # Returns true if the header has a Date defined (empty or not)
188
+ def has_date?
189
+ !fields.select { |f| f.responsible_for?('Date') }.empty?
190
+ end
191
+
192
+ # Returns true if the header has a message_id defined (empty or not)
193
+ def has_mime_version?
194
+ !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
195
+ end
196
+
197
+ private
198
+
199
+ def raw_source=(val)
200
+ @raw_source = val
201
+ end
202
+
203
+ # 2.2.3. Long Header Fields
204
+ #
205
+ # The process of moving from this folded multiple-line representation
206
+ # of a header field to its single line representation is called
207
+ # "unfolding". Unfolding is accomplished by simply removing any CRLF
208
+ # that is immediately followed by WSP. Each header field should be
209
+ # treated in its unfolded form for further syntactic and semantic
210
+ # evaluation.
211
+ def unfold(string)
212
+ string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
213
+ end
214
+
215
+ # Returns the header with all the folds removed
216
+ def unfolded_header
217
+ @unfolded_header ||= unfold(raw_source)
218
+ end
219
+
220
+ # Splits an unfolded and line break cleaned header into individual field
221
+ # strings.
222
+ def split_header
223
+ self.fields = unfolded_header.split(CRLF)
224
+ end
225
+
226
+ def select_field_for(name)
227
+ fields.select { |f| f.responsible_for?(name.to_s) }
228
+ end
229
+
230
+ def limited_field?(name)
231
+ LIMITED_FIELDS.include?(name.to_s.downcase)
232
+ end
233
+
234
+ end
235
+ end
@@ -0,0 +1,194 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+
4
+ # Allows you to create a new Mail::Message object.
5
+ #
6
+ # You can make an email via passing a string or passing a block.
7
+ #
8
+ # For example, the following two examples will create the same email
9
+ # message:
10
+ #
11
+ # Creating via a string:
12
+ #
13
+ # string = 'To: mikel@test.lindsaar.net\r\n'
14
+ # string << 'From: bob@test.lindsaar.net\r\n\r\n'
15
+ # string << 'Subject: This is an email\r\n'
16
+ # string << '\r\n'
17
+ # string << 'This is the body'
18
+ # Mail.new(string)
19
+ #
20
+ # Or creating via a block:
21
+ #
22
+ # message = Mail.new do
23
+ # to 'mikel@test.lindsaar.net'
24
+ # from 'bob@test.lindsaar.net'
25
+ # subject 'This is an email'
26
+ # body 'This is the body'
27
+ # end
28
+ #
29
+ # Or creating via a hash (or hash like object):
30
+ #
31
+ # message = Mail.new({:to => 'mikel@test.lindsaar.net',
32
+ # 'from' => 'bob@test.lindsaar.net',
33
+ # :subject 'This is an email',
34
+ # :body 'This is the body' })
35
+ #
36
+ # Note, the hash keys can be strings or symbols, the passed in object
37
+ # does not need to be a hash, it just needs to respond to :each_pair
38
+ # and yield each key value pair.
39
+ #
40
+ # As a side note, you can also create a new email through creating
41
+ # a Mail::Message object directly and then passing in values via string,
42
+ # symbol or direct method calls. See Mail::Message for more information.
43
+ #
44
+ # mail = Mail.new
45
+ # mail.to = 'mikel@test.lindsaar.net'
46
+ # mail[:from] = 'bob@test.lindsaar.net'
47
+ # mail['subject'] = 'This is an email'
48
+ # mail.body = 'This is the body'
49
+ def Mail.new(*args, &block)
50
+ Mail::Message.new(args, &block)
51
+ end
52
+
53
+ # Sets the default delivery method and retriever method for all new Mail objects.
54
+ # The delivery_method and retriever_method default to :smtp and :pop3, with defaults
55
+ # set.
56
+ #
57
+ # So sending a new email, if you have an SMTP server running on localhost is
58
+ # as easy as:
59
+ #
60
+ # Mail.deliver do
61
+ # to 'mikel@test.lindsaar.net'
62
+ # from 'bob@test.lindsaar.net'
63
+ # subject 'hi there!'
64
+ # body 'this is a body'
65
+ # end
66
+ #
67
+ # If you do not specify anything, you will get the following equivalent code set in
68
+ # every new mail object:
69
+ #
70
+ # Mail.defaults do
71
+ # delivery_method :smtp, { :address => "localhost",
72
+ # :port => 25,
73
+ # :domain => 'localhost.localdomain',
74
+ # :user_name => nil,
75
+ # :password => nil,
76
+ # :authentication => nil,
77
+ # :enable_starttls_auto => true }
78
+ #
79
+ # retriever_method :pop3, { :address => "localhost",
80
+ # :port => 995,
81
+ # :user_name => nil,
82
+ # :password => nil,
83
+ # :enable_ssl => true }
84
+ # end
85
+ #
86
+ # Mail.delivery_method.new #=> Mail::SMTP instance
87
+ # Mail.retriever_method.new #=> Mail::POP3 instance
88
+ #
89
+ # Each mail object inherits the default set in Mail.delivery_method, however, on
90
+ # a per email basis, you can override the method:
91
+ #
92
+ # mail.delivery_method :sendmail
93
+ #
94
+ # Or you can override the method and pass in settings:
95
+ #
96
+ # mail.delivery_method :sendmail, { :address => 'some.host' }
97
+ #
98
+ # You can also just modify the settings:
99
+ #
100
+ # mail.delivery_settings = { :address => 'some.host' }
101
+ #
102
+ # The passed in hash is just merged against the defaults with +merge!+ and the result
103
+ # assigned the mail object. So the above example will change only the :address value
104
+ # of the global smtp_settings to be 'some.host', keeping all other values
105
+ def Mail.defaults(&block)
106
+ Mail::Configuration.instance.instance_eval(&block)
107
+ end
108
+
109
+ # Returns the delivery method selected, defaults to an instance of Mail::SMTP
110
+ def Mail.delivery_method
111
+ Mail::Configuration.instance.delivery_method
112
+ end
113
+
114
+ # Returns the retriever method selected, defaults to an instance of Mail::POP3
115
+ def Mail.retriever_method
116
+ Mail::Configuration.instance.retriever_method
117
+ end
118
+
119
+ # Send an email using the default configuration. You do need to set a default
120
+ # configuration first before you use Mail.deliver, if you don't, an appropriate
121
+ # error will be raised telling you to.
122
+ #
123
+ # If you do not specify a delivery type, SMTP will be used.
124
+ #
125
+ # Mail.deliver do
126
+ # to 'mikel@test.lindsaar.net'
127
+ # from 'ada@test.lindsaar.net'
128
+ # subject 'This is a test email'
129
+ # body 'Not much to say here'
130
+ # end
131
+ #
132
+ # You can also do:
133
+ #
134
+ # mail = Mail.read('email.eml')
135
+ # mail.deliver!
136
+ #
137
+ # And your email object will be created and sent.
138
+ def Mail.deliver(*args, &block)
139
+ mail = Mail.new(args, &block)
140
+ mail.deliver
141
+ mail
142
+ end
143
+
144
+ # Find emails in a POP3 server.
145
+ # See Mail::POP3 for a complete documentation.
146
+ def Mail.find(*args, &block)
147
+ retriever_method.find(*args, &block)
148
+ end
149
+
150
+ # Receive the first email(s) from a Pop3 server.
151
+ # See Mail::POP3 for a complete documentation.
152
+ def Mail.first(*args, &block)
153
+ retriever_method.first(*args, &block)
154
+ end
155
+
156
+ # Receive the first email(s) from a Pop3 server.
157
+ # See Mail::POP3 for a complete documentation.
158
+ def Mail.last(*args, &block)
159
+ retriever_method.last(*args, &block)
160
+ end
161
+
162
+ # Receive all emails from a POP3 server.
163
+ # See Mail::POP3 for a complete documentation.
164
+ def Mail.all(*args, &block)
165
+ retriever_method.all(*args, &block)
166
+ end
167
+
168
+ # Reads in an email message from a path and instantiates it as a new Mail::Message
169
+ def Mail.read(filename)
170
+ Mail.new(File.read(filename))
171
+ end
172
+
173
+ protected
174
+
175
+ def Mail.random_tag
176
+ t = Time.now
177
+ sprintf('%x%x_%x%x%d%x',
178
+ t.to_i, t.tv_usec,
179
+ $$, Thread.current.object_id.abs, Mail.uniq, rand(255))
180
+ end
181
+
182
+ private
183
+
184
+ def Mail.something_random
185
+ (Thread.current.object_id * rand(255) / Time.now.to_f).to_s.slice(-3..-1).to_i
186
+ end
187
+
188
+ def Mail.uniq
189
+ @@uniq += 1
190
+ end
191
+
192
+ @@uniq = Mail.something_random
193
+
194
+ end
@@ -0,0 +1,1780 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+ # The Message class provides a single point of access to all things to do with an
4
+ # email message.
5
+ #
6
+ # You create a new email message by calling the Mail::Message.new method, or just
7
+ # Mail.new
8
+ #
9
+ # A Message object by default has the following objects inside it:
10
+ #
11
+ # * A Header object which contians all information and settings of the header of the email
12
+ # * Body object which contains all parts of the email that are not part of the header, this
13
+ # includes any attachments, body text, mime parts etc.
14
+ #
15
+ # ==Per RFC2822
16
+ #
17
+ # 2.1. General Description
18
+ #
19
+ # At the most basic level, a message is a series of characters. A
20
+ # message that is conformant with this standard is comprised of
21
+ # characters with values in the range 1 through 127 and interpreted as
22
+ # US-ASCII characters [ASCII]. For brevity, this document sometimes
23
+ # refers to this range of characters as simply "US-ASCII characters".
24
+ #
25
+ # Note: This standard specifies that messages are made up of characters
26
+ # in the US-ASCII range of 1 through 127. There are other documents,
27
+ # specifically the MIME document series [RFC2045, RFC2046, RFC2047,
28
+ # RFC2048, RFC2049], that extend this standard to allow for values
29
+ # outside of that range. Discussion of those mechanisms is not within
30
+ # the scope of this standard.
31
+ #
32
+ # Messages are divided into lines of characters. A line is a series of
33
+ # characters that is delimited with the two characters carriage-return
34
+ # and line-feed; that is, the carriage return (CR) character (ASCII
35
+ # value 13) followed immediately by the line feed (LF) character (ASCII
36
+ # value 10). (The carriage-return/line-feed pair is usually written in
37
+ # this document as "CRLF".)
38
+ #
39
+ # A message consists of header fields (collectively called "the header
40
+ # of the message") followed, optionally, by a body. The header is a
41
+ # sequence of lines of characters with special syntax as defined in
42
+ # this standard. The body is simply a sequence of characters that
43
+ # follows the header and is separated from the header by an empty line
44
+ # (i.e., a line with nothing preceding the CRLF).
45
+ class Message
46
+
47
+ include Patterns
48
+ include Utilities
49
+
50
+ # ==Making an email
51
+ #
52
+ # You can make an new mail object via a block, passing a string, file or direct assignment.
53
+ #
54
+ # ===Making an email via a block
55
+ #
56
+ # mail = Mail.new do
57
+ # from 'mikel@test.lindsaar.net'
58
+ # to 'you@test.lindsaar.net'
59
+ # subject 'This is a test email'
60
+ # body File.read('body.txt')
61
+ # end
62
+ #
63
+ # mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
64
+ #
65
+ # ===Making an email via passing a string
66
+ #
67
+ # mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
68
+ # mail.body.to_s #=> 'Hi there!'
69
+ # mail.subject #=> 'Hello'
70
+ # mail.to #=> 'mikel@test.lindsaar.net'
71
+ #
72
+ # ===Making an email from a file
73
+ #
74
+ # mail = Mail.read('path/to/file.eml')
75
+ # mail.body.to_s #=> 'Hi there!'
76
+ # mail.subject #=> 'Hello'
77
+ # mail.to #=> 'mikel@test.lindsaar.net'
78
+ #
79
+ # ===Making an email via assignment
80
+ #
81
+ # You can assign values to a mail object via four approaches:
82
+ #
83
+ # * Message#field_name=(value)
84
+ # * Message#field_name(value)
85
+ # * Message#['field_name']=(value)
86
+ # * Message#[:field_name]=(value)
87
+ #
88
+ # Examples:
89
+ #
90
+ # mail = Mail.new
91
+ # mail['from'] = 'mikel@test.lindsaar.net'
92
+ # mail[:to] = 'you@test.lindsaar.net'
93
+ # mail.subject 'This is a test email'
94
+ # mail.body = 'This is a body'
95
+ #
96
+ # mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
97
+ #
98
+ def initialize(*args, &block)
99
+ @body = nil
100
+ @text_part = nil
101
+ @html_part = nil
102
+
103
+ @perform_deliveries = true
104
+ @raise_delivery_errors = true
105
+
106
+ @delivery_handler = nil
107
+
108
+ @delivery_method = Mail.delivery_method.dup
109
+ @delivery_notification_observers = []
110
+
111
+ if args.flatten.first.respond_to?(:each_pair)
112
+ init_with_hash(args.flatten.first)
113
+ else
114
+ init_with_string(args.flatten[0].to_s.strip)
115
+ end
116
+
117
+ if block_given?
118
+ instance_eval(&block)
119
+ end
120
+
121
+ self
122
+ end
123
+
124
+ # If you assign a delivery handler, mail will call :deliver_mail on the
125
+ # object you assign to delivery_handler, it will pass itself as the
126
+ # single argument.
127
+ #
128
+ # If you define a delivery_handler, then you are responsible for the
129
+ # following actions in the delivery cycle:
130
+ #
131
+ # * Appending the mail object to Mail.deliveries as you see fit.
132
+ # * Checking the mail.perform_deliveries flag to decide if you should
133
+ # actually call :deliver! the mail object or not.
134
+ # * Checking the mail.raise_delivery_errors flag to decide if you
135
+ # should raise delivery errors if they occur.
136
+ # * Actually calling :deliver! (with the bang) on the mail object to
137
+ # get it to deliver itself.
138
+ #
139
+ # A simplest implementation of a delivery_handler would be
140
+ #
141
+ # class MyObject
142
+ #
143
+ # def initialize
144
+ # @mail = Mail.new('To: mikel@test.lindsaar.net')
145
+ # @mail.delivery_handler = self
146
+ # end
147
+ #
148
+ # attr_accessor :mail
149
+ #
150
+ # def deliver_mail(mail)
151
+ # yield
152
+ # end
153
+ # end
154
+ #
155
+ # Then doing:
156
+ #
157
+ # obj = MyObject.new
158
+ # obj.mail.deliver
159
+ #
160
+ # Would cause Mail to call obj.deliver_mail passing itself as a parameter,
161
+ # which then can just yield and let Mail do it's own private do_delivery
162
+ # method.
163
+ attr_accessor :delivery_handler
164
+
165
+ # If set to false, mail will go through the motions of doing a delivery,
166
+ # but not actually call the delivery method or append the mail object to
167
+ # the Mail.deliveries collection. Useful for testing.
168
+ #
169
+ # Mail.deliveries.size #=> 0
170
+ # mail.delivery_method :smtp
171
+ # mail.perform_deliveries = false
172
+ # mail.deliver # Mail::SMTP not called here
173
+ # Mail.deliveries.size #=> 0
174
+ #
175
+ # If you want to test and query the Mail.deliveries collection to see what
176
+ # mail you sent, you should set perform_deliveries to true and use
177
+ # the :test mail delivery_method:
178
+ #
179
+ # Mail.deliveries.size #=> 0
180
+ # mail.delivery_method :test
181
+ # mail.perform_deliveries = true
182
+ # mail.deliver
183
+ # Mail.deliveries.size #=> 1
184
+ #
185
+ # This setting is ignored by mail (though still available as a flag) if you
186
+ # define a delivery_handler
187
+ attr_accessor :perform_deliveries
188
+
189
+ # If set to false, mail will silently catch and ignore any exceptions
190
+ # raised through attempting to deliver an email.
191
+ #
192
+ # This setting is ignored by mail (though still available as a flag) if you
193
+ # define a delivery_handler
194
+ attr_accessor :raise_delivery_errors
195
+
196
+ def register_for_delivery_notification(observer)
197
+ unless @delivery_notification_observers.include?(observer)
198
+ @delivery_notification_observers << observer
199
+ end
200
+ end
201
+
202
+ def inform_observers
203
+ @delivery_notification_observers.each do |observer|
204
+ observer.delivered_email(self)
205
+ end
206
+ end
207
+
208
+ # Delivers an mail object.
209
+ #
210
+ # Examples:
211
+ #
212
+ # mail = Mail.read('file.eml')
213
+ # mail.deliver
214
+ def deliver
215
+ if delivery_handler
216
+ delivery_handler.deliver_mail(self) { do_delivery }
217
+ else
218
+ do_delivery
219
+ inform_observers
220
+ end
221
+ self
222
+ end
223
+
224
+ # This method bypasses checking perform_deliveries and raise_delivery_errors,
225
+ # so use with caution.
226
+ #
227
+ # It still however fires callbacks to the observers if they are defined.
228
+ #
229
+ # Returns self
230
+ def deliver!
231
+ delivery_method.deliver!(self)
232
+ inform_observers
233
+ self
234
+ end
235
+
236
+ def delivery_method(method = nil, settings = {})
237
+ unless method
238
+ @delivery_method
239
+ else
240
+ @delivery_method = Mail::Configuration.instance.lookup_delivery_method(method).new(settings)
241
+ end
242
+ end
243
+
244
+ # Provides the operator needed for sort et al.
245
+ #
246
+ # Compares this mail object with another mail object, this is done by date, so an
247
+ # email that is older than another will appear first.
248
+ #
249
+ # Example:
250
+ #
251
+ # mail1 = Mail.new do
252
+ # date(Time.now)
253
+ # end
254
+ # mail2 = Mail.new do
255
+ # date(Time.now - 86400) # 1 day older
256
+ # end
257
+ # [mail2, mail1].sort #=> [mail2, mail1]
258
+ def <=>(other)
259
+ if other.nil?
260
+ 1
261
+ else
262
+ self.date <=> other.date
263
+ end
264
+ end
265
+
266
+ # Two emails are the same if they have the same fields and body contents. One
267
+ # gotcha here is that Mail will insert Message-IDs when calling encoded, so doing
268
+ # mail1.encoded == mail2.encoded is most probably not going to return what you think
269
+ # as the assigned Message-IDs by Mail (if not already defined as the same) will ensure
270
+ # that the two objects are unique, and this comparison will ALWAYS return false.
271
+ #
272
+ # So the == operator has been defined like so: Two messages are the same if they have
273
+ # the same content, ignoring the Message-ID field, unless BOTH emails have a defined and
274
+ # different Message-ID value, then they are false.
275
+ #
276
+ # So, in practice the == operator works like this:
277
+ #
278
+ # m1 = Mail.new("Subject: Hello\r\n\r\nHello")
279
+ # m2 = Mail.new("Subject: Hello\r\n\r\nHello")
280
+ # m1 == m2 #=> true
281
+ #
282
+ # m1 = Mail.new("Subject: Hello\r\n\r\nHello")
283
+ # m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
284
+ # m1 == m2 #=> true
285
+ #
286
+ # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
287
+ # m2 = Mail.new("Subject: Hello\r\n\r\nHello")
288
+ # m1 == m2 #=> true
289
+ #
290
+ # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
291
+ # m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
292
+ # m1 == m2 #=> true
293
+ #
294
+ # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
295
+ # m2 = Mail.new("Message-ID: <DIFFERENT@test>\r\nSubject: Hello\r\n\r\nHello")
296
+ # m1 == m2 #=> false
297
+ def ==(other)
298
+ return false unless other.respond_to?(:encoded)
299
+
300
+ if self.message_id && other.message_id
301
+ result = (self.encoded == other.encoded)
302
+ else
303
+ self_message_id, other_message_id = self.message_id, other.message_id
304
+ self.message_id, other.message_id = '<temp@test>', '<temp@test>'
305
+ result = self.encoded == other.encoded
306
+ self.message_id, other.message_id = "<#{self_message_id}>", "<#{other_message_id}>"
307
+ result
308
+ end
309
+ end
310
+
311
+ # Provides access to the raw source of the message as it was when it
312
+ # was instantiated. This is set at initialization and so is untouched
313
+ # by the parsers or decoder / encoders
314
+ #
315
+ # Example:
316
+ #
317
+ # mail = Mail.new('This is an invalid email message')
318
+ # mail.raw_source #=> "This is an invalid email message"
319
+ def raw_source
320
+ @raw_source
321
+ end
322
+
323
+ # Sets the envelope from for the email
324
+ def set_envelope( val )
325
+ @raw_envelope = val
326
+ @envelope = Mail::Envelope.new( val )
327
+ end
328
+
329
+ # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
330
+ # type field that you can see at the top of any email that has come
331
+ # from a mailbox
332
+ def raw_envelope
333
+ @raw_envelope
334
+ end
335
+
336
+ def envelope_from
337
+ @envelope ? @envelope.from : nil
338
+ end
339
+
340
+ def envelope_date
341
+ @envelope ? @envelope.date : nil
342
+ end
343
+
344
+ # Sets the header of the message object.
345
+ #
346
+ # Example:
347
+ #
348
+ # mail.header = 'To: mikel@test.lindsaar.net\r\nFrom: Bob@bob.com'
349
+ # mail.header #=> <#Mail::Header
350
+ def header=(value)
351
+ @header = Mail::Header.new(value)
352
+ end
353
+
354
+ # Returns the header object of the message object. Or, if passed
355
+ # a parameter sets the value.
356
+ #
357
+ # Example:
358
+ #
359
+ # mail = Mail::Message.new('To: mikel\r\nFrom: you')
360
+ # mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
361
+ #
362
+ # mail.header #=> nil
363
+ # mail.header 'To: mikel\r\nFrom: you'
364
+ # mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
365
+ def header(value = nil)
366
+ value ? self.header = value : @header
367
+ end
368
+
369
+ # Provides a way to set custom headers, by passing in a hash
370
+ def headers(hash = {})
371
+ hash.each_pair do |k,v|
372
+ header[k] = v
373
+ end
374
+ end
375
+
376
+ # Returns the Bcc value of the mail object as an array of strings of
377
+ # address specs.
378
+ #
379
+ # Example:
380
+ #
381
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
382
+ # mail.bcc #=> ['mikel@test.lindsaar.net']
383
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
384
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
385
+ #
386
+ # Also allows you to set the value by passing a value as a parameter
387
+ #
388
+ # Example:
389
+ #
390
+ # mail.bcc 'Mikel <mikel@test.lindsaar.net>'
391
+ # mail.bcc #=> ['mikel@test.lindsaar.net']
392
+ #
393
+ # Additionally, you can append new addresses to the returned Array like
394
+ # object.
395
+ #
396
+ # Example:
397
+ #
398
+ # mail.bcc 'Mikel <mikel@test.lindsaar.net>'
399
+ # mail.bcc << 'ada@test.lindsaar.net'
400
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
401
+ def bcc( val = nil )
402
+ default :bcc, val
403
+ end
404
+
405
+ # Sets the Bcc value of the mail object, pass in a string of the field
406
+ #
407
+ # Example:
408
+ #
409
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
410
+ # mail.bcc #=> ['mikel@test.lindsaar.net']
411
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
412
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
413
+ def bcc=( val )
414
+ header[:bcc] = val
415
+ end
416
+
417
+ # Returns the Cc value of the mail object as an array of strings of
418
+ # address specs.
419
+ #
420
+ # Example:
421
+ #
422
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>'
423
+ # mail.cc #=> ['mikel@test.lindsaar.net']
424
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
425
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
426
+ #
427
+ # Also allows you to set the value by passing a value as a parameter
428
+ #
429
+ # Example:
430
+ #
431
+ # mail.cc 'Mikel <mikel@test.lindsaar.net>'
432
+ # mail.cc #=> ['mikel@test.lindsaar.net']
433
+ #
434
+ # Additionally, you can append new addresses to the returned Array like
435
+ # object.
436
+ #
437
+ # Example:
438
+ #
439
+ # mail.cc 'Mikel <mikel@test.lindsaar.net>'
440
+ # mail.cc << 'ada@test.lindsaar.net'
441
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
442
+ def cc( val = nil )
443
+ default :cc, val
444
+ end
445
+
446
+ # Sets the Cc value of the mail object, pass in a string of the field
447
+ #
448
+ # Example:
449
+ #
450
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>'
451
+ # mail.cc #=> ['mikel@test.lindsaar.net']
452
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
453
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
454
+ def cc=( val )
455
+ header[:cc] = val
456
+ end
457
+
458
+ def comments( val = nil )
459
+ default :comments, val
460
+ end
461
+
462
+ def comments=( val )
463
+ header[:comments] = val
464
+ end
465
+
466
+ def content_description( val = nil )
467
+ default :content_description, val
468
+ end
469
+
470
+ def content_description=( val )
471
+ header[:content_description] = val
472
+ end
473
+
474
+ def content_disposition( val = nil )
475
+ default :content_disposition, val
476
+ end
477
+
478
+ def content_disposition=( val )
479
+ header[:content_disposition] = val
480
+ end
481
+
482
+ def content_id( val = nil )
483
+ default :content_id, val
484
+ end
485
+
486
+ def content_id=( val )
487
+ header[:content_id] = val
488
+ end
489
+
490
+ def content_location( val = nil )
491
+ default :content_location, val
492
+ end
493
+
494
+ def content_location=( val )
495
+ header[:content_location] = val
496
+ end
497
+
498
+ def content_transfer_encoding( val = nil )
499
+ default :content_transfer_encoding, val
500
+ end
501
+
502
+ def content_transfer_encoding=( val )
503
+ header[:content_transfer_encoding] = val
504
+ end
505
+
506
+ def content_type( val = nil )
507
+ default :content_type, val
508
+ end
509
+
510
+ def content_type=( val )
511
+ header[:content_type] = val
512
+ end
513
+
514
+ def date( val = nil )
515
+ default :date, val
516
+ end
517
+
518
+ def date=( val )
519
+ header[:date] = val
520
+ end
521
+
522
+ # Returns the From value of the mail object as an array of strings of
523
+ # address specs.
524
+ #
525
+ # Example:
526
+ #
527
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>'
528
+ # mail.from #=> ['mikel@test.lindsaar.net']
529
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
530
+ # mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
531
+ #
532
+ # Also allows you to set the value by passing a value as a parameter
533
+ #
534
+ # Example:
535
+ #
536
+ # mail.from 'Mikel <mikel@test.lindsaar.net>'
537
+ # mail.from #=> ['mikel@test.lindsaar.net']
538
+ #
539
+ # Additionally, you can append new addresses to the returned Array like
540
+ # object.
541
+ #
542
+ # Example:
543
+ #
544
+ # mail.from 'Mikel <mikel@test.lindsaar.net>'
545
+ # mail.from << 'ada@test.lindsaar.net'
546
+ # mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
547
+ def from( val = nil )
548
+ default :from, val
549
+ end
550
+
551
+ # Sets the From value of the mail object, pass in a string of the field
552
+ #
553
+ # Example:
554
+ #
555
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>'
556
+ # mail.from #=> ['mikel@test.lindsaar.net']
557
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
558
+ # mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
559
+ def from=( val )
560
+ header[:from] = val
561
+ end
562
+
563
+ def in_reply_to( val = nil )
564
+ default :in_reply_to, val
565
+ end
566
+
567
+ def in_reply_to=( val )
568
+ header[:in_reply_to] = val
569
+ end
570
+
571
+ def keywords( val = nil )
572
+ default :keywords, val
573
+ end
574
+
575
+ def keywords=( val )
576
+ header[:keywords] = val
577
+ end
578
+
579
+ # Returns the Message-ID of the mail object. Note, per RFC 2822 the Message ID
580
+ # consists of what is INSIDE the < > usually seen in the mail header, so this method
581
+ # will return only what is inside.
582
+ #
583
+ # Example:
584
+ #
585
+ # mail.message_id = '<1234@message.id>'
586
+ # mail.message_id #=> '1234@message.id'
587
+ #
588
+ # Also allows you to set the Message-ID by passing a string as a parameter
589
+ #
590
+ # mail.message_id '<1234@message.id>'
591
+ # mail.message_id #=> '1234@message.id'
592
+ def message_id( val = nil )
593
+ default :message_id, val
594
+ end
595
+
596
+ # Sets the Message-ID. Note, per RFC 2822 the Message ID consists of what is INSIDE
597
+ # the < > usually seen in the mail header, so this method will return only what is inside.
598
+ #
599
+ # mail.message_id = '<1234@message.id>'
600
+ # mail.message_id #=> '1234@message.id'
601
+ def message_id=( val )
602
+ header[:message_id] = val
603
+ end
604
+
605
+ # Returns the mime version of the email as a string
606
+ #
607
+ # Example:
608
+ #
609
+ # mail.mime_version = '1.0'
610
+ # mail.mime_version #=> '1.0'
611
+ #
612
+ # Also allows you to set the mime version by passing a string as a parameter.
613
+ #
614
+ # Example:
615
+ #
616
+ # mail.mime_version '1.0'
617
+ # mail.mime_version #=> '1.0'
618
+ def mime_version( val = nil )
619
+ default :mime_version, val
620
+ end
621
+
622
+ # Sets the mime version of the email by accepting a string
623
+ #
624
+ # Example:
625
+ #
626
+ # mail.mime_version = '1.0'
627
+ # mail.mime_version #=> '1.0'
628
+ def mime_version=( val )
629
+ header[:mime_version] = val
630
+ end
631
+
632
+ def received( val = nil )
633
+ if val
634
+ header[:received] = val
635
+ else
636
+ header[:received]
637
+ end
638
+ end
639
+
640
+ def received=( val )
641
+ header[:received] = val
642
+ end
643
+
644
+ def references( val = nil )
645
+ default :references, val
646
+ end
647
+
648
+ def references=( val )
649
+ header[:references] = val
650
+ end
651
+
652
+ # Returns the Reply-To value of the mail object as an array of strings of
653
+ # address specs.
654
+ #
655
+ # Example:
656
+ #
657
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
658
+ # mail.reply_to #=> ['mikel@test.lindsaar.net']
659
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
660
+ # mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
661
+ #
662
+ # Also allows you to set the value by passing a value as a parameter
663
+ #
664
+ # Example:
665
+ #
666
+ # mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
667
+ # mail.reply_to #=> ['mikel@test.lindsaar.net']
668
+ #
669
+ # Additionally, you can append new addresses to the returned Array like
670
+ # object.
671
+ #
672
+ # Example:
673
+ #
674
+ # mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
675
+ # mail.reply_to << 'ada@test.lindsaar.net'
676
+ # mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
677
+ def reply_to( val = nil )
678
+ default :reply_to, val
679
+ end
680
+
681
+ # Sets the Reply-To value of the mail object, pass in a string of the field
682
+ #
683
+ # Example:
684
+ #
685
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
686
+ # mail.reply_to #=> ['mikel@test.lindsaar.net']
687
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
688
+ # mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
689
+ def reply_to=( val )
690
+ header[:reply_to] = val
691
+ end
692
+
693
+ # Returns the Resent-Bcc value of the mail object as an array of strings of
694
+ # address specs.
695
+ #
696
+ # Example:
697
+ #
698
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
699
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net']
700
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
701
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
702
+ #
703
+ # Also allows you to set the value by passing a value as a parameter
704
+ #
705
+ # Example:
706
+ #
707
+ # mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
708
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net']
709
+ #
710
+ # Additionally, you can append new addresses to the returned Array like
711
+ # object.
712
+ #
713
+ # Example:
714
+ #
715
+ # mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
716
+ # mail.resent_bcc << 'ada@test.lindsaar.net'
717
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
718
+ def resent_bcc( val = nil )
719
+ default :resent_bcc, val
720
+ end
721
+
722
+ # Sets the Resent-Bcc value of the mail object, pass in a string of the field
723
+ #
724
+ # Example:
725
+ #
726
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
727
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net']
728
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
729
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
730
+ def resent_bcc=( val )
731
+ header[:resent_bcc] = val
732
+ end
733
+
734
+ # Returns the Resent-Cc value of the mail object as an array of strings of
735
+ # address specs.
736
+ #
737
+ # Example:
738
+ #
739
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
740
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net']
741
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
742
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
743
+ #
744
+ # Also allows you to set the value by passing a value as a parameter
745
+ #
746
+ # Example:
747
+ #
748
+ # mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
749
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net']
750
+ #
751
+ # Additionally, you can append new addresses to the returned Array like
752
+ # object.
753
+ #
754
+ # Example:
755
+ #
756
+ # mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
757
+ # mail.resent_cc << 'ada@test.lindsaar.net'
758
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
759
+ def resent_cc( val = nil )
760
+ default :resent_cc, val
761
+ end
762
+
763
+ # Sets the Resent-Cc value of the mail object, pass in a string of the field
764
+ #
765
+ # Example:
766
+ #
767
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
768
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net']
769
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
770
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
771
+ def resent_cc=( val )
772
+ header[:resent_cc] = val
773
+ end
774
+
775
+ def resent_date( val = nil )
776
+ default :resent_date, val
777
+ end
778
+
779
+ def resent_date=( val )
780
+ header[:resent_date] = val
781
+ end
782
+
783
+ # Returns the Resent-From value of the mail object as an array of strings of
784
+ # address specs.
785
+ #
786
+ # Example:
787
+ #
788
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
789
+ # mail.resent_from #=> ['mikel@test.lindsaar.net']
790
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
791
+ # mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
792
+ #
793
+ # Also allows you to set the value by passing a value as a parameter
794
+ #
795
+ # Example:
796
+ #
797
+ # mail.resent_from ['Mikel <mikel@test.lindsaar.net>']
798
+ # mail.resent_from #=> 'mikel@test.lindsaar.net'
799
+ #
800
+ # Additionally, you can append new addresses to the returned Array like
801
+ # object.
802
+ #
803
+ # Example:
804
+ #
805
+ # mail.resent_from 'Mikel <mikel@test.lindsaar.net>'
806
+ # mail.resent_from << 'ada@test.lindsaar.net'
807
+ # mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
808
+ def resent_from( val = nil )
809
+ default :resent_from, val
810
+ end
811
+
812
+ # Sets the Resent-From value of the mail object, pass in a string of the field
813
+ #
814
+ # Example:
815
+ #
816
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
817
+ # mail.resent_from #=> ['mikel@test.lindsaar.net']
818
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
819
+ # mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
820
+ def resent_from=( val )
821
+ header[:resent_from] = val
822
+ end
823
+
824
+ def resent_message_id( val = nil )
825
+ default :resent_message_id, val
826
+ end
827
+
828
+ def resent_message_id=( val )
829
+ header[:resent_message_id] = val
830
+ end
831
+
832
+ # Returns the Resent-Sender value of the mail object, as a single string of an address
833
+ # spec. A sender per RFC 2822 must be a single address, so you can not append to
834
+ # this address.
835
+ #
836
+ # Example:
837
+ #
838
+ # mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
839
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
840
+ #
841
+ # Also allows you to set the value by passing a value as a parameter
842
+ #
843
+ # Example:
844
+ #
845
+ # mail.resent_sender 'Mikel <mikel@test.lindsaar.net>'
846
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
847
+ def resent_sender( val = nil )
848
+ default :resent_sender, val
849
+ end
850
+
851
+ # Sets the Resent-Sender value of the mail object, pass in a string of the field
852
+ #
853
+ # Example:
854
+ #
855
+ # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
856
+ # mail.sender #=> 'mikel@test.lindsaar.net'
857
+ def resent_sender=( val )
858
+ header[:resent_sender] = val
859
+ end
860
+
861
+ # Returns the Resent-To value of the mail object as an array of strings of
862
+ # address specs.
863
+ #
864
+ # Example:
865
+ #
866
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
867
+ # mail.resent_to #=> ['mikel@test.lindsaar.net']
868
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
869
+ # mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
870
+ #
871
+ # Also allows you to set the value by passing a value as a parameter
872
+ #
873
+ # Example:
874
+ #
875
+ # mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
876
+ # mail.resent_to #=> ['mikel@test.lindsaar.net']
877
+ #
878
+ # Additionally, you can append new addresses to the returned Array like
879
+ # object.
880
+ #
881
+ # Example:
882
+ #
883
+ # mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
884
+ # mail.resent_to << 'ada@test.lindsaar.net'
885
+ # mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
886
+ def resent_to( val = nil )
887
+ default :resent_to, val
888
+ end
889
+
890
+ # Sets the Resent-To value of the mail object, pass in a string of the field
891
+ #
892
+ # Example:
893
+ #
894
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
895
+ # mail.resent_to #=> ['mikel@test.lindsaar.net']
896
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
897
+ # mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
898
+ def resent_to=( val )
899
+ header[:resent_to] = val
900
+ end
901
+
902
+ # Returns the return path of the mail object, or sets it if you pass a string
903
+ def return_path( val = nil )
904
+ default :return_path, val
905
+ end
906
+
907
+ # Sets the return path of the object
908
+ def return_path=( val )
909
+ header[:return_path] = val
910
+ end
911
+
912
+ # Returns the Sender value of the mail object, as a single string of an address
913
+ # spec. A sender per RFC 2822 must be a single address.
914
+ #
915
+ # Example:
916
+ #
917
+ # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
918
+ # mail.sender #=> 'mikel@test.lindsaar.net'
919
+ #
920
+ # Also allows you to set the value by passing a value as a parameter
921
+ #
922
+ # Example:
923
+ #
924
+ # mail.sender 'Mikel <mikel@test.lindsaar.net>'
925
+ # mail.sender #=> 'mikel@test.lindsaar.net'
926
+ def sender( val = nil )
927
+ default :sender, val
928
+ end
929
+
930
+ # Sets the Sender value of the mail object, pass in a string of the field
931
+ #
932
+ # Example:
933
+ #
934
+ # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
935
+ # mail.sender #=> 'mikel@test.lindsaar.net'
936
+ def sender=( val )
937
+ header[:sender] = val
938
+ end
939
+
940
+ # Returns the decoded value of the subject field, as a single string.
941
+ #
942
+ # Example:
943
+ #
944
+ # mail.subject = "G'Day mate"
945
+ # mail.subject #=> "G'Day mate"
946
+ # mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
947
+ # mail.subject #=> "This is あ string"
948
+ #
949
+ # Also allows you to set the value by passing a value as a parameter
950
+ #
951
+ # Example:
952
+ #
953
+ # mail.subject "G'Day mate"
954
+ # mail.subject #=> "G'Day mate"
955
+ def subject( val = nil )
956
+ default :subject, val
957
+ end
958
+
959
+ # Sets the Subject value of the mail object, pass in a string of the field
960
+ #
961
+ # Example:
962
+ #
963
+ # mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
964
+ # mail.subject #=> "This is あ string"
965
+ def subject=( val )
966
+ header[:subject] = val
967
+ end
968
+
969
+ # Returns the To value of the mail object as an array of strings of
970
+ # address specs.
971
+ #
972
+ # Example:
973
+ #
974
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>'
975
+ # mail.to #=> ['mikel@test.lindsaar.net']
976
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
977
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
978
+ #
979
+ # Also allows you to set the value by passing a value as a parameter
980
+ #
981
+ # Example:
982
+ #
983
+ # mail.to 'Mikel <mikel@test.lindsaar.net>'
984
+ # mail.to #=> ['mikel@test.lindsaar.net']
985
+ #
986
+ # Additionally, you can append new addresses to the returned Array like
987
+ # object.
988
+ #
989
+ # Example:
990
+ #
991
+ # mail.to 'Mikel <mikel@test.lindsaar.net>'
992
+ # mail.to << 'ada@test.lindsaar.net'
993
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
994
+ def to( val = nil )
995
+ default :to, val
996
+ end
997
+
998
+ # Sets the To value of the mail object, pass in a string of the field
999
+ #
1000
+ # Example:
1001
+ #
1002
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>'
1003
+ # mail.to #=> ['mikel@test.lindsaar.net']
1004
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
1005
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
1006
+ def to=( val )
1007
+ header[:to] = val
1008
+ end
1009
+
1010
+ # Returns the default value of the field requested as a symbol.
1011
+ #
1012
+ # Each header field has a :default method which returns the most common use case for
1013
+ # that field, for example, the date field types will return a DateTime object when
1014
+ # sent :default, the subject, or unstructured fields will return a decoded string of
1015
+ # their value, the address field types will return a single addr_spec or an array of
1016
+ # addr_specs if there is more than one.
1017
+ def default( sym, val = nil )
1018
+ if val
1019
+ header[sym] = val
1020
+ else
1021
+ header[sym].default if header[sym]
1022
+ end
1023
+ end
1024
+
1025
+ # Sets the body object of the message object.
1026
+ #
1027
+ # Example:
1028
+ #
1029
+ # mail.body = 'This is the body'
1030
+ # mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
1031
+ #
1032
+ # You can also reset the body of an Message object by setting body to nil
1033
+ #
1034
+ # Example:
1035
+ #
1036
+ # mail.body = 'this is the body'
1037
+ # mail.body.encoded #=> 'this is the body'
1038
+ # mail.body = nil
1039
+ # mail.body.encoded #=> ''
1040
+ #
1041
+ # If you try and set the body of an email that is a multipart email, then instead
1042
+ # of deleting all the parts of your email, mail will add a text/plain part to
1043
+ # your email:
1044
+ #
1045
+ # mail.add_file 'somefilename.png'
1046
+ # mail.parts.length #=> 1
1047
+ # mail.body = "This is a body"
1048
+ # mail.parts.length #=> 2
1049
+ # mail.parts.last.content_type.content_type #=> 'This is a body'
1050
+ def body=(value)
1051
+ case
1052
+ when value == nil
1053
+ @body = Mail::Body.new('')
1054
+ when @body && !@body.parts.empty?
1055
+ @body << Mail::Part.new(value)
1056
+ else
1057
+ @body = Mail::Body.new(value)
1058
+ end
1059
+ add_encoding_to_body
1060
+ end
1061
+
1062
+ # Returns the body of the message object. Or, if passed
1063
+ # a parameter sets the value.
1064
+ #
1065
+ # Example:
1066
+ #
1067
+ # mail = Mail::Message.new('To: mikel\r\n\r\nThis is the body')
1068
+ # mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
1069
+ #
1070
+ # mail.body 'This is another body'
1071
+ # mail.body #=> #<Mail::Body:0x13919c @raw_source="This is anothe...
1072
+ def body(value = nil)
1073
+ if value
1074
+ self.body = value
1075
+ add_encoding_to_body
1076
+ else
1077
+ @body
1078
+ end
1079
+ end
1080
+
1081
+ # Returns the list of addresses this message should be sent to by
1082
+ # collecting the addresses off the to, cc and bcc fields.
1083
+ #
1084
+ # Example:
1085
+ #
1086
+ # mail.to = 'mikel@test.lindsaar.net'
1087
+ # mail.cc = 'sam@test.lindsaar.net'
1088
+ # mail.bcc = 'bob@test.lindsaar.net'
1089
+ # mail.destinations.length #=> 3
1090
+ # mail.destinations.first #=> 'mikel@test.lindsaar.net'
1091
+ def destinations
1092
+ [to_addrs, cc_addrs, bcc_addrs].compact.flatten
1093
+ end
1094
+
1095
+ # Returns an array of addresses (the encoded value) in the From field,
1096
+ # if no From field, returns an empty array
1097
+ def from_addrs
1098
+ from ? [from].flatten : []
1099
+ end
1100
+
1101
+ # Returns an array of addresses (the encoded value) in the To field,
1102
+ # if no To field, returns an empty array
1103
+ def to_addrs
1104
+ to ? [to].flatten : []
1105
+ end
1106
+
1107
+ # Returns an array of addresses (the encoded value) in the Cc field,
1108
+ # if no Cc field, returns an empty array
1109
+ def cc_addrs
1110
+ cc ? [cc].flatten : []
1111
+ end
1112
+
1113
+ # Returns an array of addresses (the encoded value) in the Bcc field,
1114
+ # if no Bcc field, returns an empty array
1115
+ def bcc_addrs
1116
+ bcc ? [bcc].flatten : []
1117
+ end
1118
+
1119
+ # Allows you to add an arbitrary header
1120
+ #
1121
+ # Example:
1122
+ #
1123
+ # mail['foo'] = '1234'
1124
+ # mail['foo'].to_s #=> '1234'
1125
+ def []=(name, value)
1126
+ if name.to_s == 'body'
1127
+ self.body = value
1128
+ elsif name.to_s =~ /content[-_]type/i
1129
+ header[underscoreize(name)] = value
1130
+ else
1131
+ header[underscoreize(name)] = value
1132
+ end
1133
+ end
1134
+
1135
+ # Allows you to read an arbitrary header
1136
+ #
1137
+ # Example:
1138
+ #
1139
+ # mail['foo'] = '1234'
1140
+ # mail['foo'].to_s #=> '1234'
1141
+ def [](name)
1142
+ header[underscoreize(name)]
1143
+ end
1144
+
1145
+ # Method Missing in this implementation allows you to set any of the
1146
+ # standard fields directly as you would the "to", "subject" etc.
1147
+ #
1148
+ # Those fields used most often (to, subject et al) are given their
1149
+ # own method for ease of documentation and also to avoid the hook
1150
+ # call to method missing.
1151
+ #
1152
+ # This will only catch the known fields listed in:
1153
+ #
1154
+ # Mail::Field::KNOWN_FIELDS
1155
+ #
1156
+ # as per RFC 2822, any ruby string or method name could pretty much
1157
+ # be a field name, so we don't want to just catch ANYTHING sent to
1158
+ # a message object and interpret it as a header.
1159
+ #
1160
+ # This method provides all three types of header call to set, read
1161
+ # and explicitly set with the = operator
1162
+ #
1163
+ # Examples:
1164
+ #
1165
+ # mail.comments = 'These are some comments'
1166
+ # mail.comments #=> 'These are some comments'
1167
+ #
1168
+ # mail.comments 'These are other comments'
1169
+ # mail.comments #=> 'These are other comments'
1170
+ #
1171
+ #
1172
+ # mail.date = 'Tue, 1 Jul 2003 10:52:37 +0200'
1173
+ # mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
1174
+ #
1175
+ # mail.date 'Tue, 1 Jul 2003 10:52:37 +0200'
1176
+ # mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
1177
+ #
1178
+ #
1179
+ # mail.resent_msg_id = '<1234@resent_msg_id.lindsaar.net>'
1180
+ # mail.resent_msg_id #=> '<1234@resent_msg_id.lindsaar.net>'
1181
+ #
1182
+ # mail.resent_msg_id '<4567@resent_msg_id.lindsaar.net>'
1183
+ # mail.resent_msg_id #=> '<4567@resent_msg_id.lindsaar.net>'
1184
+ def method_missing(name, *args, &block)
1185
+ #:nodoc:
1186
+ # Only take the structured fields, as we could take _anything_ really
1187
+ # as it could become an optional field... "but therin lies the dark side"
1188
+ field_name = underscoreize(name).chomp("=")
1189
+ if Mail::Field::KNOWN_FIELDS.include?(field_name)
1190
+ if args.empty?
1191
+ header[field_name]
1192
+ else
1193
+ header[field_name] = args.first
1194
+ end
1195
+ else
1196
+ super # otherwise pass it on
1197
+ end
1198
+ #:startdoc:
1199
+ end
1200
+
1201
+ # Returns an FieldList of all the fields in the header in the order that
1202
+ # they appear in the header
1203
+ def header_fields
1204
+ header.fields
1205
+ end
1206
+
1207
+ # Returns true if the message has a message ID field, the field may or may
1208
+ # not have a value, but the field exists or not.
1209
+ def has_message_id?
1210
+ header.has_message_id?
1211
+ end
1212
+
1213
+ # Returns true if the message has a Date field, the field may or may
1214
+ # not have a value, but the field exists or not.
1215
+ def has_date?
1216
+ header.has_date?
1217
+ end
1218
+
1219
+ # Returns true if the message has a Date field, the field may or may
1220
+ # not have a value, but the field exists or not.
1221
+ def has_mime_version?
1222
+ header.has_mime_version?
1223
+ end
1224
+
1225
+ def has_content_type?
1226
+ !!header[:content_type]
1227
+ end
1228
+
1229
+ def has_charset?
1230
+ !!charset
1231
+ end
1232
+
1233
+ def has_content_transfer_encoding?
1234
+ !!content_transfer_encoding
1235
+ end
1236
+
1237
+ def has_transfer_encoding? # :nodoc:
1238
+ STDERR.puts(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
1239
+ has_content_transfer_encoding?
1240
+ end
1241
+
1242
+ # Creates a new empty Message-ID field and inserts it in the correct order
1243
+ # into the Header. The MessageIdField object will automatically generate
1244
+ # a unique message ID if you try and encode it or output it to_s without
1245
+ # specifying a message id.
1246
+ #
1247
+ # It will preserve the message ID you specify if you do.
1248
+ def add_message_id(msg_id_val = '')
1249
+ header['message-id'] = msg_id_val
1250
+ end
1251
+
1252
+ # Creates a new empty Date field and inserts it in the correct order
1253
+ # into the Header. The DateField object will automatically generate
1254
+ # DateTime.now's date if you try and encode it or output it to_s without
1255
+ # specifying a date yourself.
1256
+ #
1257
+ # It will preserve any date you specify if you do.
1258
+ def add_date(date_val = '')
1259
+ header['date'] = date_val
1260
+ end
1261
+
1262
+ # Creates a new empty Mime Version field and inserts it in the correct order
1263
+ # into the Header. The MimeVersion object will automatically generate
1264
+ # DateTime.now's date if you try and encode it or output it to_s without
1265
+ # specifying a date yourself.
1266
+ #
1267
+ # It will preserve any date you specify if you do.
1268
+ def add_mime_version(ver_val = '')
1269
+ header['mime-version'] = ver_val
1270
+ end
1271
+
1272
+ # Adds a content type and charset if the body is US-ASCII
1273
+ #
1274
+ # Otherwise raises a warning
1275
+ def add_content_type
1276
+ header[:content_type] = 'text/plain'
1277
+ end
1278
+
1279
+ # Adds a content type and charset if the body is US-ASCII
1280
+ #
1281
+ # Otherwise raises a warning
1282
+ def add_charset
1283
+ if body.only_us_ascii?
1284
+ header[:content_type].parameters['charset'] = 'US-ASCII'
1285
+ else
1286
+ warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1287
+ STDERR.puts(warning)
1288
+ header[:content_type].parameters['charset'] = 'UTF-8'
1289
+ end
1290
+ end
1291
+
1292
+ # Adds a content transfer encoding
1293
+ #
1294
+ # Otherwise raises a warning
1295
+ def add_content_transfer_encoding
1296
+ if body.only_us_ascii?
1297
+ header[:content_transfer_encoding] = '7bit'
1298
+ else
1299
+ warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
1300
+ STDERR.puts(warning)
1301
+ header[:content_transfer_encoding] = '8bit'
1302
+ end
1303
+ end
1304
+
1305
+ def add_transfer_encoding # :nodoc:
1306
+ STDERR.puts(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
1307
+ add_content_transfer_encoding
1308
+ end
1309
+
1310
+ def transfer_encoding # :nodoc:
1311
+ STDERR.puts(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
1312
+ content_transfer_encoding
1313
+ end
1314
+
1315
+ # Returns the mime type of part we are on, this is taken from the content-type header
1316
+ def mime_type
1317
+ content_type ? header[:content_type].string : nil
1318
+ end
1319
+
1320
+ def message_content_type
1321
+ STDERR.puts(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
1322
+ mime_type
1323
+ end
1324
+
1325
+ # Returns the character set defined in the content type field
1326
+ def charset
1327
+ content_type ? content_type_parameters['charset'] : nil
1328
+ end
1329
+
1330
+ # Sets the charset to the supplied value. Will set the content type to text/plain if
1331
+ # it does not already exist
1332
+ def charset=(value)
1333
+ if content_type
1334
+ content_type_parameters['charset'] = value
1335
+ else
1336
+ self.content_type ['text', 'plain', {'charset' => value}]
1337
+ end
1338
+ end
1339
+
1340
+ # Returns the main content type
1341
+ def main_type
1342
+ has_content_type? ? header[:content_type].main_type : nil
1343
+ end
1344
+
1345
+ # Returns the sub content type
1346
+ def sub_type
1347
+ has_content_type? ? header[:content_type].sub_type : nil
1348
+ end
1349
+
1350
+ # Returns the content type parameters
1351
+ def mime_parameters
1352
+ STDERR.puts(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
1353
+ content_type_parameters
1354
+ end
1355
+
1356
+ # Returns the content type parameters
1357
+ def content_type_parameters
1358
+ has_content_type? ? header[:content_type].parameters : nil
1359
+ end
1360
+
1361
+ # Returns true if the message is multipart
1362
+ def multipart?
1363
+ has_content_type? ? !!(main_type =~ /^multipart$/i) : false
1364
+ end
1365
+
1366
+ # Returns true if the message is a multipart/report
1367
+ def multipart_report?
1368
+ multipart? && sub_type =~ /^report$/i
1369
+ end
1370
+
1371
+ # Returns true if the message is a multipart/report; report-type=delivery-status;
1372
+ def delivery_status_report?
1373
+ multipart_report? && content_type_parameters['report-type'] =~ /^delivery-status$/i
1374
+ end
1375
+
1376
+ # returns the part in a multipart/report email that has the content-type delivery-status
1377
+ def delivery_status_part
1378
+ @delivery_stats_part ||= parts.select { |p| p.delivery_status_report_part? }.first
1379
+ end
1380
+
1381
+ def bounced?
1382
+ delivery_status_part and delivery_status_part.bounced?
1383
+ end
1384
+
1385
+ def action
1386
+ delivery_status_part and delivery_status_part.action
1387
+ end
1388
+
1389
+ def final_recipient
1390
+ delivery_status_part and delivery_status_part.final_recipient
1391
+ end
1392
+
1393
+ def error_status
1394
+ delivery_status_part and delivery_status_part.error_status
1395
+ end
1396
+
1397
+ def diagnostic_code
1398
+ delivery_status_part and delivery_status_part.diagnostic_code
1399
+ end
1400
+
1401
+ def remote_mta
1402
+ delivery_status_part and delivery_status_part.remote_mta
1403
+ end
1404
+
1405
+ def retryable?
1406
+ delivery_status_part and delivery_status_part.retryable?
1407
+ end
1408
+
1409
+ # Returns the current boundary for this message part
1410
+ def boundary
1411
+ content_type_parameters ? content_type_parameters['boundary'] : nil
1412
+ end
1413
+
1414
+ # Returns a parts list object of all the parts in the message
1415
+ def parts
1416
+ body.parts
1417
+ end
1418
+
1419
+ # Returns an AttachmentsList object, which holds all of the attachments in
1420
+ # the receiver object (either the entier email or a part within) and all
1421
+ # of it's descendants.
1422
+ #
1423
+ # It also allows you to add attachments to the mail object directly, like so:
1424
+ #
1425
+ # mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
1426
+ #
1427
+ # If you do this, then Mail will take the file name and work out the mime type
1428
+ # set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
1429
+ # base64 encode the contents of the attachment all for you.
1430
+ #
1431
+ # You can also specify overrides if you want by passing a hash instead of a string:
1432
+ #
1433
+ # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
1434
+ # :content => File.read('/path/to/filename.jpg')}
1435
+ #
1436
+ # If you want to use a different encoding than Base64, you can pass an encoding in,
1437
+ # but then it is up to you to pass in the content pre-encoded, and don't expect
1438
+ # Mail to know how to decode this data:
1439
+ #
1440
+ # file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
1441
+ # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
1442
+ # :encoding => 'SpecialEncoding',
1443
+ # :content => file_content }
1444
+ #
1445
+ # You can also search for specific attachments:
1446
+ #
1447
+ # # By Filename
1448
+ # mail.attachments['filename.jpg'] #=> Mail::Part object or nil
1449
+ #
1450
+ # # or by index
1451
+ # mail.attachments[0] #=> Mail::Part (first attachment)
1452
+ #
1453
+ def attachments
1454
+ parts.attachments
1455
+ end
1456
+
1457
+ def has_attachments?
1458
+ !attachments.empty?
1459
+ end
1460
+
1461
+ # Accessor for html_part
1462
+ def html_part(&block)
1463
+ if block_given?
1464
+ @html_part = Mail::Part.new(&block)
1465
+ add_multipart_alternate_header unless html_part.blank?
1466
+ add_part(@html_part)
1467
+ else
1468
+ @html_part || find_first_mime_type('text/html')
1469
+ end
1470
+ end
1471
+
1472
+ # Accessor for text_part
1473
+ def text_part(&block)
1474
+ if block_given?
1475
+ @text_part = Mail::Part.new(&block)
1476
+ add_multipart_alternate_header unless html_part.blank?
1477
+ add_part(@text_part)
1478
+ else
1479
+ @text_part || find_first_mime_type('text/plain')
1480
+ end
1481
+ end
1482
+
1483
+ # Helper to add a html part to a multipart/alternative email. If this and
1484
+ # text_part are both defined in a message, then it will be a multipart/alternative
1485
+ # message and set itself that way.
1486
+ def html_part=(msg = nil)
1487
+ if msg
1488
+ @html_part = msg
1489
+ else
1490
+ @html_part = Mail::Part.new('Content-Type: text/html;')
1491
+ end
1492
+ add_multipart_alternate_header unless text_part.blank?
1493
+ add_part(@html_part)
1494
+ end
1495
+
1496
+ # Helper to add a text part to a multipart/alternative email. If this and
1497
+ # html_part are both defined in a message, then it will be a multipart/alternative
1498
+ # message and set itself that way.
1499
+ def text_part=(msg = nil)
1500
+ if msg
1501
+ @text_part = msg
1502
+ else
1503
+ @text_part = Mail::Part.new('Content-Type: text/plain;')
1504
+ end
1505
+ add_multipart_alternate_header unless html_part.blank?
1506
+ add_part(@text_part)
1507
+ end
1508
+
1509
+ # Adds a part to the parts list or creates the part list
1510
+ def add_part(part)
1511
+ if body.parts.empty? && !self.body.decoded.blank?
1512
+ @text_part = Mail::Part.new('Content-Type: text/plain;')
1513
+ @text_part.body = body.decoded
1514
+ self.body << @text_part
1515
+ add_multipart_alternate_header
1516
+ end
1517
+ add_boundary
1518
+ self.body << part
1519
+ end
1520
+
1521
+ # Allows you to add a part in block form to an existing mail message object
1522
+ #
1523
+ # Example:
1524
+ #
1525
+ # mail = Mail.new do
1526
+ # part :content_type => "multipart/alternative", :content_disposition => "inline" do |p|
1527
+ # p.part :content_type => "text/plain", :body => "test text\nline #2"
1528
+ # p.part :content_type => "text/html", :body => "<b>test</b> HTML<br/>\nline #2"
1529
+ # end
1530
+ # end
1531
+ def part(params = {})
1532
+ new_part = Part.new(params)
1533
+ yield new_part if block_given?
1534
+ add_part(new_part)
1535
+ end
1536
+
1537
+ # Adds a file to the message. You have two options with this method, you can
1538
+ # just pass in the absolute path to the file you want and Mail will read the file,
1539
+ # get the filename from the path you pass in and guess the mime type, or you
1540
+ # can pass in the filename as a string, and pass in the file content as a blob.
1541
+ #
1542
+ # Example:
1543
+ #
1544
+ # m = Mail.new
1545
+ # m.add_file('/path/to/filename.png')
1546
+ #
1547
+ # m = Mail.new
1548
+ # m.add_file(:filename => 'filename.png', :content => File.read('/path/to/file.jpg'))
1549
+ #
1550
+ # Note also that if you add a file to an existing message, Mail will convert that message
1551
+ # to a MIME multipart email, moving whatever plain text body you had into it's own text
1552
+ # plain part.
1553
+ #
1554
+ # Example:
1555
+ #
1556
+ # m = Mail.new do
1557
+ # body 'this is some text'
1558
+ # end
1559
+ # m.multipart? #=> false
1560
+ # m.add_file('/path/to/filename.png')
1561
+ # m.multipart? #=> true
1562
+ # m.parts.first.content_type.content_type #=> 'text/plain'
1563
+ # m.parts.last.content_type.content_type #=> 'image/png'
1564
+ #
1565
+ # See also #attachments
1566
+ def add_file(values)
1567
+ convert_to_multipart unless self.multipart? || self.body.decoded.blank?
1568
+ add_multipart_mixed_header
1569
+ if values.is_a?(String)
1570
+ basename = File.basename(values)
1571
+ filedata = File.read(values)
1572
+ else
1573
+ basename = values[:filename]
1574
+ filedata = values[:content] || File.read(values[:filename])
1575
+ end
1576
+ self.attachments[basename] = filedata
1577
+ end
1578
+
1579
+ def convert_to_multipart
1580
+ text = @body.decoded
1581
+ self.body = ''
1582
+ text_part = Mail::Part.new({:content_type => 'text/plain;',
1583
+ :body => text})
1584
+ self.body << text_part
1585
+ end
1586
+
1587
+ # Encodes the message, calls encode on all it's parts, gets an email message
1588
+ # ready to send
1589
+ def ready_to_send!
1590
+ parts.each { |part| part.ready_to_send! }
1591
+ add_required_fields
1592
+ end
1593
+
1594
+ def encode!
1595
+ STDERR.puts("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
1596
+ ready_to_send!
1597
+ end
1598
+
1599
+ # Outputs an encoded string representation of the mail message including
1600
+ # all headers, attachments, etc. This is an encoded email in US-ASCII,
1601
+ # so it is able to be directly sent to an email server.
1602
+ def encoded
1603
+ ready_to_send!
1604
+ buffer = header.encoded
1605
+ buffer << "\r\n"
1606
+ buffer << body.encoded
1607
+ buffer
1608
+ end
1609
+
1610
+ def to_s
1611
+ encoded
1612
+ end
1613
+
1614
+ def decoded
1615
+ if self.attachment?
1616
+ decode_body
1617
+ else
1618
+ raise NoMethodError, 'Can not decode an entire message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
1619
+ end
1620
+ end
1621
+
1622
+ def read
1623
+ if self.attachment?
1624
+ decode_body
1625
+ else
1626
+ raise NoMethodError, 'Can not call read on a part unless it is an attachment.'
1627
+ end
1628
+ end
1629
+
1630
+ def decode_body
1631
+ if Mail::Encodings.defined?(content_transfer_encoding)
1632
+ Mail::Encodings.get_encoding(content_transfer_encoding).decode(body.encoded)
1633
+ else
1634
+ raise UnknownEncodingType, "Don't know how to decode #{content_transfer_encoding}, please call #encoded and decode it yourself."
1635
+ end
1636
+ end
1637
+
1638
+ # Returns true if this part is an attachment
1639
+ def attachment?
1640
+ find_attachment
1641
+ end
1642
+
1643
+ # Returns the attachment data if there is any
1644
+ def attachment
1645
+ @attachment
1646
+ end
1647
+
1648
+ # Returns the filename of the attachment
1649
+ def filename
1650
+ find_attachment
1651
+ end
1652
+
1653
+ def all_parts
1654
+ parts.map { |p| [p, p.all_parts] }.flatten
1655
+ end
1656
+
1657
+ def find_first_mime_type(mt)
1658
+ all_parts.detect { |p| p.mime_type == mt }
1659
+ end
1660
+
1661
+ private
1662
+
1663
+ # 2.1. General Description
1664
+ # A message consists of header fields (collectively called "the header
1665
+ # of the message") followed, optionally, by a body. The header is a
1666
+ # sequence of lines of characters with special syntax as defined in
1667
+ # this standard. The body is simply a sequence of characters that
1668
+ # follows the header and is separated from the header by an empty line
1669
+ # (i.e., a line with nothing preceding the CRLF).
1670
+ #
1671
+ # Additionally, I allow for the case where someone might have put whitespace
1672
+ # on the "gap line"
1673
+ def parse_message
1674
+ header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
1675
+ self.header = header_part
1676
+ self.body = body_part
1677
+ end
1678
+
1679
+ def raw_source=(value)
1680
+ @raw_source = value.to_crlf
1681
+ end
1682
+
1683
+ def set_envelope_header
1684
+ if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}(.*)/m)
1685
+ set_envelope(match_data[1])
1686
+ self.raw_source = match_data[2]
1687
+ end
1688
+ end
1689
+
1690
+ def separate_parts
1691
+ body.split!(boundary)
1692
+ end
1693
+
1694
+ def add_encoding_to_body
1695
+ unless content_transfer_encoding.blank?
1696
+ body.encoding = content_transfer_encoding
1697
+ end
1698
+ end
1699
+
1700
+ def add_required_fields
1701
+ add_multipart_mixed_header unless parts.empty?
1702
+ @body = Mail::Body.new('') if body.nil?
1703
+ add_message_id unless (has_message_id? || self.class == Mail::Part)
1704
+ add_date unless has_date?
1705
+ add_mime_version unless has_mime_version?
1706
+ add_content_type unless has_content_type?
1707
+ add_charset unless has_charset?
1708
+ add_content_transfer_encoding unless has_content_transfer_encoding?
1709
+ end
1710
+
1711
+ def add_multipart_alternate_header
1712
+ header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
1713
+ body.boundary = boundary
1714
+ end
1715
+
1716
+ def add_boundary
1717
+ unless body.boundary && boundary
1718
+ header['content-type'] = 'multipart/mixed' unless header['content-type']
1719
+ header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
1720
+ body.boundary = boundary
1721
+ end
1722
+ end
1723
+
1724
+ def add_multipart_mixed_header
1725
+ unless header['content-type']
1726
+ header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
1727
+ body.boundary = boundary
1728
+ end
1729
+ end
1730
+
1731
+ def init_with_hash(hash)
1732
+ passed_in_options = hash.with_indifferent_access
1733
+ self.raw_source = ''
1734
+ @header = Mail::Header.new
1735
+ @body = Mail::Body.new
1736
+
1737
+ passed_in_options.each_pair do |k,v|
1738
+ k = underscoreize(k).to_sym if k.class == String
1739
+ if k == :headers
1740
+ self.headers(v)
1741
+ else
1742
+ self[k] = v
1743
+ end
1744
+ end
1745
+ end
1746
+
1747
+ def init_with_string(string)
1748
+ self.raw_source = string
1749
+ set_envelope_header
1750
+ parse_message
1751
+ separate_parts if multipart?
1752
+ end
1753
+
1754
+ # Returns the filename of the attachment (if it exists) or returns nil
1755
+ def find_attachment
1756
+ case
1757
+ when content_type && header[:content_type].filename
1758
+ filename = header[:content_type].filename
1759
+ when content_disposition && header[:content_disposition].filename
1760
+ filename = header[:content_disposition].filename
1761
+ when content_location && header[:content_location].location
1762
+ filename = header[:content_location].location
1763
+ else
1764
+ filename = nil
1765
+ end
1766
+ filename
1767
+ end
1768
+
1769
+ def do_delivery
1770
+ begin
1771
+ if perform_deliveries
1772
+ delivery_method.deliver!(self)
1773
+ end
1774
+ rescue Exception => e # Net::SMTP errors or sendmail pipe errors
1775
+ raise e if raise_delivery_errors
1776
+ end
1777
+ end
1778
+
1779
+ end
1780
+ end