dkim 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,18 +1,24 @@
1
1
  # dkim Changelog
2
2
 
3
- ## 2012.04.15, Version 0.2.0
3
+ ## 1.0.0 (2012-01-15)
4
+ * DKIM-Signature header is now prepended rather than appended
5
+ * Headers are signed in the order they appear
6
+ * Correct signing of repeated headers
7
+ * Correct signing of missing headers
8
+
9
+ ## 0.2.0 (2012-04-15)
4
10
  * Warn and strip existing signatures in Dkim::Interceptor
5
11
  * Dkim options can be accessed and modified using new Dkim.options or signed_mail.options hash
6
12
  * Refactoring and better testing
7
13
  * Improved documentation
8
14
 
9
- ## 2011.12.10, Version 0.1.0
15
+ ## 0.1.0 (2011-12-10)
10
16
  * Ensure header lines are not folded using Dkim::Interceptor
11
17
 
12
- ## 2011.07.25, Version 0.0.3
18
+ ## 0.0.3 (2011-07-25)
13
19
  * add Dkim::Interceptor class for integration with rails and [mail](https://github.com/mikel/mail)
14
20
 
15
- ## 2011.06.01, Version 0.0.2
21
+ ## 0.0.2 (2011-06-01)
16
22
 
17
23
  * add convenience method Dkim.sign
18
24
  * support for the simple canonicalization algorithm
@@ -20,7 +26,7 @@
20
26
  * correct handling of an empty message body
21
27
 
22
28
 
23
- ## 2011.05.10, Version 0.0.1
29
+ ## 0.0.1 (2011-05-10)
24
30
 
25
31
  * Initial release
26
32
 
@@ -0,0 +1,26 @@
1
+ module Dkim
2
+ class CanonicalizedHeaders
3
+ include Enumerable
4
+ def initialize header_list, signed_headers
5
+ @header_list = header_list
6
+ @signed_headers = signed_headers.map(&:downcase)
7
+ end
8
+ def each(&block)
9
+ header_hash = Hash.new {|h,k| h[k] = []}
10
+ @header_list.each do |header|
11
+ header_hash[header.relaxed_key] << header
12
+ end
13
+
14
+ @signed_headers.each do |key|
15
+ if header = header_hash[key].pop
16
+ yield header
17
+ end
18
+ end
19
+ end
20
+ def to_s(canonicalization)
21
+ map do |header|
22
+ header.to_s(canonicalization) + "\r\n"
23
+ end.join
24
+ end
25
+ end
26
+ end
@@ -1,28 +1,37 @@
1
1
 
2
2
  require 'dkim/header'
3
+ require 'dkim/tag_value_list'
4
+ require 'dkim/encodings'
3
5
 
4
6
  module Dkim
5
7
  class DkimHeader < Header
8
+ attr_reader :list
6
9
  def initialize values={}
7
10
  self.key = 'DKIM-Signature'
8
- @values = values.to_a.flatten.each_slice(2).to_a
11
+ @list = TagValueList.new values
9
12
  end
10
13
  def value
11
- @values.map do |(k, v)|
12
- " #{k}=#{v}"
13
- end.join(';')
14
+ " #{@list}"
14
15
  end
15
16
  def [] k
16
- value = @values.detect {|(a,b)| a == k }
17
- value && value[1]
17
+ encoder_for(k).decode(@list[k])
18
18
  end
19
19
  def []= k, v
20
- value = @values.detect {|(a,b)| a == k }
21
- if !value
22
- value = [k, nil]
23
- @values << value
24
- end
25
- value[1] = v
20
+ @list[k] = encoder_for(k).encode(v)
21
+ end
22
+
23
+ private
24
+ def encoder_for key
25
+ case key
26
+ when *%w{v a c d h l q s t x}
27
+ Encodings::PlainText
28
+ when *%w{i z}
29
+ Encodings::DkimQuotedPrintable
30
+ when *%w{b bh}
31
+ Encodings::Base64
32
+ else
33
+ raise "unknown key: #{key}"
34
+ end.new
26
35
  end
27
36
  end
28
37
  end
@@ -0,0 +1,3 @@
1
+ require 'dkim/encodings/base64.rb'
2
+ require 'dkim/encodings/dkim_quoted_printable.rb'
3
+ require 'dkim/encodings/plain_text.rb'
@@ -0,0 +1,12 @@
1
+ module Dkim
2
+ module Encodings
3
+ class Base64
4
+ def decode data
5
+ data.gsub(/\s/, '').unpack('m0')[0]
6
+ end
7
+ def encode data
8
+ [data].pack('m0').gsub("\n", '')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+
2
+ module Dkim
3
+ module Encodings
4
+ # Implements DKIM-Quoted-Printable as described in rfc6376 section 2.11
5
+ class DkimQuotedPrintable
6
+ DkimUnafeChar = /[^\x21-\x3A\x3C\x3E-\x7E]/
7
+ def encode string
8
+ string.gsub(DkimUnafeChar) do |char|
9
+ "=%.2x" % char.unpack('C')
10
+ end
11
+ end
12
+ def decode string
13
+ string.gsub(/=([0-9A-F]{2})/) do
14
+ $1.hex.chr
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Dkim
2
+ module Encodings
3
+ class PlainText
4
+ def encode v; v; end
5
+ alias_method :decode, :encode
6
+ end
7
+ end
8
+ end
data/lib/dkim/header.rb CHANGED
@@ -38,5 +38,12 @@ module Dkim
38
38
  def canonical_simple
39
39
  "#{key}:#{value}"
40
40
  end
41
+
42
+ def self.parse header_string
43
+ header_string.split(/\r?\n(?!([ \t]))/).map do |header|
44
+ key, value = header.split(':', 2)
45
+ new(key, value)
46
+ end
47
+ end
41
48
  end
42
49
  end
@@ -14,8 +14,8 @@ module Dkim
14
14
  # generate new signature
15
15
  dkim_signature = SignedMail.new(message.encoded).dkim_header.value
16
16
 
17
- # append signature to message
18
- message.header.fields << Mail::DkimField.new(dkim_signature)
17
+ # prepend signature to message
18
+ message.header.fields.unshift Mail::DkimField.new(dkim_signature)
19
19
  message
20
20
  end
21
21
  end
@@ -3,8 +3,8 @@ require 'openssl'
3
3
  require 'dkim/body'
4
4
  require 'dkim/dkim_header'
5
5
  require 'dkim/header'
6
- require 'dkim/header_list'
7
6
  require 'dkim/options'
7
+ require 'dkim/canonicalized_headers'
8
8
 
9
9
  module Dkim
10
10
  class SignedMail
@@ -17,23 +17,28 @@ module Dkim
17
17
  def initialize message, options={}
18
18
  message = message.to_s.gsub(/\r?\n/, "\r\n")
19
19
  headers, body = message.split(/\r?\n\r?\n/, 2)
20
- @headers = HeaderList.new headers
20
+ @original_message = message
21
+ @headers = Header.parse headers
21
22
  @body = Body.new body
22
23
 
23
24
  # default options from Dkim.options
24
25
  @options = Dkim.options.merge(options)
25
26
  end
26
27
 
27
- # @return [Array<String>] Signed headers of message in their canonical forms
28
+ def canonicalized_headers
29
+ CanonicalizedHeaders.new(@headers, signed_headers)
30
+ end
31
+
32
+ # @return [Array<String>] lowercased names of headers in the order they are signed
28
33
  def signed_headers
29
- (@headers.map(&:relaxed_key) & signable_headers.map(&:downcase)).sort
34
+ @headers.map(&:relaxed_key).select do |key|
35
+ signable_headers.map(&:downcase).include?(key)
36
+ end
30
37
  end
31
38
 
32
39
  # @return [String] Signed headers of message in their canonical forms
33
40
  def canonical_header
34
- headers = signed_headers.map do |key|
35
- @headers[key].to_s(header_canonicalization) + "\r\n"
36
- end.join
41
+ canonicalized_headers.to_s(header_canonicalization)
37
42
  end
38
43
 
39
44
  # @return [String] Body of message in its canonical form
@@ -59,31 +64,24 @@ module Dkim
59
64
  dkim_header['t'] = (time || Time.now).to_i
60
65
 
61
66
  # Add body hash and blank signature
62
- dkim_header['bh']= base64_encode digest_alg.digest(canonical_body)
67
+ dkim_header['bh']= digest_alg.digest(canonical_body)
63
68
  dkim_header['h'] = signed_headers.join(':')
64
69
  dkim_header['b'] = ''
65
70
 
66
71
  # Calculate signature based on intermediate signature header
67
72
  headers = canonical_header
68
73
  headers << dkim_header.to_s(header_canonicalization)
69
- signature = base64_encode private_key.sign(digest_alg, headers)
70
- dkim_header['b'] = signature
74
+ dkim_header['b'] = private_key.sign(digest_alg, headers)
71
75
 
72
76
  dkim_header
73
77
  end
74
78
 
75
79
  # @return [String] Message combined with calculated dkim header signature
76
80
  def to_s
77
- headers = @headers.to_a + [dkim_header]
78
- headers.map(&:to_s).join("\r\n") +
79
- "\r\n\r\n" +
80
- @body.to_s
81
+ dkim_header.to_s + "\r\n" + @original_message
81
82
  end
82
83
 
83
84
  private
84
- def base64_encode data
85
- [data].pack('m0').gsub("\n",'')
86
- end
87
85
  def digest_alg
88
86
  case signing_algorithm
89
87
  when 'rsa-sha1'
@@ -0,0 +1,28 @@
1
+ module Dkim
2
+ class TagValueList
3
+ def initialize values={}
4
+ @keys = values.keys
5
+ @values = values.dup
6
+ end
7
+ def to_s
8
+ @keys.map do |k|
9
+ "#{k}=#{@values[k]}"
10
+ end.join('; ')
11
+ end
12
+ def [] k
13
+ @values[k]
14
+ end
15
+ def []= k, v
16
+ @keys << k unless self[k]
17
+ @values[k] = v
18
+ end
19
+ def self.parse string
20
+ list = new
21
+ string.split(';').each do |keyval|
22
+ key, value = keyval.split('=', 2)
23
+ list[key.strip] = value.strip
24
+ end
25
+ list
26
+ end
27
+ end
28
+ end
data/lib/dkim/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dkim
2
- VERSION = "0.2.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -14,11 +14,11 @@ module Mail
14
14
  end
15
15
 
16
16
  def encoded
17
- "#{name}:#{value}\n"
17
+ "#{name}:#{value}\r\n"
18
18
  end
19
19
 
20
20
  def decoded
21
- "#{name}:#{value}\n"
21
+ "#{name}:#{value}\r\n"
22
22
  end
23
23
  end
24
24
  end
@@ -0,0 +1,51 @@
1
+
2
+ require 'test_helper'
3
+
4
+ module Dkim
5
+ class CanonicalizedHeadersTest < MiniTest::Unit::TestCase
6
+ def test_maintains_order
7
+ headers = "ABCD".chars.map {|c| Header.new(c, c) }
8
+ header_keys = headers.map &:relaxed_key
9
+ header_keys.permutation.each do |signed_headers|
10
+ ch = CanonicalizedHeaders.new(headers, signed_headers)
11
+ assert_equal signed_headers, ch.map(&:relaxed_key)
12
+ end
13
+ end
14
+
15
+ def test_repeated_headers
16
+ headers = [
17
+ Header.new('A', '1'),
18
+ Header.new('B', '2'),
19
+ Header.new('C', '3'),
20
+ Header.new('A', '4'),
21
+ Header.new('D', '5')
22
+ ]
23
+ ch = CanonicalizedHeaders.new(headers, %w{A A B C D})
24
+ assert_equal %w{4 1 2 3 5}, ch.map(&:value)
25
+ assert_equal <<-eos.rfc_format, ch.to_s('simple')
26
+ A:4<CRLF>
27
+ A:1<CRLF>
28
+ B:2<CRLF>
29
+ C:3<CRLF>
30
+ D:5<CRLF>
31
+ eos
32
+ end
33
+
34
+ # missing headers should be ignored
35
+ def test_missing_headers
36
+ headers = [
37
+ Header.new('A', '1'),
38
+ Header.new('B', '2'),
39
+ Header.new('C', '3'),
40
+ ]
41
+ ch = CanonicalizedHeaders.new(headers, %w{A A B C})
42
+ assert_equal %w{a b c}, ch.map(&:relaxed_key)
43
+ assert_equal <<-eos.rfc_format, ch.to_s('simple')
44
+ A:1<CRLF>
45
+ B:2<CRLF>
46
+ C:3<CRLF>
47
+ eos
48
+ end
49
+ end
50
+ end
51
+
@@ -1,5 +1,6 @@
1
1
 
2
2
  require 'test_helper'
3
+ require 'base64'
3
4
 
4
5
  module Dkim
5
6
  class DkimHeaderTest < MiniTest::Unit::TestCase
@@ -15,8 +16,8 @@ module Dkim
15
16
  @header['q'] = 'dns/txt'
16
17
  @header['i'] = 'joe@football.example.com'
17
18
  @header['h'] = 'Received : From : To : Subject : Date : Message-ID'
18
- @header['bh']= '2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8='
19
- @header['b'] = 'AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk4yAUoqOB4nujc7YopdG5dWLSdNg6xNAZpOPr+kHxt1IrE+NahM6L/LbvaHutKVdkLLkpVaVVQPzeRDI009SO2Il5Lu7rDNH6mZckBdrIx0orEtZV4bmp/YzhwvcubU4='
19
+ @header['bh']= Base64.decode64 '2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8='
20
+ @header['b'] = Base64.decode64 'AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk4yAUoqOB4nujc7YopdG5dWLSdNg6xNAZpOPr+kHxt1IrE+NahM6L/LbvaHutKVdkLLkpVaVVQPzeRDI009SO2Il5Lu7rDNH6mZckBdrIx0orEtZV4bmp/YzhwvcubU4='
20
21
  end
21
22
 
22
23
  def test_correct_format
@@ -0,0 +1,24 @@
1
+ module Dkim
2
+ class EncodingsTest < MiniTest::Unit::TestCase
3
+ def test_plain_text
4
+ @encoder = Encodings::PlainText.new
5
+ assert_equal 'testing123', @encoder.encode('testing123')
6
+ assert_equal 'testing123', @encoder.decode('testing123')
7
+ end
8
+ def test_base64
9
+ @encoder = Encodings::Base64.new
10
+ assert_equal 'dGVzdGluZzEyMw==', @encoder.encode('testing123')
11
+ assert_equal 'testing123', @encoder.decode('dGVzdGluZzEyMw==')
12
+ end
13
+ def test_quoted_printable
14
+ @encoder = Encodings::DkimQuotedPrintable.new
15
+ assert_equal 'testing123', @encoder.encode('testing123')
16
+ assert_equal 'testing123', @encoder.decode('testing123')
17
+
18
+ encoded = 'From:foo@eng.example.net|To:joe@example.com|Subject:demo=20run|Date:July=205,=202005=203:44:08=20PM=20-0700'
19
+ decoded = 'From:foo@eng.example.net|To:joe@example.com|Subject:demo run|Date:July 5, 2005 3:44:08 PM -0700'
20
+ assert_equal encoded, @encoder.encode(decoded)
21
+ assert_equal decoded, @encoder.decode(encoded)
22
+ end
23
+ end
24
+ end
@@ -40,6 +40,9 @@ Joe.
40
40
  def setup
41
41
  @original_options = Dkim.options.dup
42
42
 
43
+ # ensure time does not change
44
+ Dkim.time = Time.now
45
+
43
46
  mail = EXAMPLEEMAIL.dup
44
47
 
45
48
  @mail = Mail.new(mail)
@@ -103,6 +106,20 @@ Joe.
103
106
  assert_equal 1, @mail.header.fields.count { |field| field.name =~ /^DKIM-Signature$/i }
104
107
  assert_equal 1, @mail.encoded.scan('DKIM-Signature').count
105
108
  end
109
+
110
+ def test_same_output_as_direct_usage
111
+ dkim_header = @mail['DKIM-Signature']
112
+
113
+ # Most necessary under simple
114
+ Dkim.header_canonicalization = 'simple'
115
+ Dkim.body_canonicalization = 'simple'
116
+
117
+ expected = Dkim.sign @mail.to_s
118
+
119
+ Interceptor.delivering_email(@mail)
120
+
121
+ assert_equal expected, @mail.to_s
122
+ end
106
123
  end
107
124
  end
108
125
 
@@ -1,3 +1,5 @@
1
+ require 'test_helper'
2
+
1
3
  module Dkim
2
4
  class OptionsTest < MiniTest::Unit::TestCase
3
5
  def setup
@@ -9,17 +9,18 @@ module Dkim
9
9
 
10
10
  def test_defaults
11
11
  signed_mail = SignedMail.new(@mail, :time => Time.at(1234567890))
12
- dkim_header = signed_mail.dkim_header
12
+ dkim_header = signed_mail.dkim_header.list
13
13
 
14
14
  assert_equal 'rsa-sha256', dkim_header['a']
15
15
  assert_equal 'brisbane', dkim_header['s']
16
16
  assert_equal 'example.com', dkim_header['d']
17
17
  assert_equal 'relaxed/relaxed', dkim_header['c']
18
18
  assert_equal 'dns/txt', dkim_header['q']
19
+ assert_equal 'from:to:subject:date:message-id', dkim_header['h']
19
20
 
20
21
  # bh value from RFC 6376
21
22
  assert_equal '2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=', dkim_header['bh']
22
- assert_equal 'mamSUb17FQSZY2lfkeAsH/DvmpHsXdaFAu6BfbVblGBQ5+2yIPCx+clF5wClVBj97utSZb1WwOM0iup1JL37FI/UG+bxHo+MdGLqbLR63THGEdVF8FVeST4o4EQTWe0H3P/sU2rRZ61+M2SrTS94QkKAgj89QNOG48xSAO9xdfs=', dkim_header['b']
23
+ assert_equal 'dQOeSpGJTfSbX4hPGGsy4ipcNAzC/33K7XaEXkjBneJJhv6MczHkJNsfmXeYESNIh5WVTuvE5IbnDPBVFrL+b3GKiLiyp/vlKO2NJViX4dLnKT/GdxjJh06ljZcYjUA+PorHvMwdu+cDsCffN8A7IhfVdsFruQr3vFPD0JyJ9XU=', dkim_header['b']
23
24
  end
24
25
 
25
26
  def test_overrides
@@ -33,15 +34,16 @@ module Dkim
33
34
  :time => Time.at(1234567890)
34
35
  }
35
36
  signed_mail = SignedMail.new(@mail, options)
36
- dkim_header = signed_mail.dkim_header
37
-
38
- assert_equal 'rsa-sha1', dkim_header['a']
39
- assert_equal 'sidney', dkim_header['s']
40
- assert_equal 'example.org', dkim_header['d']
41
- assert_equal 'simple/simple', dkim_header['c']
42
- assert_equal 'dns/txt', dkim_header['q']
43
- assert_equal 'yk6W9pJJilr5MMgeEdSd7J3IaJI=', dkim_header['bh']
44
- assert_equal 'sqYGmen+fouyIj83HuJ1v+1x40xp481bLxxcgAWMFsWYEwG05KYl+o0ZWn8jqgd1coKlX29o9iFjcMtZHudT8KpOdcLVYpY3gxzNfEgH79eRz32/ieGgroSK2GoMA/aV1QkxfUZexLUdj9oOX8uaMYXDkj8RGmlEGi+NDz/e4sE=', dkim_header['b']
37
+ dkim_header = signed_mail.dkim_header.list
38
+
39
+ assert_equal 'rsa-sha1', dkim_header['a']
40
+ assert_equal 'sidney', dkim_header['s']
41
+ assert_equal 'example.org', dkim_header['d']
42
+ assert_equal 'simple/simple', dkim_header['c']
43
+ assert_equal 'dns/txt', dkim_header['q']
44
+ assert_equal "from:to:subject:date:message-id", dkim_header['h']
45
+ assert_equal 'yk6W9pJJilr5MMgeEdSd7J3IaJI=', dkim_header['bh']
46
+ assert_equal 't+dk4yxTI2ByZxxRzkwhZhM4WzTZjGWHiWnS2t4pg7oT7fAIlMrfihJ/CIvGmYqYv4lbq4LStHqHx9TmEgxrkjLevHtuqhxkN55xJ2vA2QzTzFi2fMDZ4fFqWy4QtvlLjBAhevG+LXpmjPYec1cyeMlHlPAthq5+RNi6NHErJiM=', dkim_header['b']
45
47
  end
46
48
 
47
49
  def test_empty_body_hashes
@@ -56,11 +58,27 @@ module Dkim
56
58
  ['47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', {:body_canonicalization => 'relaxed', :signing_algorithm => 'rsa-sha256'}],
57
59
  ].each do |body_hash, options|
58
60
  signed_mail = SignedMail.new(@mail, options)
59
- dkim_header = signed_mail.dkim_header
61
+ dkim_header = signed_mail.dkim_header.list
60
62
 
61
63
  assert_equal body_hash, dkim_header['bh']
62
64
  end
63
65
  end
66
+
67
+ def test_multiple_instances_of_header
68
+ @mail = <<-eos
69
+ Received: <A>
70
+ Received: <B>
71
+ Received: <C>
72
+
73
+ eos
74
+
75
+ signed_mail = SignedMail.new(@mail, :header_canonicalization => 'simple', :signable_headers => Dkim::DefaultHeaders + ['Received'])
76
+
77
+ assert_equal "received:received:received", signed_mail.dkim_header['h']
78
+
79
+ headers = signed_mail.canonical_header
80
+ assert_equal "Received: <C>\r\nReceived: <B>\r\nReceived: <A>\r\n", headers.to_s
81
+ end
64
82
  end
65
83
  end
66
84
 
@@ -0,0 +1,28 @@
1
+
2
+ require 'test_helper'
3
+
4
+ module Dkim
5
+ class TagValueListTest < MiniTest::Unit::TestCase
6
+ def test_replacement
7
+ @list = TagValueList.new
8
+
9
+ @list['a'] = '1'
10
+ @list['b'] = '2'
11
+ @list['c'] = '3'
12
+ assert_equal 'a=1; b=2; c=3', @list.to_s
13
+
14
+ @list['b'] = '4'
15
+ assert_equal 'a=1; b=4; c=3', @list.to_s
16
+ end
17
+ def test_correct_format
18
+ @list = TagValueList.new
19
+
20
+ @list['b'] = '2'
21
+ @list['a'] = '1'
22
+ @list['c'] = '!@#$%^'
23
+
24
+ assert_equal 'b=2; a=1; c=!@#$%^', @list.to_s
25
+ end
26
+ end
27
+ end
28
+
data/test/test_helper.rb CHANGED
@@ -1,4 +1,6 @@
1
1
 
2
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
3
+
2
4
  require 'minitest/autorun'
3
5
  require 'dkim'
4
6
 
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dkim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
4
  prerelease:
5
+ version: 1.0.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - John Hawthorn
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-16 00:00:00.000000000 Z
12
+ date: 2013-01-15 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: gem for adding DKIM signatures to email messages
15
15
  email:
@@ -31,19 +31,27 @@ files:
31
31
  - lib/dkim.rb
32
32
  - lib/dkim/body.rb
33
33
  - lib/dkim/canonicalizable.rb
34
+ - lib/dkim/canonicalized_headers.rb
34
35
  - lib/dkim/dkim_header.rb
36
+ - lib/dkim/encodings.rb
37
+ - lib/dkim/encodings/base64.rb
38
+ - lib/dkim/encodings/dkim_quoted_printable.rb
39
+ - lib/dkim/encodings/plain_text.rb
35
40
  - lib/dkim/header.rb
36
- - lib/dkim/header_list.rb
37
41
  - lib/dkim/interceptor.rb
38
42
  - lib/dkim/options.rb
39
43
  - lib/dkim/signed_mail.rb
44
+ - lib/dkim/tag_value_list.rb
40
45
  - lib/dkim/version.rb
41
46
  - lib/mail/dkim_field.rb
42
47
  - test/dkim/canonicalization_test.rb
48
+ - test/dkim/canonicalized_headers_test.rb
43
49
  - test/dkim/dkim_header_test.rb
50
+ - test/dkim/encodings_test.rb
44
51
  - test/dkim/interceptor_test.rb
45
52
  - test/dkim/options_test.rb
46
53
  - test/dkim/signed_mail_test.rb
54
+ - test/dkim/tag_value_list_test.rb
47
55
  - test/test_helper.rb
48
56
  homepage: https://github.com/jhawthorn/dkim
49
57
  licenses: []
@@ -57,28 +65,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
65
  - - ! '>='
58
66
  - !ruby/object:Gem::Version
59
67
  version: '0'
60
- segments:
61
- - 0
62
- hash: -2701069676603955611
63
68
  required_rubygems_version: !ruby/object:Gem::Requirement
64
69
  none: false
65
70
  requirements:
66
71
  - - ! '>='
67
72
  - !ruby/object:Gem::Version
68
73
  version: '0'
69
- segments:
70
- - 0
71
- hash: -2701069676603955611
72
74
  requirements: []
73
75
  rubyforge_project: dkim
74
- rubygems_version: 1.8.17
76
+ rubygems_version: 1.8.24
75
77
  signing_key:
76
78
  specification_version: 3
77
79
  summary: DKIM library in ruby
78
80
  test_files:
79
81
  - test/dkim/canonicalization_test.rb
82
+ - test/dkim/canonicalized_headers_test.rb
80
83
  - test/dkim/dkim_header_test.rb
84
+ - test/dkim/encodings_test.rb
81
85
  - test/dkim/interceptor_test.rb
82
86
  - test/dkim/options_test.rb
83
87
  - test/dkim/signed_mail_test.rb
88
+ - test/dkim/tag_value_list_test.rb
84
89
  - test/test_helper.rb
@@ -1,19 +0,0 @@
1
- module Dkim
2
- class HeaderList
3
- include Enumerable
4
- def initialize headers
5
- @headers = headers.split(/\r?\n(?!([ \t]))/).map do |header|
6
- key, value = header.split(':', 2)
7
- Header.new(key, value)
8
- end
9
- end
10
- def [](key)
11
- @headers.detect do |header|
12
- header.relaxed_key == key
13
- end
14
- end
15
- def each(&block)
16
- @headers.each(&block)
17
- end
18
- end
19
- end