mail 2.4.4 → 2.6.3
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.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +156 -0
- data/CONTRIBUTING.md +21 -6
- data/Dependencies.txt +0 -1
- data/Gemfile +10 -21
- data/MIT-LICENSE +20 -0
- data/README.md +52 -32
- data/Rakefile +11 -22
- data/lib/mail/attachments_list.rb +2 -2
- data/lib/mail/body.rb +33 -9
- data/lib/mail/check_delivery_params.rb +20 -0
- data/lib/mail/configuration.rb +1 -1
- data/lib/mail/{patterns.rb → constants.rb} +26 -6
- data/lib/mail/core_extensions/nil.rb +4 -2
- data/lib/mail/core_extensions/object.rb +8 -8
- data/lib/mail/core_extensions/smtp.rb +12 -13
- data/lib/mail/core_extensions/string.rb +19 -6
- data/lib/mail/elements/address.rb +82 -141
- data/lib/mail/elements/address_list.rb +9 -42
- data/lib/mail/elements/content_disposition_element.rb +3 -7
- data/lib/mail/elements/content_location_element.rb +2 -6
- data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
- data/lib/mail/elements/content_type_element.rb +4 -8
- data/lib/mail/elements/date_time_element.rb +3 -7
- data/lib/mail/elements/envelope_from_element.rb +18 -13
- data/lib/mail/elements/message_ids_element.rb +1 -6
- data/lib/mail/elements/mime_version_element.rb +3 -7
- data/lib/mail/elements/phrase_list.rb +2 -7
- data/lib/mail/elements/received_element.rb +3 -7
- data/lib/mail/elements.rb +12 -12
- data/lib/mail/encodings/quoted_printable.rb +4 -3
- data/lib/mail/encodings/unix_to_unix.rb +17 -0
- data/lib/mail/encodings.rb +53 -38
- data/lib/mail/envelope.rb +0 -5
- data/lib/mail/field.rb +131 -118
- data/lib/mail/field_list.rb +18 -18
- data/lib/mail/fields/bcc_field.rb +2 -2
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +1 -1
- data/lib/mail/fields/common/common_address.rb +32 -22
- data/lib/mail/fields/common/common_date.rb +0 -7
- data/lib/mail/fields/common/common_field.rb +10 -3
- data/lib/mail/fields/common/common_message_id.rb +9 -5
- data/lib/mail/fields/common/parameter_hash.rb +3 -3
- data/lib/mail/fields/content_disposition_field.rb +1 -0
- data/lib/mail/fields/content_id_field.rb +1 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
- data/lib/mail/fields/content_type_field.rb +5 -2
- data/lib/mail/fields/date_field.rb +14 -14
- data/lib/mail/fields/from_field.rb +2 -2
- data/lib/mail/fields/in_reply_to_field.rb +2 -1
- data/lib/mail/fields/keywords_field.rb +1 -1
- data/lib/mail/fields/message_id_field.rb +2 -3
- data/lib/mail/fields/references_field.rb +2 -1
- data/lib/mail/fields/reply_to_field.rb +2 -2
- data/lib/mail/fields/resent_bcc_field.rb +2 -2
- data/lib/mail/fields/resent_cc_field.rb +2 -2
- data/lib/mail/fields/resent_from_field.rb +2 -2
- data/lib/mail/fields/resent_sender_field.rb +3 -3
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +8 -8
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +44 -31
- data/lib/mail/fields.rb +32 -32
- data/lib/mail/header.rb +47 -37
- data/lib/mail/mail.rb +15 -1
- data/lib/mail/matchers/has_sent_mail.rb +36 -3
- data/lib/mail/message.rb +164 -57
- data/lib/mail/multibyte/chars.rb +4 -4
- data/lib/mail/multibyte/unicode.rb +9 -1
- data/lib/mail/network/delivery_methods/exim.rb +2 -3
- data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
- data/lib/mail/network/delivery_methods/smtp.rb +34 -45
- data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
- data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
- data/lib/mail/network/retriever_methods/imap.rb +32 -19
- data/lib/mail/network/retriever_methods/pop3.rb +2 -2
- data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
- data/lib/mail/network.rb +9 -9
- data/lib/mail/parsers/address_lists_parser.rb +132 -0
- data/lib/mail/parsers/content_disposition_parser.rb +67 -0
- data/lib/mail/parsers/content_location_parser.rb +35 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
- data/lib/mail/parsers/content_type_parser.rb +66 -0
- data/lib/mail/parsers/date_time_parser.rb +36 -0
- data/lib/mail/parsers/envelope_from_parser.rb +45 -0
- data/lib/mail/parsers/message_ids_parser.rb +39 -0
- data/lib/mail/parsers/mime_version_parser.rb +41 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
- data/lib/mail/parsers/ragel/common.rl +185 -0
- data/lib/mail/parsers/ragel/date_time.rl +30 -0
- data/lib/mail/parsers/ragel/parser_info.rb +61 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
- data/lib/mail/parsers/ragel/ruby.rb +40 -0
- data/lib/mail/parsers/ragel.rb +17 -0
- data/lib/mail/parsers/received_parser.rb +47 -0
- data/lib/mail/parsers.rb +26 -0
- data/lib/mail/part.rb +6 -2
- data/lib/mail/parts_list.rb +5 -3
- data/lib/mail/utilities.rb +70 -60
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +7 -15
- data/lib/mail/version_specific/ruby_1_8.rb +25 -4
- data/lib/mail/version_specific/ruby_1_9.rb +110 -30
- data/lib/mail.rb +20 -19
- metadata +129 -73
- data/Gemfile.lock +0 -36
- data/lib/VERSION +0 -4
- data/lib/mail/core_extensions/shell_escape.rb +0 -56
- data/lib/mail/parsers/address_lists.rb +0 -64
- data/lib/mail/parsers/address_lists.treetop +0 -19
- data/lib/mail/parsers/content_disposition.rb +0 -535
- data/lib/mail/parsers/content_disposition.treetop +0 -46
- data/lib/mail/parsers/content_location.rb +0 -139
- data/lib/mail/parsers/content_location.treetop +0 -20
- data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
- data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
- data/lib/mail/parsers/content_type.rb +0 -967
- data/lib/mail/parsers/content_type.treetop +0 -68
- data/lib/mail/parsers/date_time.rb +0 -114
- data/lib/mail/parsers/date_time.treetop +0 -11
- data/lib/mail/parsers/envelope_from.rb +0 -194
- data/lib/mail/parsers/envelope_from.treetop +0 -32
- data/lib/mail/parsers/message_ids.rb +0 -45
- data/lib/mail/parsers/message_ids.treetop +0 -15
- data/lib/mail/parsers/mime_version.rb +0 -144
- data/lib/mail/parsers/mime_version.treetop +0 -19
- data/lib/mail/parsers/phrase_lists.rb +0 -45
- data/lib/mail/parsers/phrase_lists.treetop +0 -15
- data/lib/mail/parsers/received.rb +0 -71
- data/lib/mail/parsers/received.treetop +0 -11
- data/lib/mail/parsers/rfc2045.rb +0 -464
- data/lib/mail/parsers/rfc2045.treetop +0 -36
- data/lib/mail/parsers/rfc2822.rb +0 -5341
- data/lib/mail/parsers/rfc2822.treetop +0 -410
- data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
- data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
- data/lib/tasks/corpus.rake +0 -125
- data/lib/tasks/treetop.rake +0 -10
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'mail/check_delivery_params'
|
|
2
|
+
|
|
1
3
|
module Mail
|
|
2
4
|
# == Sending Email with SMTP
|
|
3
5
|
#
|
|
@@ -72,6 +74,7 @@ module Mail
|
|
|
72
74
|
#
|
|
73
75
|
# mail.deliver!
|
|
74
76
|
class SMTP
|
|
77
|
+
include Mail::CheckDeliveryParams
|
|
75
78
|
|
|
76
79
|
def initialize(values)
|
|
77
80
|
self.settings = { :address => "localhost",
|
|
@@ -86,68 +89,54 @@ module Mail
|
|
|
86
89
|
:tls => nil
|
|
87
90
|
}.merge!(values)
|
|
88
91
|
end
|
|
89
|
-
|
|
92
|
+
|
|
90
93
|
attr_accessor :settings
|
|
91
|
-
|
|
94
|
+
|
|
92
95
|
# Send the message via SMTP.
|
|
93
96
|
# The from and to attributes are optional. If not set, they are retrieve from the Message.
|
|
94
97
|
def deliver!(mail)
|
|
98
|
+
smtp_from, smtp_to, message = check_delivery_params(mail)
|
|
95
99
|
|
|
96
|
-
# Set the envelope from to be either the return-path, the sender or the first from address
|
|
97
|
-
envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
|
|
98
|
-
if envelope_from.blank?
|
|
99
|
-
raise ArgumentError.new('A sender (Return-Path, Sender or From) required to send a message')
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
destinations ||= mail.destinations if mail.respond_to?(:destinations) && mail.destinations
|
|
103
|
-
if destinations.blank?
|
|
104
|
-
raise ArgumentError.new('At least one recipient (To, Cc or Bcc) is required to send a message')
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
message ||= mail.encoded if mail.respond_to?(:encoded)
|
|
108
|
-
if message.blank?
|
|
109
|
-
raise ArgumentError.new('A encoded content is required to send a message')
|
|
110
|
-
end
|
|
111
|
-
|
|
112
100
|
smtp = Net::SMTP.new(settings[:address], settings[:port])
|
|
113
101
|
if settings[:tls] || settings[:ssl]
|
|
114
102
|
if smtp.respond_to?(:enable_tls)
|
|
115
|
-
|
|
116
|
-
smtp.enable_tls
|
|
117
|
-
else
|
|
118
|
-
openssl_verify_mode = settings[:openssl_verify_mode]
|
|
119
|
-
if openssl_verify_mode.kind_of?(String)
|
|
120
|
-
openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
|
|
121
|
-
end
|
|
122
|
-
context = Net::SMTP.default_ssl_context
|
|
123
|
-
context.verify_mode = openssl_verify_mode
|
|
124
|
-
smtp.enable_tls(context)
|
|
125
|
-
end
|
|
103
|
+
smtp.enable_tls(ssl_context)
|
|
126
104
|
end
|
|
127
105
|
elsif settings[:enable_starttls_auto]
|
|
128
|
-
if smtp.respond_to?(:enable_starttls_auto)
|
|
129
|
-
|
|
130
|
-
smtp.enable_starttls_auto
|
|
131
|
-
else
|
|
132
|
-
openssl_verify_mode = settings[:openssl_verify_mode]
|
|
133
|
-
if openssl_verify_mode.kind_of?(String)
|
|
134
|
-
openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
|
|
135
|
-
end
|
|
136
|
-
context = Net::SMTP.default_ssl_context
|
|
137
|
-
context.verify_mode = openssl_verify_mode
|
|
138
|
-
smtp.enable_starttls_auto(context)
|
|
139
|
-
end
|
|
106
|
+
if smtp.respond_to?(:enable_starttls_auto)
|
|
107
|
+
smtp.enable_starttls_auto(ssl_context)
|
|
140
108
|
end
|
|
141
109
|
end
|
|
142
|
-
|
|
110
|
+
|
|
143
111
|
response = nil
|
|
144
112
|
smtp.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do |smtp_obj|
|
|
145
|
-
response = smtp_obj.sendmail(message,
|
|
113
|
+
response = smtp_obj.sendmail(message, smtp_from, smtp_to)
|
|
146
114
|
end
|
|
147
115
|
|
|
148
|
-
|
|
116
|
+
if settings[:return_response]
|
|
117
|
+
response
|
|
118
|
+
else
|
|
119
|
+
self
|
|
120
|
+
end
|
|
149
121
|
end
|
|
150
122
|
|
|
151
|
-
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
# Allow SSL context to be configured via settings, for Ruby >= 1.9
|
|
127
|
+
# Just returns openssl verify mode for Ruby 1.8.x
|
|
128
|
+
def ssl_context
|
|
129
|
+
openssl_verify_mode = settings[:openssl_verify_mode]
|
|
130
|
+
|
|
131
|
+
if openssl_verify_mode.kind_of?(String)
|
|
132
|
+
openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
context = Net::SMTP.default_ssl_context
|
|
136
|
+
context.verify_mode = openssl_verify_mode
|
|
137
|
+
context.ca_path = settings[:ca_path] if settings[:ca_path]
|
|
138
|
+
context.ca_file = settings[:ca_file] if settings[:ca_file]
|
|
139
|
+
context
|
|
140
|
+
end
|
|
152
141
|
end
|
|
153
142
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'mail/check_delivery_params'
|
|
2
|
+
|
|
1
3
|
module Mail
|
|
2
4
|
# == Sending Email with SMTP
|
|
3
5
|
#
|
|
@@ -35,40 +37,25 @@ module Mail
|
|
|
35
37
|
#
|
|
36
38
|
# mail.deliver!
|
|
37
39
|
class SMTPConnection
|
|
40
|
+
include Mail::CheckDeliveryParams
|
|
38
41
|
|
|
39
42
|
def initialize(values)
|
|
40
43
|
raise ArgumentError.new('A Net::SMTP object is required for this delivery method') if values[:connection].nil?
|
|
41
44
|
self.smtp = values[:connection]
|
|
42
45
|
self.settings = values
|
|
43
46
|
end
|
|
44
|
-
|
|
47
|
+
|
|
45
48
|
attr_accessor :smtp
|
|
46
49
|
attr_accessor :settings
|
|
47
|
-
|
|
50
|
+
|
|
48
51
|
# Send the message via SMTP.
|
|
49
52
|
# The from and to attributes are optional. If not set, they are retrieve from the Message.
|
|
50
53
|
def deliver!(mail)
|
|
54
|
+
smtp_from, smtp_to, message = check_delivery_params(mail)
|
|
55
|
+
response = smtp.sendmail(message, smtp_from, smtp_to)
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
|
|
54
|
-
if envelope_from.blank?
|
|
55
|
-
raise ArgumentError.new('A sender (Return-Path, Sender or From) required to send a message')
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
destinations ||= mail.destinations if mail.respond_to?(:destinations) && mail.destinations
|
|
59
|
-
if destinations.blank?
|
|
60
|
-
raise ArgumentError.new('At least one recipient (To, Cc or Bcc) is required to send a message')
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
message ||= mail.encoded if mail.respond_to?(:encoded)
|
|
64
|
-
if message.blank?
|
|
65
|
-
raise ArgumentError.new('A encoded content is required to send a message')
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
response = smtp.sendmail(message, envelope_from, destinations)
|
|
69
|
-
|
|
70
|
-
settings[:return_response] ? response : self
|
|
57
|
+
settings[:return_response] ? response : self
|
|
71
58
|
end
|
|
72
|
-
|
|
59
|
+
|
|
73
60
|
end
|
|
74
61
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'mail/check_delivery_params'
|
|
2
|
+
|
|
1
3
|
module Mail
|
|
2
4
|
# The TestMailer is a bare bones mailer that does nothing. It is useful
|
|
3
5
|
# when you are testing.
|
|
@@ -5,6 +7,7 @@ module Mail
|
|
|
5
7
|
# It also provides a template of the minimum methods you require to implement
|
|
6
8
|
# if you want to make a custom mailer for Mail
|
|
7
9
|
class TestMailer
|
|
10
|
+
include Mail::CheckDeliveryParams
|
|
8
11
|
|
|
9
12
|
# Provides a store of all the emails sent with the TestMailer so you can check them.
|
|
10
13
|
def TestMailer.deliveries
|
|
@@ -27,14 +30,15 @@ module Mail
|
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
def initialize(values)
|
|
30
|
-
@settings =
|
|
33
|
+
@settings = values.dup
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
attr_accessor :settings
|
|
34
37
|
|
|
35
38
|
def deliver!(mail)
|
|
39
|
+
check_delivery_params(mail)
|
|
36
40
|
Mail::TestMailer.deliveries << mail
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
end
|
|
40
|
-
end
|
|
44
|
+
end
|
|
@@ -18,7 +18,7 @@ module Mail
|
|
|
18
18
|
#
|
|
19
19
|
# Mail.all #=> Returns an array of all emails
|
|
20
20
|
# Mail.first #=> Returns the first unread email
|
|
21
|
-
# Mail.last #=> Returns the
|
|
21
|
+
# Mail.last #=> Returns the last unread email
|
|
22
22
|
#
|
|
23
23
|
# You can also pass options into Mail.find to locate an email in your imap mailbox
|
|
24
24
|
# with the following options:
|
|
@@ -28,12 +28,15 @@ module Mail
|
|
|
28
28
|
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
|
|
29
29
|
# count: number of emails to retrieve. The default value is 10. A value of 1 returns an
|
|
30
30
|
# instance of Message, not an array of Message instances.
|
|
31
|
+
# keys: are passed as criteria to the SEARCH command. They can either be a string holding the entire search string,
|
|
32
|
+
# or a single-dimension array of search keywords and arguments. Refer to [IMAP] section 6.4.4 for a full list
|
|
33
|
+
# The default is 'ALL'
|
|
31
34
|
#
|
|
32
|
-
# Mail.find(:what => :first, :count => 10, :order => :asc)
|
|
35
|
+
# Mail.find(:what => :first, :count => 10, :order => :asc, :keys=>'ALL')
|
|
33
36
|
# #=> Returns the first 10 emails in ascending order
|
|
34
37
|
#
|
|
35
38
|
class IMAP < Retriever
|
|
36
|
-
require 'net/imap'
|
|
39
|
+
require 'net/imap' unless defined?(Net::IMAP)
|
|
37
40
|
|
|
38
41
|
def initialize(values)
|
|
39
42
|
self.settings = { :address => "localhost",
|
|
@@ -54,42 +57,51 @@ module Mail
|
|
|
54
57
|
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
|
|
55
58
|
# count: number of emails to retrieve. The default value is 10. A value of 1 returns an
|
|
56
59
|
# instance of Message, not an array of Message instances.
|
|
57
|
-
#
|
|
58
|
-
#
|
|
60
|
+
# read_only: will ensure that no writes are made to the inbox during the session. Specifically, if this is
|
|
61
|
+
# set to true, the code will use the EXAMINE command to retrieve the mail. If set to false, which
|
|
62
|
+
# is the default, a SELECT command will be used to retrieve the mail
|
|
63
|
+
# This is helpful when you don't want your messages to be set to read automatically. Default is false.
|
|
59
64
|
# delete_after_find: flag for whether to delete each retreived email after find. Default
|
|
60
65
|
# is false. Use #find_and_delete if you would like this to default to true.
|
|
66
|
+
# keys: are passed as criteria to the SEARCH command. They can either be a string holding the entire search string,
|
|
67
|
+
# or a single-dimension array of search keywords and arguments. Refer to [IMAP] section 6.4.4 for a full list
|
|
68
|
+
# The default is 'ALL'
|
|
61
69
|
#
|
|
62
70
|
def find(options={}, &block)
|
|
63
71
|
options = validate_options(options)
|
|
64
72
|
|
|
65
73
|
start do |imap|
|
|
66
|
-
options[:read_only] ? imap.
|
|
74
|
+
options[:read_only] ? imap.examine(options[:mailbox]) : imap.select(options[:mailbox])
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
uids = imap.uid_search(options[:keys])
|
|
77
|
+
uids.reverse! if options[:what].to_sym == :last
|
|
78
|
+
uids = uids.first(options[:count]) if options[:count].is_a?(Integer)
|
|
79
|
+
uids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
|
|
72
80
|
(options[:what].to_sym != :last && options[:order].to_sym == :desc)
|
|
73
81
|
|
|
74
82
|
if block_given?
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
uids.each do |uid|
|
|
84
|
+
uid = options[:uid].to_i unless options[:uid].nil?
|
|
85
|
+
fetchdata = imap.uid_fetch(uid, ['RFC822'])[0]
|
|
77
86
|
new_message = Mail.new(fetchdata.attr['RFC822'])
|
|
78
87
|
new_message.mark_for_delete = true if options[:delete_after_find]
|
|
79
88
|
if block.arity == 3
|
|
80
|
-
yield new_message, imap,
|
|
89
|
+
yield new_message, imap, uid
|
|
81
90
|
else
|
|
82
91
|
yield new_message
|
|
83
92
|
end
|
|
84
|
-
imap.uid_store(
|
|
93
|
+
imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
|
|
94
|
+
break unless options[:uid].nil?
|
|
85
95
|
end
|
|
86
96
|
imap.expunge if options[:delete_after_find]
|
|
87
97
|
else
|
|
88
98
|
emails = []
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
uids.each do |uid|
|
|
100
|
+
uid = options[:uid].to_i unless options[:uid].nil?
|
|
101
|
+
fetchdata = imap.uid_fetch(uid, ['RFC822'])[0]
|
|
91
102
|
emails << Mail.new(fetchdata.attr['RFC822'])
|
|
92
|
-
imap.uid_store(
|
|
103
|
+
imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find]
|
|
104
|
+
break unless options[:uid].nil?
|
|
93
105
|
end
|
|
94
106
|
imap.expunge if options[:delete_after_find]
|
|
95
107
|
emails.size == 1 && options[:count] == 1 ? emails.first : emails
|
|
@@ -103,8 +115,8 @@ module Mail
|
|
|
103
115
|
mailbox = Net::IMAP.encode_utf7(mailbox)
|
|
104
116
|
|
|
105
117
|
start do |imap|
|
|
106
|
-
imap.uid_search(['ALL']).each do |
|
|
107
|
-
imap.uid_store(
|
|
118
|
+
imap.uid_search(['ALL']).each do |uid|
|
|
119
|
+
imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED])
|
|
108
120
|
end
|
|
109
121
|
imap.expunge
|
|
110
122
|
end
|
|
@@ -129,6 +141,7 @@ module Mail
|
|
|
129
141
|
options[:order] ||= :asc
|
|
130
142
|
options[:what] ||= :first
|
|
131
143
|
options[:keys] ||= 'ALL'
|
|
144
|
+
options[:uid] ||= nil
|
|
132
145
|
options[:delete_after_find] ||= false
|
|
133
146
|
options[:mailbox] = Net::IMAP.encode_utf7(options[:mailbox])
|
|
134
147
|
options[:read_only] ||= false
|
|
@@ -18,7 +18,7 @@ module Mail
|
|
|
18
18
|
#
|
|
19
19
|
# Mail.all #=> Returns an array of all emails
|
|
20
20
|
# Mail.first #=> Returns the first unread email
|
|
21
|
-
# Mail.last #=> Returns the
|
|
21
|
+
# Mail.last #=> Returns the last unread email
|
|
22
22
|
#
|
|
23
23
|
# You can also pass options into Mail.find to locate an email in your pop mailbox
|
|
24
24
|
# with the following options:
|
|
@@ -32,7 +32,7 @@ module Mail
|
|
|
32
32
|
# #=> Returns the first 10 emails in ascending order
|
|
33
33
|
#
|
|
34
34
|
class POP3 < Retriever
|
|
35
|
-
require 'net/pop'
|
|
35
|
+
require 'net/pop' unless defined?(Net::POP)
|
|
36
36
|
|
|
37
37
|
def initialize(values)
|
|
38
38
|
self.settings = { :address => "localhost",
|
|
@@ -20,26 +20,22 @@ module Mail
|
|
|
20
20
|
options[:count] ||= :all
|
|
21
21
|
options[:order] ||= :asc
|
|
22
22
|
options[:what] ||= :first
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
when :all then
|
|
27
|
-
when
|
|
28
|
-
when Fixnum then emails[0, count]
|
|
23
|
+
emails_index = (0...@@emails.size).to_a
|
|
24
|
+
emails_index.reverse! if options[:what] == :last
|
|
25
|
+
emails_index = case count = options[:count]
|
|
26
|
+
when :all then emails_index
|
|
27
|
+
when Fixnum then emails_index[0, count]
|
|
29
28
|
else
|
|
30
29
|
raise 'Invalid count option value: ' + count.inspect
|
|
31
30
|
end
|
|
32
31
|
if options[:what] == :last && options[:order] == :asc || options[:what] == :first && options[:order] == :desc
|
|
33
|
-
|
|
34
|
-
end
|
|
35
|
-
emails.each { |email| email.mark_for_delete = true } if options[:delete_after_find]
|
|
36
|
-
if block_given?
|
|
37
|
-
emails.each { |email| yield email }
|
|
38
|
-
else
|
|
39
|
-
emails
|
|
40
|
-
end.tap do |results|
|
|
41
|
-
emails.each { |email| @@emails.delete(email) if email.is_marked_for_delete? } if options[:delete_after_find]
|
|
32
|
+
emails_index.reverse!
|
|
42
33
|
end
|
|
34
|
+
emails_index.each { |idx| @@emails[idx].mark_for_delete = true } if options[:delete_after_find]
|
|
35
|
+
emails = emails_index.map { |idx| @@emails[idx] }
|
|
36
|
+
emails.each { |email| yield email } if block_given?
|
|
37
|
+
@@emails.reject!(&:is_marked_for_delete?) if options[:delete_after_find]
|
|
38
|
+
emails.size == 1 && options[:count] == 1 ? emails.first : emails
|
|
43
39
|
end
|
|
44
40
|
|
|
45
41
|
end
|
data/lib/mail/network.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
require 'mail/network/retriever_methods/base'
|
|
2
2
|
|
|
3
3
|
module Mail
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
register_autoload :SMTP, 'mail/network/delivery_methods/smtp'
|
|
5
|
+
register_autoload :FileDelivery, 'mail/network/delivery_methods/file_delivery'
|
|
6
|
+
register_autoload :Sendmail, 'mail/network/delivery_methods/sendmail'
|
|
7
|
+
register_autoload :Exim, 'mail/network/delivery_methods/exim'
|
|
8
|
+
register_autoload :SMTPConnection, 'mail/network/delivery_methods/smtp_connection'
|
|
9
|
+
register_autoload :TestMailer, 'mail/network/delivery_methods/test_mailer'
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
register_autoload :POP3, 'mail/network/retriever_methods/pop3'
|
|
12
|
+
register_autoload :IMAP, 'mail/network/retriever_methods/imap'
|
|
13
|
+
register_autoload :TestRetriever, 'mail/network/retriever_methods/test_retriever'
|
|
14
14
|
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Mail::Parsers
|
|
2
|
+
class AddressListsParser
|
|
3
|
+
include Mail::Utilities
|
|
4
|
+
|
|
5
|
+
def parse(s)
|
|
6
|
+
address_list = AddressListStruct.new([],[])
|
|
7
|
+
|
|
8
|
+
if s.blank?
|
|
9
|
+
return address_list
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
actions, error = Ragel.parse(:address_lists, s)
|
|
13
|
+
if error
|
|
14
|
+
raise Mail::Field::ParseError.new(Mail::AddressList, s, error)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
phrase_s = phrase_e = qstr_s = qstr = comment_s = nil
|
|
19
|
+
group_name_s = domain_s = group_name = nil
|
|
20
|
+
local_dot_atom_s = local_dot_atom_e = nil
|
|
21
|
+
local_dot_atom_pre_comment_e = nil
|
|
22
|
+
obs_domain_list_s = nil
|
|
23
|
+
|
|
24
|
+
address_s = 0
|
|
25
|
+
address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
|
|
26
|
+
actions.each_slice(2) do |action_id, p|
|
|
27
|
+
action = Mail::Parsers::Ragel::ACTIONS[action_id]
|
|
28
|
+
case action
|
|
29
|
+
|
|
30
|
+
# Phrase
|
|
31
|
+
when :phrase_s then phrase_s = p
|
|
32
|
+
when :phrase_e then phrase_e = p-1
|
|
33
|
+
|
|
34
|
+
# Quoted String.
|
|
35
|
+
when :qstr_s then qstr_s = p
|
|
36
|
+
when :qstr_e then qstr = s[qstr_s..(p-1)]
|
|
37
|
+
|
|
38
|
+
# Comment
|
|
39
|
+
when :comment_s
|
|
40
|
+
comment_s = p unless comment_s
|
|
41
|
+
when :comment_e
|
|
42
|
+
if address
|
|
43
|
+
address.comments << s[comment_s..(p-2)]
|
|
44
|
+
end
|
|
45
|
+
comment_s = nil
|
|
46
|
+
|
|
47
|
+
# Group Name
|
|
48
|
+
when :group_name_s then group_name_s = p
|
|
49
|
+
when :group_name_e
|
|
50
|
+
if qstr
|
|
51
|
+
group = qstr
|
|
52
|
+
qstr = nil
|
|
53
|
+
else
|
|
54
|
+
group = s[group_name_s..(p-1)]
|
|
55
|
+
group_name_s = nil
|
|
56
|
+
end
|
|
57
|
+
address_list.group_names << group
|
|
58
|
+
group_name = group
|
|
59
|
+
|
|
60
|
+
# Start next address
|
|
61
|
+
address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
|
|
62
|
+
address_s = p
|
|
63
|
+
address.group = group_name
|
|
64
|
+
|
|
65
|
+
# Address
|
|
66
|
+
when :address_s
|
|
67
|
+
address_s = p
|
|
68
|
+
when :address_e
|
|
69
|
+
# Ignore address end events if they don't have
|
|
70
|
+
# a matching address start event.
|
|
71
|
+
next if address_s.nil?
|
|
72
|
+
if address.local.nil? && local_dot_atom_pre_comment_e && local_dot_atom_s && local_dot_atom_e
|
|
73
|
+
if address.domain
|
|
74
|
+
address.local = s[local_dot_atom_s..local_dot_atom_e] if address
|
|
75
|
+
else
|
|
76
|
+
address.local = s[local_dot_atom_s..local_dot_atom_pre_comment_e] if address
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
address.raw = s[address_s..(p-1)]
|
|
80
|
+
address_list.addresses << address if address
|
|
81
|
+
|
|
82
|
+
# Start next address
|
|
83
|
+
address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
|
|
84
|
+
address.group = group_name
|
|
85
|
+
address_s = nil
|
|
86
|
+
|
|
87
|
+
# Don't set the display name until the
|
|
88
|
+
# address has actually started. This allows
|
|
89
|
+
# us to choose quoted_s version if it
|
|
90
|
+
# exists and always use the 'full' phrase
|
|
91
|
+
# version.
|
|
92
|
+
when :angle_addr_s
|
|
93
|
+
if qstr
|
|
94
|
+
address.display_name = qstr
|
|
95
|
+
qstr = nil
|
|
96
|
+
elsif phrase_e
|
|
97
|
+
address.display_name = s[phrase_s..phrase_e].strip
|
|
98
|
+
phrase_e = phrase_s = nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Domain
|
|
102
|
+
when :domain_s
|
|
103
|
+
domain_s = p
|
|
104
|
+
when :domain_e
|
|
105
|
+
address.domain = s[domain_s..(p-1)].rstrip if address
|
|
106
|
+
|
|
107
|
+
# Local
|
|
108
|
+
when :local_dot_atom_s then local_dot_atom_s = p
|
|
109
|
+
when :local_dot_atom_e then local_dot_atom_e = p-1
|
|
110
|
+
when :local_dot_atom_pre_comment_e
|
|
111
|
+
local_dot_atom_pre_comment_e = p-1
|
|
112
|
+
when :local_quoted_string_e
|
|
113
|
+
address.local = '"' + qstr + '"' if address
|
|
114
|
+
|
|
115
|
+
# obs_domain_list
|
|
116
|
+
when :obs_domain_list_s then obs_domain_list_s = p
|
|
117
|
+
when :obs_domain_list_e
|
|
118
|
+
address.obs_domain_list = s[obs_domain_list_s..(p-1)]
|
|
119
|
+
|
|
120
|
+
else
|
|
121
|
+
raise Mail::Field::ParseError.new(Mail::AddressList, s, "Failed to process unknown action: #{action}")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if address_list.addresses.empty? && address_list.group_names.empty?
|
|
126
|
+
raise Mail::Field::ParseError.new(Mail::AddressList, s, "Didn't find any addresses or groups")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
address_list
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Mail::Parsers
|
|
2
|
+
class ContentDispositionParser
|
|
3
|
+
include Mail::Utilities
|
|
4
|
+
|
|
5
|
+
def parse(s)
|
|
6
|
+
content_disposition = ContentDispositionStruct.new("", nil)
|
|
7
|
+
if s.blank?
|
|
8
|
+
return content_disposition
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
actions, error = Ragel.parse(:content_disposition, s)
|
|
12
|
+
if error
|
|
13
|
+
raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, error)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
content_disposition.parameters = []
|
|
17
|
+
|
|
18
|
+
disp_type_s = param_attr_s = param_attr = qstr_s = qstr = param_val_s = nil
|
|
19
|
+
actions.each_slice(2) do |action_id, p|
|
|
20
|
+
action = Mail::Parsers::Ragel::ACTIONS[action_id]
|
|
21
|
+
case action
|
|
22
|
+
|
|
23
|
+
# Disposition Type
|
|
24
|
+
when :disp_type_s then disp_type_s = p
|
|
25
|
+
when :disp_type_e
|
|
26
|
+
content_disposition.disposition_type = s[disp_type_s..(p-1)].downcase
|
|
27
|
+
|
|
28
|
+
# Parameter Attribute
|
|
29
|
+
when :param_attr_s then param_attr_s = p
|
|
30
|
+
when :param_attr_e then param_attr = s[param_attr_s..(p-1)]
|
|
31
|
+
|
|
32
|
+
# Quoted String.
|
|
33
|
+
when :qstr_s then qstr_s = p
|
|
34
|
+
when :qstr_e then qstr = s[qstr_s..(p-1)]
|
|
35
|
+
|
|
36
|
+
# Parameter Value
|
|
37
|
+
when :param_val_s then param_val_s = p
|
|
38
|
+
when :param_val_e
|
|
39
|
+
if param_attr.nil?
|
|
40
|
+
raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, "no attribute for value")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Use quoted string value if one exists, otherwise use parameter value
|
|
44
|
+
if qstr
|
|
45
|
+
value = qstr
|
|
46
|
+
else
|
|
47
|
+
value = s[param_val_s..(p-1)]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
content_disposition.parameters << { param_attr => value }
|
|
51
|
+
param_attr = nil
|
|
52
|
+
qstr = nil
|
|
53
|
+
|
|
54
|
+
else
|
|
55
|
+
raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, "Failed to process unknown action: #{action}")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
content_disposition
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
def cleaned(string)
|
|
64
|
+
string =~ /(.+);\s*$/ ? $1 : string
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Mail::Parsers
|
|
2
|
+
class ContentLocationParser
|
|
3
|
+
def parse(s)
|
|
4
|
+
content_location = ContentLocationStruct.new(nil)
|
|
5
|
+
if s.blank?
|
|
6
|
+
return content_location
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
actions, error = Ragel.parse(:content_location, s)
|
|
10
|
+
if error
|
|
11
|
+
raise Mail::Field::ParseError.new(Mail::ContentLocationElement, s, error)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
qstr_s = token_string_s = nil
|
|
15
|
+
actions.each_slice(2) do |action_id, p|
|
|
16
|
+
action = Mail::Parsers::Ragel::ACTIONS[action_id]
|
|
17
|
+
case action
|
|
18
|
+
|
|
19
|
+
# Quoted String.
|
|
20
|
+
when :qstr_s then qstr_s = p
|
|
21
|
+
when :qstr_e then content_location.location = s[qstr_s..(p-1)]
|
|
22
|
+
|
|
23
|
+
# Token String
|
|
24
|
+
when :token_string_s then token_string_s = p
|
|
25
|
+
when :token_string_e
|
|
26
|
+
content_location.location = s[token_string_s..(p-1)]
|
|
27
|
+
|
|
28
|
+
else
|
|
29
|
+
raise Mail::Field::ParseError.new(Mail::ContentLocationElement, s, "Failed to process unknown action: #{action}")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
content_location
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Mail::Parsers
|
|
2
|
+
class ContentTransferEncodingParser
|
|
3
|
+
|
|
4
|
+
def parse(s)
|
|
5
|
+
content_transfer_encoding = ContentTransferEncodingStruct.new("")
|
|
6
|
+
if s.blank?
|
|
7
|
+
return content_transfer_encoding
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
actions, error = Ragel.parse(:content_transfer_encoding, s)
|
|
11
|
+
if error
|
|
12
|
+
raise Mail::Field::ParseError.new(Mail::ContentTransferEncodingElement, s, error)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
encoding_s = nil
|
|
16
|
+
actions.each_slice(2) do |action_id, p|
|
|
17
|
+
action = Mail::Parsers::Ragel::ACTIONS[action_id]
|
|
18
|
+
case action
|
|
19
|
+
|
|
20
|
+
# Encoding
|
|
21
|
+
when :encoding_s then encoding_s = p
|
|
22
|
+
when :encoding_e
|
|
23
|
+
content_transfer_encoding.encoding = s[encoding_s..(p-1)].downcase
|
|
24
|
+
|
|
25
|
+
else
|
|
26
|
+
raise Mail::Field::ParseError.new(Mail::ContentTransferEncodingElement, s, "Failed to process unknown action: #{action}")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
content_transfer_encoding
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|