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.
Files changed (163) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +156 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +52 -32
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +2 -2
  10. data/lib/mail/body.rb +33 -9
  11. data/lib/mail/check_delivery_params.rb +20 -0
  12. data/lib/mail/configuration.rb +1 -1
  13. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  14. data/lib/mail/core_extensions/nil.rb +4 -2
  15. data/lib/mail/core_extensions/object.rb +8 -8
  16. data/lib/mail/core_extensions/smtp.rb +12 -13
  17. data/lib/mail/core_extensions/string.rb +19 -6
  18. data/lib/mail/elements/address.rb +82 -141
  19. data/lib/mail/elements/address_list.rb +9 -42
  20. data/lib/mail/elements/content_disposition_element.rb +3 -7
  21. data/lib/mail/elements/content_location_element.rb +2 -6
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  23. data/lib/mail/elements/content_type_element.rb +4 -8
  24. data/lib/mail/elements/date_time_element.rb +3 -7
  25. data/lib/mail/elements/envelope_from_element.rb +18 -13
  26. data/lib/mail/elements/message_ids_element.rb +1 -6
  27. data/lib/mail/elements/mime_version_element.rb +3 -7
  28. data/lib/mail/elements/phrase_list.rb +2 -7
  29. data/lib/mail/elements/received_element.rb +3 -7
  30. data/lib/mail/elements.rb +12 -12
  31. data/lib/mail/encodings/quoted_printable.rb +4 -3
  32. data/lib/mail/encodings/unix_to_unix.rb +17 -0
  33. data/lib/mail/encodings.rb +53 -38
  34. data/lib/mail/envelope.rb +0 -5
  35. data/lib/mail/field.rb +131 -118
  36. data/lib/mail/field_list.rb +18 -18
  37. data/lib/mail/fields/bcc_field.rb +2 -2
  38. data/lib/mail/fields/cc_field.rb +2 -2
  39. data/lib/mail/fields/comments_field.rb +1 -1
  40. data/lib/mail/fields/common/common_address.rb +32 -22
  41. data/lib/mail/fields/common/common_date.rb +0 -7
  42. data/lib/mail/fields/common/common_field.rb +10 -3
  43. data/lib/mail/fields/common/common_message_id.rb +9 -5
  44. data/lib/mail/fields/common/parameter_hash.rb +3 -3
  45. data/lib/mail/fields/content_disposition_field.rb +1 -0
  46. data/lib/mail/fields/content_id_field.rb +1 -2
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  48. data/lib/mail/fields/content_type_field.rb +5 -2
  49. data/lib/mail/fields/date_field.rb +14 -14
  50. data/lib/mail/fields/from_field.rb +2 -2
  51. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  52. data/lib/mail/fields/keywords_field.rb +1 -1
  53. data/lib/mail/fields/message_id_field.rb +2 -3
  54. data/lib/mail/fields/references_field.rb +2 -1
  55. data/lib/mail/fields/reply_to_field.rb +2 -2
  56. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  57. data/lib/mail/fields/resent_cc_field.rb +2 -2
  58. data/lib/mail/fields/resent_from_field.rb +2 -2
  59. data/lib/mail/fields/resent_sender_field.rb +3 -3
  60. data/lib/mail/fields/resent_to_field.rb +2 -2
  61. data/lib/mail/fields/sender_field.rb +8 -8
  62. data/lib/mail/fields/to_field.rb +2 -2
  63. data/lib/mail/fields/unstructured_field.rb +44 -31
  64. data/lib/mail/fields.rb +32 -32
  65. data/lib/mail/header.rb +47 -37
  66. data/lib/mail/mail.rb +15 -1
  67. data/lib/mail/matchers/has_sent_mail.rb +36 -3
  68. data/lib/mail/message.rb +164 -57
  69. data/lib/mail/multibyte/chars.rb +4 -4
  70. data/lib/mail/multibyte/unicode.rb +9 -1
  71. data/lib/mail/network/delivery_methods/exim.rb +2 -3
  72. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  73. data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
  74. data/lib/mail/network/delivery_methods/smtp.rb +34 -45
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
  77. data/lib/mail/network/retriever_methods/imap.rb +32 -19
  78. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  79. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  80. data/lib/mail/network.rb +9 -9
  81. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  82. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  83. data/lib/mail/parsers/content_location_parser.rb +35 -0
  84. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  85. data/lib/mail/parsers/content_type_parser.rb +66 -0
  86. data/lib/mail/parsers/date_time_parser.rb +36 -0
  87. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  88. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  89. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  90. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  91. data/lib/mail/parsers/ragel/common.rl +185 -0
  92. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  93. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  105. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  106. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  107. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  108. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  109. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  110. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  111. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  112. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  113. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  114. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  115. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  117. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  118. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  119. data/lib/mail/parsers/ragel.rb +17 -0
  120. data/lib/mail/parsers/received_parser.rb +47 -0
  121. data/lib/mail/parsers.rb +26 -0
  122. data/lib/mail/part.rb +6 -2
  123. data/lib/mail/parts_list.rb +5 -3
  124. data/lib/mail/utilities.rb +70 -60
  125. data/lib/mail/values/unicode_tables.dat +0 -0
  126. data/lib/mail/version.rb +7 -15
  127. data/lib/mail/version_specific/ruby_1_8.rb +25 -4
  128. data/lib/mail/version_specific/ruby_1_9.rb +110 -30
  129. data/lib/mail.rb +20 -19
  130. metadata +129 -73
  131. data/Gemfile.lock +0 -36
  132. data/lib/VERSION +0 -4
  133. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  134. data/lib/mail/parsers/address_lists.rb +0 -64
  135. data/lib/mail/parsers/address_lists.treetop +0 -19
  136. data/lib/mail/parsers/content_disposition.rb +0 -535
  137. data/lib/mail/parsers/content_disposition.treetop +0 -46
  138. data/lib/mail/parsers/content_location.rb +0 -139
  139. data/lib/mail/parsers/content_location.treetop +0 -20
  140. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  141. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  142. data/lib/mail/parsers/content_type.rb +0 -967
  143. data/lib/mail/parsers/content_type.treetop +0 -68
  144. data/lib/mail/parsers/date_time.rb +0 -114
  145. data/lib/mail/parsers/date_time.treetop +0 -11
  146. data/lib/mail/parsers/envelope_from.rb +0 -194
  147. data/lib/mail/parsers/envelope_from.treetop +0 -32
  148. data/lib/mail/parsers/message_ids.rb +0 -45
  149. data/lib/mail/parsers/message_ids.treetop +0 -15
  150. data/lib/mail/parsers/mime_version.rb +0 -144
  151. data/lib/mail/parsers/mime_version.treetop +0 -19
  152. data/lib/mail/parsers/phrase_lists.rb +0 -45
  153. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  154. data/lib/mail/parsers/received.rb +0 -71
  155. data/lib/mail/parsers/received.treetop +0 -11
  156. data/lib/mail/parsers/rfc2045.rb +0 -464
  157. data/lib/mail/parsers/rfc2045.treetop +0 -36
  158. data/lib/mail/parsers/rfc2822.rb +0 -5341
  159. data/lib/mail/parsers/rfc2822.treetop +0 -410
  160. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  161. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  162. data/lib/tasks/corpus.rake +0 -125
  163. 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
- unless settings[:openssl_verify_mode]
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
- unless settings[:openssl_verify_mode]
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, envelope_from, destinations)
113
+ response = smtp_obj.sendmail(message, smtp_from, smtp_to)
146
114
  end
147
115
 
148
- return settings[:return_response] ? response : self
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
- # Set the envelope from to be either the return-path, the sender or the first from address
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 first unread email
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
- # ready_only: will ensure that no writes are made to the inbox during the session.
58
- # This is helpful when you don't want your messages to be set to read automatically. Default is false.
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.select(options[:mailbox]) : imap.examine(options[:mailbox])
74
+ options[:read_only] ? imap.examine(options[:mailbox]) : imap.select(options[:mailbox])
67
75
 
68
- message_ids = imap.uid_search(options[:keys])
69
- message_ids.reverse! if options[:what].to_sym == :last
70
- message_ids = message_ids.first(options[:count]) if options[:count].is_a?(Integer)
71
- message_ids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
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
- message_ids.each do |message_id|
76
- fetchdata = imap.uid_fetch(message_id, ['RFC822'])[0]
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, message_id
89
+ yield new_message, imap, uid
81
90
  else
82
91
  yield new_message
83
92
  end
84
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
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
- message_ids.each do |message_id|
90
- fetchdata = imap.uid_fetch(message_id, ['RFC822'])[0]
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(message_id, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find]
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 |message_id|
107
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED])
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 first unread email
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
- emails = @@emails.dup
24
- emails.reverse! if options[:what] == :last
25
- emails = case count = options[:count]
26
- when :all then emails
27
- when 1 then emails.first
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
- emails.reverse!
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
- autoload :SMTP, 'mail/network/delivery_methods/smtp'
5
- autoload :FileDelivery, 'mail/network/delivery_methods/file_delivery'
6
- autoload :Sendmail, 'mail/network/delivery_methods/sendmail'
7
- autoload :Exim, 'mail/network/delivery_methods/exim'
8
- autoload :SMTPConnection, 'mail/network/delivery_methods/smtp_connection'
9
- autoload :TestMailer, 'mail/network/delivery_methods/test_mailer'
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
- autoload :POP3, 'mail/network/retriever_methods/pop3'
12
- autoload :IMAP, 'mail/network/retriever_methods/imap'
13
- autoload :TestRetriever, 'mail/network/retriever_methods/test_retriever'
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