otherinbox-mail 2.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/CHANGELOG.rdoc +607 -0
  2. data/CONTRIBUTING.md +45 -0
  3. data/Dependencies.txt +3 -0
  4. data/Gemfile +26 -0
  5. data/Gemfile.lock +44 -0
  6. data/README.md +663 -0
  7. data/Rakefile +40 -0
  8. data/TODO.rdoc +9 -0
  9. data/lib/VERSION +4 -0
  10. data/lib/mail.rb +101 -0
  11. data/lib/mail/attachments_list.rb +104 -0
  12. data/lib/mail/body.rb +291 -0
  13. data/lib/mail/configuration.rb +75 -0
  14. data/lib/mail/core_extensions/nil.rb +17 -0
  15. data/lib/mail/core_extensions/object.rb +13 -0
  16. data/lib/mail/core_extensions/shell_escape.rb +56 -0
  17. data/lib/mail/core_extensions/smtp.rb +25 -0
  18. data/lib/mail/core_extensions/string.rb +33 -0
  19. data/lib/mail/core_extensions/string/access.rb +145 -0
  20. data/lib/mail/core_extensions/string/multibyte.rb +78 -0
  21. data/lib/mail/elements.rb +14 -0
  22. data/lib/mail/elements/address.rb +306 -0
  23. data/lib/mail/elements/address_list.rb +74 -0
  24. data/lib/mail/elements/content_disposition_element.rb +30 -0
  25. data/lib/mail/elements/content_location_element.rb +25 -0
  26. data/lib/mail/elements/content_transfer_encoding_element.rb +24 -0
  27. data/lib/mail/elements/content_type_element.rb +35 -0
  28. data/lib/mail/elements/date_time_element.rb +26 -0
  29. data/lib/mail/elements/envelope_from_element.rb +34 -0
  30. data/lib/mail/elements/message_ids_element.rb +29 -0
  31. data/lib/mail/elements/mime_version_element.rb +26 -0
  32. data/lib/mail/elements/phrase_list.rb +21 -0
  33. data/lib/mail/elements/received_element.rb +30 -0
  34. data/lib/mail/encodings.rb +274 -0
  35. data/lib/mail/encodings/7bit.rb +31 -0
  36. data/lib/mail/encodings/8bit.rb +31 -0
  37. data/lib/mail/encodings/base64.rb +33 -0
  38. data/lib/mail/encodings/binary.rb +31 -0
  39. data/lib/mail/encodings/quoted_printable.rb +38 -0
  40. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  41. data/lib/mail/envelope.rb +35 -0
  42. data/lib/mail/field.rb +234 -0
  43. data/lib/mail/field_list.rb +33 -0
  44. data/lib/mail/fields.rb +35 -0
  45. data/lib/mail/fields/bcc_field.rb +56 -0
  46. data/lib/mail/fields/cc_field.rb +55 -0
  47. data/lib/mail/fields/comments_field.rb +41 -0
  48. data/lib/mail/fields/common/address_container.rb +16 -0
  49. data/lib/mail/fields/common/common_address.rb +125 -0
  50. data/lib/mail/fields/common/common_date.rb +42 -0
  51. data/lib/mail/fields/common/common_field.rb +51 -0
  52. data/lib/mail/fields/common/common_message_id.rb +44 -0
  53. data/lib/mail/fields/common/parameter_hash.rb +58 -0
  54. data/lib/mail/fields/content_description_field.rb +19 -0
  55. data/lib/mail/fields/content_disposition_field.rb +69 -0
  56. data/lib/mail/fields/content_id_field.rb +63 -0
  57. data/lib/mail/fields/content_location_field.rb +42 -0
  58. data/lib/mail/fields/content_transfer_encoding_field.rb +50 -0
  59. data/lib/mail/fields/content_type_field.rb +198 -0
  60. data/lib/mail/fields/date_field.rb +57 -0
  61. data/lib/mail/fields/from_field.rb +55 -0
  62. data/lib/mail/fields/in_reply_to_field.rb +55 -0
  63. data/lib/mail/fields/keywords_field.rb +44 -0
  64. data/lib/mail/fields/message_id_field.rb +83 -0
  65. data/lib/mail/fields/mime_version_field.rb +53 -0
  66. data/lib/mail/fields/optional_field.rb +13 -0
  67. data/lib/mail/fields/received_field.rb +75 -0
  68. data/lib/mail/fields/references_field.rb +55 -0
  69. data/lib/mail/fields/reply_to_field.rb +55 -0
  70. data/lib/mail/fields/resent_bcc_field.rb +55 -0
  71. data/lib/mail/fields/resent_cc_field.rb +55 -0
  72. data/lib/mail/fields/resent_date_field.rb +35 -0
  73. data/lib/mail/fields/resent_from_field.rb +55 -0
  74. data/lib/mail/fields/resent_message_id_field.rb +34 -0
  75. data/lib/mail/fields/resent_sender_field.rb +62 -0
  76. data/lib/mail/fields/resent_to_field.rb +55 -0
  77. data/lib/mail/fields/return_path_field.rb +65 -0
  78. data/lib/mail/fields/sender_field.rb +67 -0
  79. data/lib/mail/fields/structured_field.rb +51 -0
  80. data/lib/mail/fields/subject_field.rb +16 -0
  81. data/lib/mail/fields/to_field.rb +55 -0
  82. data/lib/mail/fields/unstructured_field.rb +191 -0
  83. data/lib/mail/header.rb +265 -0
  84. data/lib/mail/indifferent_hash.rb +146 -0
  85. data/lib/mail/mail.rb +255 -0
  86. data/lib/mail/matchers/has_sent_mail.rb +124 -0
  87. data/lib/mail/message.rb +2059 -0
  88. data/lib/mail/multibyte.rb +42 -0
  89. data/lib/mail/multibyte/chars.rb +474 -0
  90. data/lib/mail/multibyte/exceptions.rb +8 -0
  91. data/lib/mail/multibyte/unicode.rb +392 -0
  92. data/lib/mail/multibyte/utils.rb +60 -0
  93. data/lib/mail/network.rb +14 -0
  94. data/lib/mail/network/delivery_methods/exim.rb +53 -0
  95. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  96. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  97. data/lib/mail/network/delivery_methods/smtp.rb +153 -0
  98. data/lib/mail/network/delivery_methods/smtp_connection.rb +74 -0
  99. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  100. data/lib/mail/network/retriever_methods/base.rb +63 -0
  101. data/lib/mail/network/retriever_methods/imap.rb +168 -0
  102. data/lib/mail/network/retriever_methods/pop3.rb +140 -0
  103. data/lib/mail/network/retriever_methods/test_retriever.rb +47 -0
  104. data/lib/mail/parsers/address_lists.rb +64 -0
  105. data/lib/mail/parsers/address_lists.treetop +19 -0
  106. data/lib/mail/parsers/content_disposition.rb +535 -0
  107. data/lib/mail/parsers/content_disposition.treetop +46 -0
  108. data/lib/mail/parsers/content_location.rb +139 -0
  109. data/lib/mail/parsers/content_location.treetop +20 -0
  110. data/lib/mail/parsers/content_transfer_encoding.rb +162 -0
  111. data/lib/mail/parsers/content_transfer_encoding.treetop +20 -0
  112. data/lib/mail/parsers/content_type.rb +967 -0
  113. data/lib/mail/parsers/content_type.treetop +68 -0
  114. data/lib/mail/parsers/date_time.rb +114 -0
  115. data/lib/mail/parsers/date_time.treetop +11 -0
  116. data/lib/mail/parsers/envelope_from.rb +194 -0
  117. data/lib/mail/parsers/envelope_from.treetop +32 -0
  118. data/lib/mail/parsers/message_ids.rb +45 -0
  119. data/lib/mail/parsers/message_ids.treetop +15 -0
  120. data/lib/mail/parsers/mime_version.rb +144 -0
  121. data/lib/mail/parsers/mime_version.treetop +19 -0
  122. data/lib/mail/parsers/phrase_lists.rb +45 -0
  123. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  124. data/lib/mail/parsers/received.rb +71 -0
  125. data/lib/mail/parsers/received.treetop +11 -0
  126. data/lib/mail/parsers/rfc2045.rb +464 -0
  127. data/lib/mail/parsers/rfc2045.treetop +36 -0
  128. data/lib/mail/parsers/rfc2822.rb +5341 -0
  129. data/lib/mail/parsers/rfc2822.treetop +410 -0
  130. data/lib/mail/parsers/rfc2822_obsolete.rb +3768 -0
  131. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  132. data/lib/mail/part.rb +116 -0
  133. data/lib/mail/parts_list.rb +55 -0
  134. data/lib/mail/patterns.rb +34 -0
  135. data/lib/mail/utilities.rb +215 -0
  136. data/lib/mail/version.rb +24 -0
  137. data/lib/mail/version_specific/ruby_1_8.rb +98 -0
  138. data/lib/mail/version_specific/ruby_1_9.rb +113 -0
  139. data/lib/tasks/corpus.rake +125 -0
  140. data/lib/tasks/treetop.rake +10 -0
  141. metadata +253 -0
@@ -0,0 +1,265 @@
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, charset = nil)
37
+ @errors = []
38
+ @charset = charset
39
+ self.raw_source = header_text.to_crlf
40
+ split_header if header_text
41
+ end
42
+
43
+ # The preserved raw source of the header as you passed it in, untouched
44
+ # for your Regexing glory.
45
+ def raw_source
46
+ @raw_source
47
+ end
48
+
49
+ # Returns an array of all the fields in the header in order that they
50
+ # were read in.
51
+ def fields
52
+ @fields ||= FieldList.new
53
+ end
54
+
55
+ # 3.6. Field definitions
56
+ #
57
+ # It is important to note that the header fields are not guaranteed to
58
+ # be in a particular order. They may appear in any order, and they
59
+ # have been known to be reordered occasionally when transported over
60
+ # the Internet. However, for the purposes of this standard, header
61
+ # fields SHOULD NOT be reordered when a message is transported or
62
+ # transformed. More importantly, the trace header fields and resent
63
+ # header fields MUST NOT be reordered, and SHOULD be kept in blocks
64
+ # prepended to the message. See sections 3.6.6 and 3.6.7 for more
65
+ # information.
66
+ #
67
+ # Populates the fields container with Field objects in the order it
68
+ # receives them in.
69
+ #
70
+ # Acceps an array of field string values, for example:
71
+ #
72
+ # h = Header.new
73
+ # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
74
+ def fields=(unfolded_fields)
75
+ @fields = Mail::FieldList.new
76
+ warn "Warning: more than 1000 header fields only using the first 1000" if unfolded_fields.length > 1000
77
+ unfolded_fields[0..1000].each do |field|
78
+
79
+ field = Field.new(field, nil, charset)
80
+ field.errors.each { |error| self.errors << error }
81
+ selected = select_field_for(field.name)
82
+
83
+ if selected.any? && limited_field?(field.name)
84
+ selected.first.update(field.name, field.value)
85
+ else
86
+ @fields << field
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ def errors
93
+ @errors
94
+ end
95
+
96
+ # 3.6. Field definitions
97
+ #
98
+ # The following table indicates limits on the number of times each
99
+ # field may occur in a message header as well as any special
100
+ # limitations on the use of those fields. An asterisk next to a value
101
+ # in the minimum or maximum column indicates that a special restriction
102
+ # appears in the Notes column.
103
+ #
104
+ # <snip table from 3.6>
105
+ #
106
+ # As per RFC, many fields can appear more than once, we will return a string
107
+ # of the value if there is only one header, or if there is more than one
108
+ # matching header, will return an array of values in order that they appear
109
+ # in the header ordered from top to bottom.
110
+ #
111
+ # Example:
112
+ #
113
+ # h = Header.new
114
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
115
+ # h['To'] #=> 'mikel@me.com'
116
+ # h['X-Mail-SPAM'] #=> ['15', '20']
117
+ def [](name)
118
+ name = dasherize(name).downcase
119
+ selected = select_field_for(name)
120
+ case
121
+ when selected.length > 1
122
+ selected.map { |f| f }
123
+ when !selected.blank?
124
+ selected.first
125
+ else
126
+ nil
127
+ end
128
+ end
129
+
130
+ # Sets the FIRST matching field in the header to passed value, or deletes
131
+ # the FIRST field matched from the header if passed nil
132
+ #
133
+ # Example:
134
+ #
135
+ # h = Header.new
136
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
137
+ # h['To'] = 'bob@you.com'
138
+ # h['To'] #=> 'bob@you.com'
139
+ # h['X-Mail-SPAM'] = '10000'
140
+ # h['X-Mail-SPAM'] # => ['15', '20', '10000']
141
+ # h['X-Mail-SPAM'] = nil
142
+ # h['X-Mail-SPAM'] # => nil
143
+ def []=(name, value)
144
+ name = dasherize(name)
145
+ fn = name.downcase
146
+ selected = select_field_for(fn)
147
+
148
+ case
149
+ # User wants to delete the field
150
+ when !selected.blank? && value == nil
151
+ fields.delete_if { |f| selected.include?(f) }
152
+
153
+ # User wants to change the field
154
+ when !selected.blank? && limited_field?(fn)
155
+ selected.first.update(fn, value)
156
+
157
+ # User wants to create the field
158
+ else
159
+ # Need to insert in correct order for trace fields
160
+ self.fields << Field.new(name.to_s, value, charset)
161
+ end
162
+ end
163
+
164
+ def charset
165
+ params = self[:content_type].parameters rescue nil
166
+ if params
167
+ params[:charset]
168
+ else
169
+ @charset
170
+ end
171
+ end
172
+
173
+ def charset=(val)
174
+ params = self[:content_type].parameters rescue nil
175
+ if params
176
+ params[:charset] = val
177
+ end
178
+ @charset = val
179
+ end
180
+
181
+ LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
182
+ message-id in-reply-to references subject
183
+ return-path content-type mime-version
184
+ content-transfer-encoding content-description
185
+ content-id content-disposition content-location]
186
+
187
+ def encoded
188
+ buffer = ''
189
+ fields.each do |field|
190
+ buffer << field.encoded
191
+ end
192
+ buffer
193
+ end
194
+
195
+ def to_s
196
+ encoded
197
+ end
198
+
199
+ def decoded
200
+ raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts, try calling #decoded on the various fields.'
201
+ end
202
+
203
+ def field_summary
204
+ fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
205
+ end
206
+
207
+ # Returns true if the header has a Message-ID defined (empty or not)
208
+ def has_message_id?
209
+ !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
210
+ end
211
+
212
+ # Returns true if the header has a Content-ID defined (empty or not)
213
+ def has_content_id?
214
+ !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
215
+ end
216
+
217
+ # Returns true if the header has a Date defined (empty or not)
218
+ def has_date?
219
+ !fields.select { |f| f.responsible_for?('Date') }.empty?
220
+ end
221
+
222
+ # Returns true if the header has a MIME version defined (empty or not)
223
+ def has_mime_version?
224
+ !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
225
+ end
226
+
227
+ private
228
+
229
+ def raw_source=(val)
230
+ @raw_source = val
231
+ end
232
+
233
+ # 2.2.3. Long Header Fields
234
+ #
235
+ # The process of moving from this folded multiple-line representation
236
+ # of a header field to its single line representation is called
237
+ # "unfolding". Unfolding is accomplished by simply removing any CRLF
238
+ # that is immediately followed by WSP. Each header field should be
239
+ # treated in its unfolded form for further syntactic and semantic
240
+ # evaluation.
241
+ def unfold(string)
242
+ string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
243
+ end
244
+
245
+ # Returns the header with all the folds removed
246
+ def unfolded_header
247
+ @unfolded_header ||= unfold(raw_source)
248
+ end
249
+
250
+ # Splits an unfolded and line break cleaned header into individual field
251
+ # strings.
252
+ def split_header
253
+ self.fields = unfolded_header.split(CRLF)
254
+ end
255
+
256
+ def select_field_for(name)
257
+ fields.select { |f| f.responsible_for?(name.to_s) }
258
+ end
259
+
260
+ def limited_field?(name)
261
+ LIMITED_FIELDS.include?(name.to_s.downcase)
262
+ end
263
+
264
+ end
265
+ end
@@ -0,0 +1,146 @@
1
+ # encoding: utf-8
2
+
3
+ # This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
4
+ # itself does not depend on ActiveSupport to avoid versioning conflicts
5
+
6
+ module Mail
7
+ class IndifferentHash < Hash
8
+
9
+ def initialize(constructor = {})
10
+ if constructor.is_a?(Hash)
11
+ super()
12
+ update(constructor)
13
+ else
14
+ super(constructor)
15
+ end
16
+ end
17
+
18
+ def default(key = nil)
19
+ if key.is_a?(Symbol) && include?(key = key.to_s)
20
+ self[key]
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def self.new_from_hash_copying_default(hash)
27
+ IndifferentHash.new(hash).tap do |new_hash|
28
+ new_hash.default = hash.default
29
+ end
30
+ end
31
+
32
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
33
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
34
+
35
+ # Assigns a new value to the hash:
36
+ #
37
+ # hash = HashWithIndifferentAccess.new
38
+ # hash[:key] = "value"
39
+ #
40
+ def []=(key, value)
41
+ regular_writer(convert_key(key), convert_value(value))
42
+ end
43
+
44
+ alias_method :store, :[]=
45
+
46
+ # Updates the instantized hash with values from the second:
47
+ #
48
+ # hash_1 = HashWithIndifferentAccess.new
49
+ # hash_1[:key] = "value"
50
+ #
51
+ # hash_2 = HashWithIndifferentAccess.new
52
+ # hash_2[:key] = "New Value!"
53
+ #
54
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
55
+ #
56
+ def update(other_hash)
57
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
58
+ self
59
+ end
60
+
61
+ alias_method :merge!, :update
62
+
63
+ # Checks the hash for a key matching the argument passed in:
64
+ #
65
+ # hash = HashWithIndifferentAccess.new
66
+ # hash["key"] = "value"
67
+ # hash.key? :key # => true
68
+ # hash.key? "key" # => true
69
+ #
70
+ def key?(key)
71
+ super(convert_key(key))
72
+ end
73
+
74
+ alias_method :include?, :key?
75
+ alias_method :has_key?, :key?
76
+ alias_method :member?, :key?
77
+
78
+ # Fetches the value for the specified key, same as doing hash[key]
79
+ def fetch(key, *extras)
80
+ super(convert_key(key), *extras)
81
+ end
82
+
83
+ # Returns an array of the values at the specified indices:
84
+ #
85
+ # hash = HashWithIndifferentAccess.new
86
+ # hash[:a] = "x"
87
+ # hash[:b] = "y"
88
+ # hash.values_at("a", "b") # => ["x", "y"]
89
+ #
90
+ def values_at(*indices)
91
+ indices.collect {|key| self[convert_key(key)]}
92
+ end
93
+
94
+ # Returns an exact copy of the hash.
95
+ def dup
96
+ IndifferentHash.new(self)
97
+ end
98
+
99
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
100
+ # Does not overwrite the existing hash.
101
+ def merge(hash)
102
+ self.dup.update(hash)
103
+ end
104
+
105
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
106
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
107
+ def reverse_merge(other_hash)
108
+ super self.class.new_from_hash_copying_default(other_hash)
109
+ end
110
+
111
+ def reverse_merge!(other_hash)
112
+ replace(reverse_merge( other_hash ))
113
+ end
114
+
115
+ # Removes a specified key from the hash.
116
+ def delete(key)
117
+ super(convert_key(key))
118
+ end
119
+
120
+ def stringify_keys!; self end
121
+ def stringify_keys; dup end
122
+ def symbolize_keys; to_hash.symbolize_keys end
123
+ def to_options!; self end
124
+
125
+ def to_hash
126
+ Hash.new(default).merge!(self)
127
+ end
128
+
129
+ protected
130
+
131
+ def convert_key(key)
132
+ key.kind_of?(Symbol) ? key.to_s : key
133
+ end
134
+
135
+ def convert_value(value)
136
+ if value.class == Hash
137
+ self.class.new_from_hash_copying_default(value)
138
+ elsif value.is_a?(Array)
139
+ value.dup.replace(value.map { |e| convert_value(e) })
140
+ else
141
+ value
142
+ end
143
+ end
144
+
145
+ end
146
+ end
data/lib/mail/mail.rb ADDED
@@ -0,0 +1,255 @@
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"
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 self.new(*args, &block)
50
+ 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 self.defaults(&block)
106
+ Configuration.instance.instance_eval(&block)
107
+ end
108
+
109
+ # Returns the delivery method selected, defaults to an instance of Mail::SMTP
110
+ def self.delivery_method
111
+ Configuration.instance.delivery_method
112
+ end
113
+
114
+ # Returns the retriever method selected, defaults to an instance of Mail::POP3
115
+ def self.retriever_method
116
+ 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 self.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 self.deliver(*args, &block)
139
+ mail = self.new(args, &block)
140
+ mail.deliver
141
+ mail
142
+ end
143
+
144
+ # Find emails from the default retriever
145
+ # See Mail::Retriever for a complete documentation.
146
+ def self.find(*args, &block)
147
+ retriever_method.find(*args, &block)
148
+ end
149
+
150
+ # Finds and then deletes retrieved emails from the default retriever
151
+ # See Mail::Retriever for a complete documentation.
152
+ def self.find_and_delete(*args, &block)
153
+ retriever_method.find_and_delete(*args, &block)
154
+ end
155
+
156
+ # Receive the first email(s) from the default retriever
157
+ # See Mail::Retriever for a complete documentation.
158
+ def self.first(*args, &block)
159
+ retriever_method.first(*args, &block)
160
+ end
161
+
162
+ # Receive the first email(s) from the default retriever
163
+ # See Mail::Retriever for a complete documentation.
164
+ def self.last(*args, &block)
165
+ retriever_method.last(*args, &block)
166
+ end
167
+
168
+ # Receive all emails from the default retriever
169
+ # See Mail::Retriever for a complete documentation.
170
+ def self.all(*args, &block)
171
+ retriever_method.all(*args, &block)
172
+ end
173
+
174
+ # Reads in an email message from a path and instantiates it as a new Mail::Message
175
+ def self.read(filename)
176
+ self.new(File.open(filename, 'rb') { |f| f.read })
177
+ end
178
+
179
+ # Delete all emails from the default retriever
180
+ # See Mail::Retriever for a complete documentation.
181
+ def self.delete_all(*args, &block)
182
+ retriever_method.delete_all(*args, &block)
183
+ end
184
+
185
+ # Instantiates a new Mail::Message using a string
186
+ def Mail.read_from_string(mail_as_string)
187
+ Mail.new(mail_as_string)
188
+ end
189
+
190
+ def Mail.connection(&block)
191
+ retriever_method.connection(&block)
192
+ end
193
+
194
+ # Initialize the observers and interceptors arrays
195
+ @@delivery_notification_observers = []
196
+ @@delivery_interceptors = []
197
+
198
+ # You can register an object to be informed of every email that is sent through
199
+ # this method.
200
+ #
201
+ # Your object needs to respond to a single method #delivered_email(mail)
202
+ # which receives the email that is sent.
203
+ def self.register_observer(observer)
204
+ unless @@delivery_notification_observers.include?(observer)
205
+ @@delivery_notification_observers << observer
206
+ end
207
+ end
208
+
209
+ # You can register an object to be given every mail object that will be sent,
210
+ # before it is sent. So if you want to add special headers or modify any
211
+ # email that gets sent through the Mail library, you can do so.
212
+ #
213
+ # Your object needs to respond to a single method #delivering_email(mail)
214
+ # which receives the email that is about to be sent. Make your modifications
215
+ # directly to this object.
216
+ def self.register_interceptor(interceptor)
217
+ unless @@delivery_interceptors.include?(interceptor)
218
+ @@delivery_interceptors << interceptor
219
+ end
220
+ end
221
+
222
+ def self.inform_observers(mail)
223
+ @@delivery_notification_observers.each do |observer|
224
+ observer.delivered_email(mail)
225
+ end
226
+ end
227
+
228
+ def self.inform_interceptors(mail)
229
+ @@delivery_interceptors.each do |interceptor|
230
+ interceptor.delivering_email(mail)
231
+ end
232
+ end
233
+
234
+ protected
235
+
236
+ def self.random_tag
237
+ t = Time.now
238
+ sprintf('%x%x_%x%x%d%x',
239
+ t.to_i, t.tv_usec,
240
+ $$, Thread.current.object_id.abs, self.uniq, rand(255))
241
+ end
242
+
243
+ private
244
+
245
+ def self.something_random
246
+ (Thread.current.object_id * rand(255) / Time.now.to_f).to_s.slice(-3..-1).to_i
247
+ end
248
+
249
+ def self.uniq
250
+ @@uniq += 1
251
+ end
252
+
253
+ @@uniq = self.something_random
254
+
255
+ end