mail 2.4.4 → 2.5.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mail might be problematic. Click here for more details.

Files changed (45) hide show
  1. data/CHANGELOG.rdoc +60 -0
  2. data/Gemfile +1 -1
  3. data/README.md +11 -10
  4. data/lib/VERSION +2 -2
  5. data/lib/load_parsers.rb +41 -0
  6. data/lib/mail.rb +18 -17
  7. data/lib/mail/body.rb +2 -2
  8. data/lib/mail/check_delivery_params.rb +30 -0
  9. data/lib/mail/configuration.rb +1 -1
  10. data/lib/mail/core_extensions/nil.rb +4 -2
  11. data/lib/mail/core_extensions/shell_escape.rb +1 -1
  12. data/lib/mail/elements.rb +12 -12
  13. data/lib/mail/encodings.rb +8 -10
  14. data/lib/mail/encodings/quoted_printable.rb +1 -1
  15. data/lib/mail/field.rb +73 -95
  16. data/lib/mail/fields.rb +32 -32
  17. data/lib/mail/fields/common/common_address.rb +6 -1
  18. data/lib/mail/fields/common/common_field.rb +7 -1
  19. data/lib/mail/fields/common/common_message_id.rb +9 -5
  20. data/lib/mail/fields/content_disposition_field.rb +1 -0
  21. data/lib/mail/fields/content_type_field.rb +4 -1
  22. data/lib/mail/fields/keywords_field.rb +1 -1
  23. data/lib/mail/fields/unstructured_field.rb +33 -26
  24. data/lib/mail/header.rb +32 -13
  25. data/lib/mail/message.rb +8 -9
  26. data/lib/mail/multibyte/chars.rb +2 -2
  27. data/lib/mail/multibyte/unicode.rb +6 -0
  28. data/lib/mail/network.rb +9 -9
  29. data/lib/mail/network/delivery_methods/exim.rb +5 -1
  30. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  31. data/lib/mail/network/delivery_methods/sendmail.rb +5 -0
  32. data/lib/mail/network/delivery_methods/smtp.rb +11 -19
  33. data/lib/mail/network/delivery_methods/smtp_connection.rb +5 -18
  34. data/lib/mail/network/delivery_methods/test_mailer.rb +5 -1
  35. data/lib/mail/network/retriever_methods/imap.rb +14 -6
  36. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  37. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  38. data/lib/mail/parsers/rfc2822.rb +77 -21
  39. data/lib/mail/parsers/rfc2822.treetop +3 -3
  40. data/lib/mail/patterns.rb +0 -1
  41. data/lib/mail/values/unicode_tables.dat +0 -0
  42. data/lib/mail/version_specific/ruby_1_8.rb +18 -1
  43. data/lib/mail/version_specific/ruby_1_9.rb +7 -1
  44. metadata +27 -10
  45. data/Gemfile.lock +0 -36
@@ -339,7 +339,7 @@ module Mail #:nodoc:
339
339
  # Example:
340
340
  # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
341
341
  def upcase
342
- chars(Unicode.apply_mapping(@wrapped_string), :uppercase_mapping)
342
+ chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping))
343
343
  end
344
344
 
345
345
  # Convert characters in the string to lowercase.
@@ -347,7 +347,7 @@ module Mail #:nodoc:
347
347
  # Example:
348
348
  # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
349
349
  def downcase
350
- chars(Unicode.apply_mapping(@wrapped_string), :lowercase_mapping)
350
+ chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping))
351
351
  end
352
352
 
353
353
  # Converts the first character to uppercase and the remainder to lowercase.
@@ -390,3 +390,9 @@ module Mail
390
390
  end
391
391
  end
392
392
  end
393
+
394
+ module ActiveSupport
395
+ unless const_defined?(:Multibyte)
396
+ Multibyte = Mail::Multibyte
397
+ end
398
+ end
@@ -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
@@ -1,3 +1,5 @@
1
+ require 'mail/check_delivery_params'
2
+
1
3
  module Mail
2
4
 
3
5
  # A delivery method implementation which sends via exim.
@@ -36,13 +38,15 @@ module Mail
36
38
  #
37
39
  # mail.deliver!
38
40
  class Exim < Sendmail
41
+ include Mail::CheckDeliveryParams
39
42
 
40
43
  def initialize(values)
41
44
  self.settings = { :location => '/usr/sbin/exim',
42
45
  :arguments => '-i -t' }.merge(values)
43
46
  end
44
47
 
45
- def self.call(path, arguments, mail)
48
+ def self.call(path, arguments, destinations, mail)
49
+ check_params(mail)
46
50
  IO.popen("#{path} #{arguments}", "w+") do |io|
47
51
  io.puts mail.encoded.to_lf
48
52
  io.flush
@@ -1,3 +1,5 @@
1
+ require 'mail/check_delivery_params'
2
+
1
3
  module Mail
2
4
 
3
5
  # FileDelivery class delivers emails into multiple files based on the destination
@@ -11,6 +13,7 @@ module Mail
11
13
  # Make sure the path you specify with :location is writable by the Ruby process
12
14
  # running Mail.
13
15
  class FileDelivery
16
+ include Mail::CheckDeliveryParams
14
17
 
15
18
  if RUBY_VERSION >= '1.9.1'
16
19
  require 'fileutils'
@@ -25,6 +28,8 @@ module Mail
25
28
  attr_accessor :settings
26
29
 
27
30
  def deliver!(mail)
31
+ check_params(mail)
32
+
28
33
  if ::File.respond_to?(:makedirs)
29
34
  ::File.makedirs settings[:location]
30
35
  else
@@ -1,3 +1,5 @@
1
+ require 'mail/check_delivery_params'
2
+
1
3
  module Mail
2
4
  # A delivery method implementation which sends via sendmail.
3
5
  #
@@ -35,6 +37,7 @@ module Mail
35
37
  #
36
38
  # mail.deliver!
37
39
  class Sendmail
40
+ include Mail::CheckDeliveryParams
38
41
 
39
42
  def initialize(values)
40
43
  self.settings = { :location => '/usr/sbin/sendmail',
@@ -44,6 +47,8 @@ module Mail
44
47
  attr_accessor :settings
45
48
 
46
49
  def deliver!(mail)
50
+ check_params(mail)
51
+
47
52
  envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
48
53
  return_path = "-f " + '"' + envelope_from.escape_for_shell + '"' if envelope_from
49
54
 
@@ -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",
@@ -92,23 +95,8 @@ module Mail
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
+ envelope_from, destinations, message = check_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)
@@ -133,9 +121,13 @@ module Mail
133
121
  if openssl_verify_mode.kind_of?(String)
134
122
  openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
135
123
  end
136
- context = Net::SMTP.default_ssl_context
137
- context.verify_mode = openssl_verify_mode
138
- smtp.enable_starttls_auto(context)
124
+ if RUBY_VERSION >= '1.9.0'
125
+ context = Net::SMTP.default_ssl_context
126
+ context.verify_mode = openssl_verify_mode
127
+ smtp.enable_tls(context)
128
+ else
129
+ smtp.enable_tls(openssl_verify_mode)
130
+ end
139
131
  end
140
132
  end
141
133
  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,6 +37,7 @@ 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?
@@ -48,25 +51,9 @@ module Mail
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)
51
-
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
-
54
+ envelope_from, destinations, message = check_params(mail)
68
55
  response = smtp.sendmail(message, envelope_from, destinations)
69
-
56
+
70
57
  settings[:return_response] ? response : self
71
58
  end
72
59
 
@@ -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
@@ -33,8 +36,9 @@ module Mail
33
36
  attr_accessor :settings
34
37
 
35
38
  def deliver!(mail)
39
+ check_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,16 +57,21 @@ 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
76
  message_ids = imap.uid_search(options[:keys])
69
77
  message_ids.reverse! if options[:what].to_sym == :last
@@ -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
@@ -1321,6 +1321,7 @@ module Mail
1321
1321
  def domain_text
1322
1322
  elements[1]
1323
1323
  end
1324
+
1324
1325
  end
1325
1326
 
1326
1327
  def _nt_local_dot_atom_text
@@ -1357,6 +1358,25 @@ module Mail
1357
1358
  if r2
1358
1359
  r4 = _nt_domain_text
1359
1360
  s1 << r4
1361
+ if r4
1362
+ s5, i5 = [], index
1363
+ loop do
1364
+ if has_terminal?(".", false, index)
1365
+ r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
1366
+ @index += 1
1367
+ else
1368
+ terminal_parse_failure(".")
1369
+ r6 = nil
1370
+ end
1371
+ if r6
1372
+ s5 << r6
1373
+ else
1374
+ break
1375
+ end
1376
+ end
1377
+ r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
1378
+ s1 << r5
1379
+ end
1360
1380
  end
1361
1381
  if s1.last
1362
1382
  r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
@@ -2714,17 +2734,35 @@ module Mail
2714
2734
  s3, i3 = [], index
2715
2735
  loop do
2716
2736
  i4, s4 = index, []
2737
+ i5 = index
2717
2738
  if has_terminal?(",", false, index)
2718
- r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
2739
+ r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
2719
2740
  @index += 1
2720
2741
  else
2721
2742
  terminal_parse_failure(",")
2722
- r5 = nil
2743
+ r6 = nil
2744
+ end
2745
+ if r6
2746
+ r5 = r6
2747
+ else
2748
+ if has_terminal?(";", false, index)
2749
+ r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
2750
+ @index += 1
2751
+ else
2752
+ terminal_parse_failure(";")
2753
+ r7 = nil
2754
+ end
2755
+ if r7
2756
+ r5 = r7
2757
+ else
2758
+ @index = i5
2759
+ r5 = nil
2760
+ end
2723
2761
  end
2724
2762
  s4 << r5
2725
2763
  if r5
2726
- r6 = _nt_mailbox
2727
- s4 << r6
2764
+ r8 = _nt_mailbox
2765
+ s4 << r8
2728
2766
  end
2729
2767
  if s4.last
2730
2768
  r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
@@ -2752,9 +2790,9 @@ module Mail
2752
2790
  if r1
2753
2791
  r0 = r1
2754
2792
  else
2755
- r7 = _nt_obs_mbox_list
2756
- if r7
2757
- r0 = r7
2793
+ r9 = _nt_obs_mbox_list
2794
+ if r9
2795
+ r0 = r9
2758
2796
  else
2759
2797
  @index = i0
2760
2798
  r0 = nil
@@ -2915,34 +2953,52 @@ module Mail
2915
2953
  r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
2916
2954
  s4 << r5
2917
2955
  if r5
2956
+ i7 = index
2918
2957
  if has_terminal?(",", false, index)
2919
- r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
2958
+ r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
2920
2959
  @index += 1
2921
2960
  else
2922
2961
  terminal_parse_failure(",")
2923
- r7 = nil
2962
+ r8 = nil
2963
+ end
2964
+ if r8
2965
+ r7 = r8
2966
+ else
2967
+ if has_terminal?(";", false, index)
2968
+ r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
2969
+ @index += 1
2970
+ else
2971
+ terminal_parse_failure(";")
2972
+ r9 = nil
2973
+ end
2974
+ if r9
2975
+ r7 = r9
2976
+ else
2977
+ @index = i7
2978
+ r7 = nil
2979
+ end
2924
2980
  end
2925
2981
  s4 << r7
2926
2982
  if r7
2927
- s8, i8 = [], index
2983
+ s10, i10 = [], index
2928
2984
  loop do
2929
- r9 = _nt_FWS
2930
- if r9
2931
- s8 << r9
2985
+ r11 = _nt_FWS
2986
+ if r11
2987
+ s10 << r11
2932
2988
  else
2933
2989
  break
2934
2990
  end
2935
2991
  end
2936
- r8 = instantiate_node(SyntaxNode,input, i8...index, s8)
2937
- s4 << r8
2938
- if r8
2939
- r11 = _nt_address
2940
- if r11
2941
- r10 = r11
2992
+ r10 = instantiate_node(SyntaxNode,input, i10...index, s10)
2993
+ s4 << r10
2994
+ if r10
2995
+ r13 = _nt_address
2996
+ if r13
2997
+ r12 = r13
2942
2998
  else
2943
- r10 = instantiate_node(SyntaxNode,input, index...index)
2999
+ r12 = instantiate_node(SyntaxNode,input, index...index)
2944
3000
  end
2945
- s4 << r10
3001
+ s4 << r12
2946
3002
  end
2947
3003
  end
2948
3004
  end