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
@@ -1,5 +1,65 @@
1
1
  == HEAD
2
2
 
3
+ == Version 2.5.2 - Sun Nov 18 15:01:00 +1100 2012 Mikel Lindsaar <mikel@reinteractive.net>
4
+
5
+ * Removing double loading of treetop parsers to remove warnings
6
+ * Making parsers auto compile on spec suite and load in production code to avoid error that caused yank of 2.5.0
7
+ * Reapply pull request 443 - CC fields with semicolon are now parsed right (paulwittmann)
8
+
9
+ == Version 2.5.1 - Sun Nov 18 14:01:00 +1100 2012 Mikel Lindsaar <mikel@reinteractive.net>
10
+
11
+ * Yanked 2.5.0
12
+ * Reverted pull request 443 - CC fields with semicolon are now parsed right (paulwittmann)
13
+
14
+ == Version 2.5.0 - Sun Nov 18 12:20:00 +1100 2012 Mikel Lindsaar <mikel@reinteractive.net>
15
+
16
+ Features:
17
+ * Close pull request 406 - Add Mail#eager_autoload! to load all autoloaded files on demand (bpot)
18
+ * Close pull request 461 - Allow string as delivery_method (skyeagle, radar)
19
+ * Close pull request 407 - Do not require Net::IMAP or Net::POP if they're already loaded (bpot)
20
+ * Close pull request 400 - Raise exception if delivery values or from values are missing completely from an email that is getting delivered (dmathieu)
21
+ * Close pull request 397 - Support dots in local part of the addresses (eac)
22
+ * Close pull request 477 - Fixed handling content_type with superfluous spaces (ledermann)
23
+ * Close pull request 451 - Ignore nil in addresses so things do not blow up when e.g. a user had no email (grosser)
24
+ * Close pull request 362 - Enable TLS in Ruby 1.8 (kingargyle)
25
+ * Close pull request 358 - Fix Mail::CommonAddress#value=, Mail::CommonAddress#<< and Mail::Encodings.encode_non_usascii (mrkn)
26
+ * Close pull request 350 - Makes mail Header object ennumerable (ged)
27
+
28
+ Performance:
29
+ * Close pull request 369 - Mail::Header#charset is called pretty often during header parser work (bogdan)
30
+ * Close pull request 368 - Improve existing code by moving some objects to contstant instead of constructing them over and over again. (bogdan)
31
+ * Close pull request 366 - Headers parsing performance optimization (bogdan)
32
+ * Close pull request 365 - Add maximum_amount of parsed headers configuration parameter (bogdan)
33
+
34
+ Bugs:
35
+ * Close pull request 444 - Fix typo in spec (cczona)
36
+ * Close pull request 439 - Fix Ruby 1.9 behaviour to match 1.8.7 behaviour on ignoring invalid or undefined characters (ochko)
37
+ * Close pull request 430 - Unstructured field converts to string before calling encoding on it (brupm mikel)
38
+ * Close pull request 424 - Use String#to_crlf instead of String#gsub (okkez)
39
+ * Close pull request 429 - Fix an obvious bug in exim delivery_method (dskim)
40
+ * Close pull request 425 - Remove Gemfile.lock from generated gem (kbackowski)
41
+ * Close pull request 414 - Fix typo on "ignoring" (derwiki)
42
+ * Close pull request 405 - Fix stack overflow (RegexpError) triggered by large emails with an envelope (bpot)
43
+ * Close pull request 402 - Prevent InReplyTo, Keyword, References or ResentMessageId fields from generating lines longer than 998 chars (pplr)
44
+ * Close pull request 391 - Fixed failed attachment parsing when file name in headers contains spaces and is not wrapped in quotes (danieltreacy)
45
+ * Close pull request 385 - Fix Multibyte::Chars#upcase/downcase (technoweenie)
46
+ * Close pull request 384 - copy dat unicode over from active support (technoweenie)
47
+ * Close pull request 380 - Split strictly on MIME boundary lines (ConradIrwin)
48
+ * Close pull request 277 - Fix specific email decoding failure example (yalab)
49
+ * Close pull request 361 - Support 8bit encoding for ruby 1.9 (bogdan)
50
+ * Close pull request 346 - Fix two bugs of TestRetriever (ermaker)
51
+ * Close pull request 337 - Make the behavior of value_decode the same between Ruby 1.8 and Ruby 1.9. (kennyj)
52
+ * Close pull request 336 - Fix more warning: possibly useless use of == in void context (kennyj)
53
+ * Close pull request 293 - make charset and mime type more resliant to bad header values (kmeehl)
54
+ * Fix failing spec Issue 453 on Ruby 1.9.3
55
+ * Fix mail reading: don't raise invalid byte sequence in UTF-8 when reading non-UTF-8 emails (mreinsch)
56
+ * Close pull request 353 - define NilClass#blank? only if not defined yet (amatsuda)
57
+ * Close pull request 357 - Fixes #349 an inverted condition on imap open read_only (felixroeser)
58
+ * Remove duplicated line feed from regexp
59
+ * Remove unused variable
60
+ * Updated IMAP documentation
61
+ * Tweak publisher
62
+
3
63
  == Version 2.4.4 - Wed Mar 14 22:44:00 +1100 2012 Mikel Lindsaar <mikel@reinteractive.net>
4
64
 
5
65
  * Fix security vulnerability allowing command line exploit when using file delivery method
data/Gemfile CHANGED
@@ -12,7 +12,7 @@ end
12
12
 
13
13
  group :test do
14
14
  gem "rake", "> 0.8.7"
15
- gem "rspec", "~> 2.8.0"
15
+ gem "rspec", "~> 2.12.0"
16
16
  case
17
17
  when defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
18
18
  # Skip it
data/README.md CHANGED
@@ -29,7 +29,7 @@ Donations
29
29
  -------------
30
30
 
31
31
  Mail has been downloaded millions of times, by people around the world, in fact,
32
- it represents more than 1% of *all* gems downloaded.
32
+ it represents more than 1% of *all* gems downloaded.
33
33
 
34
34
  It is (like all open source software) a labour of love and something I am doing
35
35
  with my own free time. If you would like to say thanks, please feel free to
@@ -42,14 +42,15 @@ me a nice email :)
42
42
  Compatibility
43
43
  -------------
44
44
 
45
- Mail is tested by Travis (![Travis Build Status](https://secure.travis-ci.org/mikel/mail.png "Build Status")) and works on the following platforms:
45
+ Mail is tested by Travis ([![Travis Build Status](https://travis-ci.org/mikel/mail.png "Build Status")](https://travis-ci.org/mikel/mail)) and works on the [following platforms](https://github.com/mikel/mail/blob/master/.travis.yml)
46
46
 
47
- * jruby-1.6.5.1 [ x86_64 ]
48
- * rbx-head-d18 [ x86_64 ]
49
- * ree-1.8.7-2011.03 [ i686 ]
50
- * ruby-1.8.7-p357 [ i686 ]
47
+ * ruby-1.8.7-p370 [ i686 ]
51
48
  * ruby-1.9.2-p290 [ x86_64 ]
52
- * ruby-1.9.3-p0 [ x86_64 ]
49
+ * ruby-1.9.3-p327 [ x86_64 ]
50
+ * jruby-1.6.8 [ x86_64 ]
51
+ * jruby-1.7.0 [ x86_64 ]
52
+ * rbx-d18 [ x86_64 ]
53
+ * rbx-d19 [ x86_64 ]
53
54
 
54
55
  Discussion
55
56
  ----------
@@ -104,7 +105,7 @@ It also means you can be sure Mail will behave correctly.
104
105
  API Policy
105
106
  ----------
106
107
 
107
- No API removals within a single point release. All removals to be depreciated with
108
+ No API removals within a single point release. All removals to be deprecated with
108
109
  warnings for at least one MINOR point release before removal.
109
110
 
110
111
  Also, all private or protected methods to be declared as such - though this is still I/P.
@@ -294,7 +295,7 @@ The most recent email:
294
295
  ```ruby
295
296
  Mail.all #=> Returns an array of all emails
296
297
  Mail.first #=> Returns the first unread email
297
- Mail.last #=> Returns the first unread email
298
+ Mail.last #=> Returns the last unread email
298
299
  ```
299
300
 
300
301
  The first 10 emails sorted by date in ascending order:
@@ -641,7 +642,7 @@ License
641
642
 
642
643
  (The MIT License)
643
644
 
644
- Copyright (c) 2009, 2010, 2011, 2012
645
+ Copyright (c) 2009, 2010, 2011, 2012 Mikel Lindsaar
645
646
 
646
647
  Permission is hereby granted, free of charge, to any person obtaining
647
648
  a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
1
  major:2
2
- minor:4
3
- patch:4
2
+ minor:5
3
+ patch:2
4
4
  build:
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ # This file loads up the parsers for mail to use. It also will attempt to compile parsers
3
+ # if they don't exist.
4
+ #
5
+ # It also only uses the compiler if we are running the SPEC suite
6
+ module Mail # :doc:
7
+ require 'treetop/runtime'
8
+
9
+ def self.compile_parser(parser)
10
+ require 'treetop/compiler'
11
+ STDOUT.puts "Compiling parser #{parser} from treetop source"
12
+ Treetop.load(File.join(File.dirname(__FILE__)) + "/mail/parsers/#{parser}")
13
+ end
14
+
15
+ parsers = %w[ rfc2822_obsolete rfc2822 address_lists phrase_lists
16
+ date_time received message_ids envelope_from rfc2045
17
+ mime_version content_type content_disposition
18
+ content_transfer_encoding content_location ]
19
+
20
+ if defined?(MAIL_SPEC_SUITE_RUNNING)
21
+ STDOUT.puts "Compiling all parsers from treetop source"
22
+
23
+ parsers.each do |parser|
24
+ compile_parser(parser)
25
+ end
26
+
27
+ else
28
+ STDOUT.puts "Loading precompiled parsers from ruby source"
29
+
30
+ parsers.each do |parser|
31
+ begin
32
+ require "mail/parsers/#{parser}"
33
+ rescue LoadError
34
+ STDOUT.puts "Couldn't load parser #{parser} from ruby source"
35
+ compile_parser(parser)
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -46,6 +46,23 @@ module Mail # :doc:
46
46
  require 'mail/utilities'
47
47
  require 'mail/configuration'
48
48
 
49
+ @@autoloads = {}
50
+ def self.register_autoload(name, path)
51
+ @@autoloads[name] = path
52
+ autoload(name, path)
53
+ end
54
+
55
+ # This runs through the autoload list and explictly requires them for you.
56
+ # Useful when running mail in a threaded process.
57
+ #
58
+ # Usage:
59
+ #
60
+ # require 'mail'
61
+ # Mail.eager_autoload!
62
+ def self.eager_autoload!
63
+ @@autoloads.each { |_,path| require(path) }
64
+ end
65
+
49
66
  # Autoload mail send and receive classes.
50
67
  require 'mail/network'
51
68
 
@@ -60,23 +77,7 @@ module Mail # :doc:
60
77
 
61
78
  require 'mail/envelope'
62
79
 
63
- parsers = %w[ rfc2822_obsolete rfc2822 address_lists phrase_lists
64
- date_time received message_ids envelope_from rfc2045
65
- mime_version content_type content_disposition
66
- content_transfer_encoding content_location ]
67
-
68
- parsers.each do |parser|
69
- begin
70
- # Try requiring the pre-compiled ruby version first
71
- require 'treetop/runtime'
72
- require "mail/parsers/#{parser}"
73
- rescue LoadError
74
- # Otherwise, get treetop to compile and load it
75
- require 'treetop/runtime'
76
- require 'treetop/compiler'
77
- Treetop.load(File.join(File.dirname(__FILE__)) + "/mail/parsers/#{parser}")
78
- end
79
- end
80
+ require 'load_parsers'
80
81
 
81
82
  # Autoload header field elements and transfer encodings.
82
83
  require 'mail/elements'
@@ -6,7 +6,7 @@ module Mail
6
6
  # The body is where the text of the email is stored. Mail treats the body
7
7
  # as a single object. The body itself has no information about boundaries
8
8
  # used in the MIME standard, it just looks at it's content as either a single
9
- # block of text, or (if it is a multipart message) as an array of blocks o text.
9
+ # block of text, or (if it is a multipart message) as an array of blocks of text.
10
10
  #
11
11
  # A body has to be told to split itself up into a multipart message by calling
12
12
  # #split with the correct boundary. This is because the body object has no way
@@ -257,7 +257,7 @@ module Mail
257
257
 
258
258
  def split!(boundary)
259
259
  self.boundary = boundary
260
- parts = raw_source.split("--#{boundary}")
260
+ parts = raw_source.split(/--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
261
261
  # Make the preamble equal to the preamble (if any)
262
262
  self.preamble = parts[0].to_s.strip
263
263
  # Make the epilogue equal to the epilogue (if any)
@@ -0,0 +1,30 @@
1
+ module Mail
2
+
3
+ module CheckDeliveryParams
4
+
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+
8
+ def check_params(mail)
9
+ envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
10
+ if envelope_from.blank?
11
+ raise ArgumentError.new('A sender (Return-Path, Sender or From) required to send a message')
12
+ end
13
+
14
+ destinations ||= mail.destinations if mail.respond_to?(:destinations) && mail.destinations
15
+ if destinations.blank?
16
+ raise ArgumentError.new('At least one recipient (To, Cc or Bcc) is required to send a message')
17
+ end
18
+
19
+ message ||= mail.encoded if mail.respond_to?(:encoded)
20
+ if message.blank?
21
+ raise ArgumentError.new('A encoded content is required to send a message')
22
+ end
23
+
24
+ [envelope_from, destinations, message]
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -26,7 +26,7 @@ module Mail
26
26
  end
27
27
 
28
28
  def lookup_delivery_method(method)
29
- case method
29
+ case method.is_a?(String) ? method.to_sym : method
30
30
  when nil
31
31
  Mail::SMTP
32
32
  when :smtp
@@ -3,8 +3,10 @@
3
3
  # This is not loaded if ActiveSupport is already loaded
4
4
 
5
5
  class NilClass #:nodoc:
6
- def blank?
7
- true
6
+ unless nil.respond_to? :blank?
7
+ def blank?
8
+ true
9
+ end
8
10
  end
9
11
 
10
12
  def to_crlf
@@ -30,7 +30,7 @@ module Mail
30
30
 
31
31
  # Process as a single byte sequence because not all shell
32
32
  # implementations are multibyte aware.
33
- str.gsub!(/([^A-Za-z0-9_\s\+\-.,:\/@\n])/n, "\\\\\\1")
33
+ str.gsub!(/([^A-Za-z0-9_\s\+\-.,:\/@])/n, "\\\\\\1")
34
34
 
35
35
  # A LF cannot be escaped with a backslash because a backslash + LF
36
36
  # combo is regarded as line continuation and simply ignored.
@@ -1,14 +1,14 @@
1
1
  module Mail
2
- autoload :Address, 'mail/elements/address'
3
- autoload :AddressList, 'mail/elements/address_list'
4
- autoload :ContentDispositionElement, 'mail/elements/content_disposition_element'
5
- autoload :ContentLocationElement, 'mail/elements/content_location_element'
6
- autoload :ContentTransferEncodingElement, 'mail/elements/content_transfer_encoding_element'
7
- autoload :ContentTypeElement, 'mail/elements/content_type_element'
8
- autoload :DateTimeElement, 'mail/elements/date_time_element'
9
- autoload :EnvelopeFromElement, 'mail/elements/envelope_from_element'
10
- autoload :MessageIdsElement, 'mail/elements/message_ids_element'
11
- autoload :MimeVersionElement, 'mail/elements/mime_version_element'
12
- autoload :PhraseList, 'mail/elements/phrase_list'
13
- autoload :ReceivedElement, 'mail/elements/received_element'
2
+ register_autoload :Address, 'mail/elements/address'
3
+ register_autoload :AddressList, 'mail/elements/address_list'
4
+ register_autoload :ContentDispositionElement, 'mail/elements/content_disposition_element'
5
+ register_autoload :ContentLocationElement, 'mail/elements/content_location_element'
6
+ register_autoload :ContentTransferEncodingElement, 'mail/elements/content_transfer_encoding_element'
7
+ register_autoload :ContentTypeElement, 'mail/elements/content_type_element'
8
+ register_autoload :DateTimeElement, 'mail/elements/date_time_element'
9
+ register_autoload :EnvelopeFromElement, 'mail/elements/envelope_from_element'
10
+ register_autoload :MessageIdsElement, 'mail/elements/message_ids_element'
11
+ register_autoload :MimeVersionElement, 'mail/elements/mime_version_element'
12
+ register_autoload :PhraseList, 'mail/elements/phrase_list'
13
+ register_autoload :ReceivedElement, 'mail/elements/received_element'
14
14
  end
@@ -147,19 +147,17 @@ module Mail
147
147
 
148
148
  # Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
149
149
  def Encodings.unquote_and_convert_to(str, to_encoding)
150
- original_encoding = split_encoding_from_string( str )
151
-
152
- output = value_decode( str ).to_s
153
-
154
- if original_encoding.to_s.downcase.gsub("-", "") == to_encoding.to_s.downcase.gsub("-", "")
150
+ output = value_decode( str ).to_s # output is already converted to UTF-8
151
+
152
+ if 'utf8' == to_encoding.to_s.downcase.gsub("-", "")
155
153
  output
156
- elsif original_encoding && to_encoding
154
+ elsif to_encoding
157
155
  begin
158
156
  if RUBY_VERSION >= '1.9'
159
157
  output.encode(to_encoding)
160
158
  else
161
159
  require 'iconv'
162
- Iconv.iconv(to_encoding, original_encoding, output).first
160
+ Iconv.iconv(to_encoding, 'UTF-8', output).first
163
161
  end
164
162
  rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
165
163
  # the 'from' parameter specifies a charset other than what the text
@@ -178,10 +176,10 @@ module Mail
178
176
  def Encodings.address_encode(address, charset = 'utf-8')
179
177
  if address.is_a?(Array)
180
178
  # loop back through for each element
181
- address.map { |a| Encodings.address_encode(a, charset) }.join(", ")
179
+ address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
182
180
  else
183
181
  # find any word boundary that is not ascii and encode it
184
- encode_non_usascii(address, charset)
182
+ encode_non_usascii(address, charset) if address
185
183
  end
186
184
  end
187
185
 
@@ -189,7 +187,7 @@ module Mail
189
187
  return address if address.ascii_only? or charset.nil?
190
188
  us_ascii = %Q{\x00-\x7f}
191
189
  # Encode any non usascii strings embedded inside of quotes
192
- address.gsub!(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
190
+ address = address.gsub(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
193
191
  # Then loop through all remaining items and encode as needed
194
192
  tokens = address.split(/\s/)
195
193
  map_with_index(tokens) do |word, i|
@@ -18,7 +18,7 @@ module Mail
18
18
  end
19
19
 
20
20
  def self.encode(str)
21
- [str].pack("M").gsub(/\n/, "\r\n")
21
+ [str].pack("M").to_crlf
22
22
  end
23
23
 
24
24
  def self.cost(str)
@@ -5,11 +5,11 @@ module Mail
5
5
  # Provides a single class to call to create a new structured or unstructured
6
6
  # field. Works out per RFC what field of field it is being given and returns
7
7
  # the correct field of class back on new.
8
- #
8
+ #
9
9
  # ===Per RFC 2822
10
- #
10
+ #
11
11
  # 2.2. Header Fields
12
- #
12
+ #
13
13
  # Header fields are lines composed of a field name, followed by a colon
14
14
  # (":"), followed by a field body, and terminated by CRLF. A field
15
15
  # name MUST be composed of printable US-ASCII characters (i.e.,
@@ -19,12 +19,12 @@ module Mail
19
19
  # used in header "folding" and "unfolding" as described in section
20
20
  # 2.2.3. All field bodies MUST conform to the syntax described in
21
21
  # sections 3 and 4 of this standard.
22
- #
22
+ #
23
23
  class Field
24
-
24
+
25
25
  include Patterns
26
26
  include Comparable
27
-
27
+
28
28
  STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
29
29
  content-id content-location content-transfer-encoding
30
30
  content-type date from in-reply-to keywords message-id
@@ -34,7 +34,39 @@ module Mail
34
34
  return-path sender to ]
35
35
 
36
36
  KNOWN_FIELDS = STRUCTURED_FIELDS + ['comments', 'subject']
37
-
37
+
38
+ FIELDS_MAP = {
39
+ "to" => ToField,
40
+ "cc" => CcField,
41
+ "bcc" => BccField,
42
+ "message-id" => MessageIdField,
43
+ "in-reply-to" => InReplyToField,
44
+ "references" => ReferencesField,
45
+ "subject" => SubjectField,
46
+ "comments" => CommentsField,
47
+ "keywords" => KeywordsField,
48
+ "date" => DateField,
49
+ "from" => FromField,
50
+ "sender" => SenderField,
51
+ "reply-to" => ReplyToField,
52
+ "resent-date" => ResentDateField,
53
+ "resent-from" => ResentFromField,
54
+ "resent-sender" => ResentSenderField,
55
+ "resent-to" => ResentToField,
56
+ "resent-cc" => ResentCcField,
57
+ "resent-bcc" => ResentBccField,
58
+ "resent-message-id" => ResentMessageIdField,
59
+ "return-path" => ReturnPathField,
60
+ "received" => ReceivedField,
61
+ "mime-version" => MimeVersionField,
62
+ "content-transfer-encoding" => ContentTransferEncodingField,
63
+ "content-description" => ContentDescriptionField,
64
+ "content-disposition" => ContentDispositionField,
65
+ "content-type" => ContentTypeField,
66
+ "content-id" => ContentIdField,
67
+ "content-location" => ContentLocationField,
68
+ }
69
+
38
70
  # Generic Field Exception
39
71
  class FieldError < StandardError
40
72
  end
@@ -55,25 +87,25 @@ module Mail
55
87
  # Raised when attempting to set a structured field's contents to an invalid syntax
56
88
  class SyntaxError < FieldError #:nodoc:
57
89
  end
58
-
90
+
59
91
  # Accepts a string:
60
- #
92
+ #
61
93
  # Field.new("field-name: field data")
62
- #
94
+ #
63
95
  # Or name, value pair:
64
- #
96
+ #
65
97
  # Field.new("field-name", "value")
66
- #
98
+ #
67
99
  # Or a name by itself:
68
- #
100
+ #
69
101
  # Field.new("field-name")
70
- #
102
+ #
71
103
  # Note, does not want a terminating carriage return. Returns
72
104
  # self appropriately parsed. If value is not a string, then
73
105
  # it will be passed through as is, for example, content-type
74
- # field can accept an array with the type and a hash of
106
+ # field can accept an array with the type and a hash of
75
107
  # parameters:
76
- #
108
+ #
77
109
  # Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
78
110
  def initialize(name, value = nil, charset = 'utf-8')
79
111
  case
@@ -92,47 +124,47 @@ module Mail
92
124
  def field=(value)
93
125
  @field = value
94
126
  end
95
-
127
+
96
128
  def field
97
129
  @field
98
130
  end
99
-
131
+
100
132
  def name
101
133
  field.name
102
134
  end
103
-
135
+
104
136
  def value
105
137
  field.value
106
138
  end
107
-
139
+
108
140
  def value=(val)
109
141
  create_field(name, val, charset)
110
142
  end
111
-
143
+
112
144
  def to_s
113
145
  field.to_s
114
146
  end
115
-
147
+
116
148
  def update(name, value)
117
149
  create_field(name, value, charset)
118
150
  end
119
-
151
+
120
152
  def same( other )
121
153
  match_to_s(other.name, field.name)
122
154
  end
123
-
155
+
124
156
  alias_method :==, :same
125
-
157
+
126
158
  def <=>( other )
127
159
  self_order = FIELD_ORDER.rindex(self.name.to_s.downcase) || 100
128
160
  other_order = FIELD_ORDER.rindex(other.name.to_s.downcase) || 100
129
161
  self_order <=> other_order
130
162
  end
131
-
163
+
132
164
  def method_missing(name, *args, &block)
133
165
  field.send(name, *args, &block)
134
166
  end
135
-
167
+
136
168
  FIELD_ORDER = %w[ return-path received
137
169
  resent-date resent-from resent-sender resent-to
138
170
  resent-cc resent-bcc resent-message-id
@@ -141,16 +173,16 @@ module Mail
141
173
  subject comments keywords
142
174
  mime-version content-type content-transfer-encoding
143
175
  content-location content-disposition content-description ]
144
-
176
+
145
177
  private
146
-
178
+
147
179
  def split(raw_field)
148
180
  match_data = raw_field.mb_chars.match(/^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/)
149
181
  [match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip]
150
182
  rescue
151
- STDERR.puts "WARNING: Could not parse (and so ignorning) '#{raw_field}'"
183
+ STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
152
184
  end
153
-
185
+
154
186
  def create_field(name, value, charset)
155
187
  begin
156
188
  self.field = new_field(name, value, charset)
@@ -162,73 +194,19 @@ module Mail
162
194
  end
163
195
 
164
196
  def new_field(name, value, charset)
165
- # Could do this with constantize and make it "as DRY as", but a simple case
166
- # statement is, well, simpler...
167
- case name.to_s.downcase
168
- when /^to$/i
169
- ToField.new(value, charset)
170
- when /^cc$/i
171
- CcField.new(value, charset)
172
- when /^bcc$/i
173
- BccField.new(value, charset)
174
- when /^message-id$/i
175
- MessageIdField.new(value, charset)
176
- when /^in-reply-to$/i
177
- InReplyToField.new(value, charset)
178
- when /^references$/i
179
- ReferencesField.new(value, charset)
180
- when /^subject$/i
181
- SubjectField.new(value, charset)
182
- when /^comments$/i
183
- CommentsField.new(value, charset)
184
- when /^keywords$/i
185
- KeywordsField.new(value, charset)
186
- when /^date$/i
187
- DateField.new(value, charset)
188
- when /^from$/i
189
- FromField.new(value, charset)
190
- when /^sender$/i
191
- SenderField.new(value, charset)
192
- when /^reply-to$/i
193
- ReplyToField.new(value, charset)
194
- when /^resent-date$/i
195
- ResentDateField.new(value, charset)
196
- when /^resent-from$/i
197
- ResentFromField.new(value, charset)
198
- when /^resent-sender$/i
199
- ResentSenderField.new(value, charset)
200
- when /^resent-to$/i
201
- ResentToField.new(value, charset)
202
- when /^resent-cc$/i
203
- ResentCcField.new(value, charset)
204
- when /^resent-bcc$/i
205
- ResentBccField.new(value, charset)
206
- when /^resent-message-id$/i
207
- ResentMessageIdField.new(value, charset)
208
- when /^return-path$/i
209
- ReturnPathField.new(value, charset)
210
- when /^received$/i
211
- ReceivedField.new(value, charset)
212
- when /^mime-version$/i
213
- MimeVersionField.new(value, charset)
214
- when /^content-transfer-encoding$/i
215
- ContentTransferEncodingField.new(value, charset)
216
- when /^content-description$/i
217
- ContentDescriptionField.new(value, charset)
218
- when /^content-disposition$/i
219
- ContentDispositionField.new(value, charset)
220
- when /^content-type$/i
221
- ContentTypeField.new(value, charset)
222
- when /^content-id$/i
223
- ContentIdField.new(value, charset)
224
- when /^content-location$/i
225
- ContentLocationField.new(value, charset)
226
- else
197
+ lower_case_name = name.to_s.downcase
198
+ header_name = nil
199
+ FIELDS_MAP.each do |field_name, _|
200
+ header_name = field_name if lower_case_name == field_name
201
+ end
202
+ if header_name
203
+ FIELDS_MAP[header_name].new(value, charset)
204
+ else
227
205
  OptionalField.new(name, value, charset)
228
206
  end
229
-
207
+
230
208
  end
231
209
 
232
210
  end
233
-
211
+
234
212
  end