dkim-query 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +51 -0
- data/.rspec +1 -0
- data/.travis.yml +17 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +3 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +20 -0
- data/README.md +98 -0
- data/Rakefile +23 -0
- data/bin/dkim-query +34 -0
- data/dkim-query.gemspec +26 -0
- data/lib/dkim/query/domain.rb +138 -0
- data/lib/dkim/query/exceptions.rb +8 -0
- data/lib/dkim/query/key.rb +135 -0
- data/lib/dkim/query/malformed_key.rb +33 -0
- data/lib/dkim/query/parser.rb +163 -0
- data/lib/dkim/query/query.rb +60 -0
- data/lib/dkim/query/version.rb +5 -0
- data/lib/dkim/query.rb +4 -0
- data/spec/domain_spec.rb +96 -0
- data/spec/key_spec.rb +91 -0
- data/spec/parser_spec.rb +243 -0
- data/spec/query_spec.rb +60 -0
- data/spec/spec_helper.rb +13 -0
- data/tasks/alexa.rb +43 -0
- metadata +118 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module DKIM
|
5
|
+
module Query
|
6
|
+
#
|
7
|
+
# DKIM parser.
|
8
|
+
#
|
9
|
+
# @see https://tools.ietf.org/html/rfc6376#section-3
|
10
|
+
#
|
11
|
+
class Parser < Parslet::Parser
|
12
|
+
|
13
|
+
root :record
|
14
|
+
rule(:record) do
|
15
|
+
(
|
16
|
+
fws? >> key_tag >> fws? >>
|
17
|
+
(str(';') >> fws? >> key_tag >> fws?).repeat(0) >> str(';').maybe
|
18
|
+
).as(:tag_list)
|
19
|
+
end
|
20
|
+
|
21
|
+
rule(:key_tag) do
|
22
|
+
(
|
23
|
+
key_v_tag |
|
24
|
+
key_g_tag |
|
25
|
+
key_h_tag |
|
26
|
+
key_k_tag |
|
27
|
+
key_n_tag |
|
28
|
+
key_p_tag |
|
29
|
+
key_s_tag |
|
30
|
+
key_t_tag
|
31
|
+
).as(:tag)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.key_tag_rule(name,&block)
|
35
|
+
rule(:"key_#{name}_tag") do
|
36
|
+
str(name).as(:name) >>
|
37
|
+
fws? >> str('=') >> fws? >>
|
38
|
+
instance_eval(&block).as(:value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def symbol(name)
|
43
|
+
str(name).as(:symbol)
|
44
|
+
end
|
45
|
+
|
46
|
+
key_tag_rule('v') { symbol('DKIM1') }
|
47
|
+
key_tag_rule('g') { key_g_tag_lpart }
|
48
|
+
rule(:key_g_tag_lpart) do
|
49
|
+
dot_atom_text.maybe >> (str('*') >> dot_atom_text.maybe).maybe
|
50
|
+
end
|
51
|
+
rule(:dot_atom_text) { atext.repeat(1) >> (str('.') >> atext.repeat(1)).repeat(0) }
|
52
|
+
|
53
|
+
key_tag_rule('h') do
|
54
|
+
key_h_tag_alg >>
|
55
|
+
(fws? >> str(':') >> fws? >> key_h_tag_alg).repeat(0)
|
56
|
+
end
|
57
|
+
rule(:key_h_tag_alg) { symbol('sha1') | symbol('sha256') | x_key_h_tag_alg }
|
58
|
+
rule(:x_key_h_tag_alg) { hyphenated_word }
|
59
|
+
|
60
|
+
key_tag_rule('k') { key_k_tag_type }
|
61
|
+
rule(:key_k_tag_type) { symbol('rsa') | x_key_k_tag_type }
|
62
|
+
rule(:x_key_k_tag_type) { hyphenated_word }
|
63
|
+
|
64
|
+
key_tag_rule('n') { qp_section }
|
65
|
+
key_tag_rule('p') { base64string.as(:asn1).maybe }
|
66
|
+
key_tag_rule('s') do
|
67
|
+
key_s_tag_type >> (fws? >> str(':') >> fws? >> key_s_tag_type).repeat(0)
|
68
|
+
end
|
69
|
+
rule(:key_s_tag_type) { symbol('email') | symbol('*') | x_key_s_tag_type }
|
70
|
+
rule(:x_key_s_tag_type) { hyphenated_word }
|
71
|
+
|
72
|
+
key_tag_rule('t') do
|
73
|
+
key_t_tag_flag >> (fws? >> str(':') >> fws? >> key_t_tag_flag).repeat(0)
|
74
|
+
end
|
75
|
+
rule(:key_t_tag_flag) { match['ys'] | x_key_t_tag_flag }
|
76
|
+
rule(:x_key_t_tag_flag) { hyphenated_word }
|
77
|
+
|
78
|
+
#
|
79
|
+
# Section 2.6: DKIM-Quoted-Printable
|
80
|
+
#
|
81
|
+
rule(:dkim_quoted_printable) do
|
82
|
+
(fws | hex_octet | dkim_safe_char).repeat(0)
|
83
|
+
end
|
84
|
+
rule(:dkim_safe_char) do
|
85
|
+
match['\x21-\x3a'] | str("\x3c") | match['\x3e-\x7e']
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Section 2.4: Common ABNF Tokens
|
90
|
+
#
|
91
|
+
rule(:hypthenated_word) do
|
92
|
+
alpha >> ((alpha | digit | str('-')).repeat(0) >> (alpha | digit)).maybe
|
93
|
+
end
|
94
|
+
rule(:base64string) do
|
95
|
+
(alpha | digit | str('+') | str('/') | fws).repeat(1) >>
|
96
|
+
(str('=') >> fws? >> (str('=') >> fws?)).maybe
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Section 2.3: Whitespace
|
101
|
+
#
|
102
|
+
rule(:sp) { str(' ') }
|
103
|
+
rule(:crlf) { str("\r\n") }
|
104
|
+
rule(:wsp) { match['\t '] }
|
105
|
+
rule(:wsp?) { wsp.maybe }
|
106
|
+
rule(:lwsp) { (wsp | crlf >> wsp).repeat(0) }
|
107
|
+
rule(:fws) { (wsp.repeat(0) >> crlf).maybe >> wsp.repeat(1) }
|
108
|
+
rule(:fws?) { fws.maybe }
|
109
|
+
|
110
|
+
#
|
111
|
+
# Character rules
|
112
|
+
#
|
113
|
+
rule(:alpha) { match['a-zA-Z'] }
|
114
|
+
rule(:digit) { match['0-9'] }
|
115
|
+
rule(:alnum) { match['a-zA-Z0-9'] }
|
116
|
+
rule(:valchar) { match['\x21-\x3a'] | match['\x3c-\x7e'] }
|
117
|
+
rule(:alnumpunc) { match['a-zA-Z0-9_'] }
|
118
|
+
rule(:atext) { alnum | match['!#$%&\'*+\-/=?^ `{|}~'] }
|
119
|
+
|
120
|
+
#
|
121
|
+
# Quoted printable
|
122
|
+
#
|
123
|
+
rule(:qp_section) do
|
124
|
+
(wsp? >> ptext.repeat(1) >> (wsp >> ptext.repeat(1)).repeat(0)).maybe
|
125
|
+
end
|
126
|
+
rule(:ptext) { hex_octet | safe_char }
|
127
|
+
rule(:safe_char) { match['\x21-\x3c'] | match['\x3e-\x7e'] }
|
128
|
+
rule(:hex_octet) { str('=') >> match['0-9A-F'].repeat(2,2) }
|
129
|
+
|
130
|
+
class Transform < Parslet::Transform
|
131
|
+
|
132
|
+
rule(:symbol => simple(:name)) { name.to_sym }
|
133
|
+
# XXX: temporarily disable ASN1 decoding, due to an OpenSSL bug.
|
134
|
+
# rule(:asn1 => simple(:blob)) { OpenSSL::ASN1.decode(blob) }
|
135
|
+
rule(:asn1 => simple(:blob)) { blob }
|
136
|
+
|
137
|
+
rule(tag: {name: simple(:name), value: subtree(:value)}) do
|
138
|
+
{name.to_sym => value}
|
139
|
+
end
|
140
|
+
|
141
|
+
rule(tag_list: subtree(:hashes)) do
|
142
|
+
case hashes
|
143
|
+
when Array then hashes.reduce(&:merge!)
|
144
|
+
else hashes
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Parses the text into structured data.
|
152
|
+
#
|
153
|
+
# @param [String] text
|
154
|
+
#
|
155
|
+
# @return [Hash]
|
156
|
+
#
|
157
|
+
def self.parse(text)
|
158
|
+
Transform.new.apply(new.parse(text))
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
3
|
+
module DKIM
|
4
|
+
module Query
|
5
|
+
#
|
6
|
+
# Queries the domain for all DKIM selectors.
|
7
|
+
#
|
8
|
+
# @param [String] domain
|
9
|
+
# The domain to query.
|
10
|
+
#
|
11
|
+
# @option options [Array<String>] :selectors
|
12
|
+
# sub-domain selectors.
|
13
|
+
#
|
14
|
+
# @option options [Resolv::DNS] :resolver
|
15
|
+
# Optional resolver to use.
|
16
|
+
#
|
17
|
+
# @return [Hash{String => String}]
|
18
|
+
# The DKIM keys for the domain.
|
19
|
+
#
|
20
|
+
# @api semipublic
|
21
|
+
#
|
22
|
+
def self.query(domain,options={})
|
23
|
+
selectors = options.fetch(:selectors) { selectors_for(domain) }
|
24
|
+
resolver = options.fetch(:resolver) { Resolv::DNS.new }
|
25
|
+
|
26
|
+
keys = {}
|
27
|
+
|
28
|
+
selectors.each do |selector|
|
29
|
+
host = "#{selector}._domainkey.#{domain}"
|
30
|
+
|
31
|
+
begin
|
32
|
+
keys[selector] = resolver.getresource(
|
33
|
+
host, Resolv::DNS::Resource::IN::TXT
|
34
|
+
).strings.join
|
35
|
+
rescue Resolv::ResolvError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
return keys
|
40
|
+
end
|
41
|
+
|
42
|
+
# Default known selectors
|
43
|
+
SELECTORS = %w[default dkim s1024 c1211]
|
44
|
+
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
#
|
48
|
+
def self.selectors_for(host)
|
49
|
+
SELECTORS + [host_without_tld(host)]
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
def self.host_without_tld(host)
|
56
|
+
host[0,host.rindex('.')]
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/lib/dkim/query.rb
ADDED
data/spec/domain_spec.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dkim/query/domain'
|
3
|
+
|
4
|
+
describe Domain do
|
5
|
+
let(:domain) { 'yahoo.com' }
|
6
|
+
let(:key) do
|
7
|
+
Key.new(
|
8
|
+
k: :rsa,
|
9
|
+
p: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB",
|
10
|
+
n: "A 1024 bit key;",
|
11
|
+
)
|
12
|
+
end
|
13
|
+
let(:selector) { 's1024' }
|
14
|
+
let(:keys) { {selector => key} }
|
15
|
+
|
16
|
+
subject { described_class.new(domain,keys) }
|
17
|
+
|
18
|
+
describe "#initialize" do
|
19
|
+
it "should set name" do
|
20
|
+
expect(subject.name).to be domain
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should set keys" do
|
24
|
+
expect(subject.keys).to be keys
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".parse" do
|
29
|
+
let(:key) do
|
30
|
+
%{k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB; n=A 1024 bit key}
|
31
|
+
end
|
32
|
+
let(:keys) { {selector => key} }
|
33
|
+
|
34
|
+
subject { described_class.parse(domain,keys) }
|
35
|
+
|
36
|
+
it "should parse the keys" do
|
37
|
+
expect(subject.keys[selector]).to be_kind_of(Key)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe ".query" do
|
42
|
+
subject { described_class.query(domain) }
|
43
|
+
|
44
|
+
it "should find all known keys" do
|
45
|
+
expect(subject.keys).to have_key('s1024')
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with custom selectors" do
|
49
|
+
let(:selectors) { ['google', 's1024'] }
|
50
|
+
|
51
|
+
subject { described_class.query(domain, selectors: selectors) }
|
52
|
+
|
53
|
+
it "should query those selectors only" do
|
54
|
+
expect(subject.keys).to have_key('s1024')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with no selectors" do
|
59
|
+
let(:selectors) { [] }
|
60
|
+
|
61
|
+
subject { described_class.query(domain, selectors: selectors) }
|
62
|
+
|
63
|
+
it "should not find any keys" do
|
64
|
+
expect(subject.keys).to be_empty
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#each" do
|
70
|
+
context "when given a block" do
|
71
|
+
it "should yield each Key" do
|
72
|
+
expect { |b| subject.each(&b) }.to yield_with_args(key)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when not given a block" do
|
77
|
+
it "should return an Enumerator" do
|
78
|
+
expect(subject.each).to be_kind_of(Enumerator)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#[]" do
|
84
|
+
context "when given a valid selector" do
|
85
|
+
it "should return the key" do
|
86
|
+
expect(subject[selector]).to be key
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when given an unknown selector" do
|
91
|
+
it "should return nil" do
|
92
|
+
expect(subject['foo']).to be_nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/spec/key_spec.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dkim/query/key'
|
3
|
+
|
4
|
+
describe Key do
|
5
|
+
let(:k) { :rsa }
|
6
|
+
let(:p) { "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB" }
|
7
|
+
let(:n) { "A 1024 bit key;" }
|
8
|
+
|
9
|
+
subject do
|
10
|
+
described_class.new(
|
11
|
+
k: k,
|
12
|
+
p: p,
|
13
|
+
n: n
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#initialize" do
|
18
|
+
it "should set @k" do
|
19
|
+
expect(subject.k).to be k
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should set @p" do
|
23
|
+
expect(subject.p).to be p
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should set @n" do
|
27
|
+
expect(subject.n).to be n
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:record) { %{k=#{k}; p=#{p}; n=#{n}} }
|
32
|
+
let(:invalid_record) { "v=spf1" }
|
33
|
+
|
34
|
+
describe ".parse!" do
|
35
|
+
context "when parsing a valid DKIM Key record" do
|
36
|
+
subject { described_class.parse!(record) }
|
37
|
+
|
38
|
+
it "should return a Key" do
|
39
|
+
expect(subject).to be_kind_of(described_class)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when parsing an invalid DKIM Key record" do
|
44
|
+
it "should raise an InvalidKey exception" do
|
45
|
+
expect {
|
46
|
+
described_class.parse!(invalid_record)
|
47
|
+
}.to raise_error(InvalidKey)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".parse" do
|
53
|
+
context "when parsing a valid DKIM Key record" do
|
54
|
+
subject { described_class.parse(record) }
|
55
|
+
|
56
|
+
it "should return a Key" do
|
57
|
+
expect(subject).to be_kind_of(described_class)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when parsing an invalid DKIM Key record" do
|
62
|
+
subject { described_class.parse(invalid_record) }
|
63
|
+
|
64
|
+
it "should return a MalformedKey" do
|
65
|
+
expect(subject).to be_kind_of(MalformedKey)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#to_hash" do
|
71
|
+
subject { super().to_hash }
|
72
|
+
|
73
|
+
it "should include :k" do
|
74
|
+
expect(subject[:k]).to be == k
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should include :p" do
|
78
|
+
expect(subject[:p]).to be == p
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should include :n" do
|
82
|
+
expect(subject[:n]).to be == n
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#to_s" do
|
87
|
+
it "should return a semicolon deliminited string" do
|
88
|
+
expect(subject.to_s).to be == "k=#{k}; p=#{p}; n=#{n}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dkim/query/parser'
|
3
|
+
|
4
|
+
describe Parser do
|
5
|
+
describe ".parse" do
|
6
|
+
subject { described_class }
|
7
|
+
|
8
|
+
let(:dkim) do
|
9
|
+
%{k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB; n=A 1024 bit key;}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should parse a DKIM record into a Hash" do
|
13
|
+
expect(subject.parse(dkim)).to be == {
|
14
|
+
k: :rsa,
|
15
|
+
p: %{MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB},
|
16
|
+
n: "A 1024 bit key;"
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "tags" do
|
22
|
+
describe "v" do
|
23
|
+
subject { super().key_v_tag }
|
24
|
+
|
25
|
+
it "should parse v=DKIM1" do
|
26
|
+
expect(subject.parse('v=DKIM1')).to be == {
|
27
|
+
name: 'v',
|
28
|
+
value: {symbol: 'DKIM1'}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "g" do
|
34
|
+
subject { super().key_g_tag }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "h" do
|
38
|
+
subject { super().key_h_tag }
|
39
|
+
|
40
|
+
it "should parse h=sha1" do
|
41
|
+
expect(subject.parse('h=sha1')).to be == {
|
42
|
+
name: 'h',
|
43
|
+
value: {symbol: 'sha1'}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should parse h=sha256" do
|
48
|
+
expect(subject.parse('h=sha256')).to be == {
|
49
|
+
name: 'h',
|
50
|
+
value: {symbol: 'sha256'}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should parse h=sha1:sha256" do
|
55
|
+
expect(subject.parse('h=sha1:sha256')).to be == {
|
56
|
+
name: 'h',
|
57
|
+
value: [
|
58
|
+
{symbol: 'sha1'},
|
59
|
+
{symbol: 'sha256'}
|
60
|
+
]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "k" do
|
66
|
+
subject { super().key_k_tag }
|
67
|
+
|
68
|
+
it "should parse k=rsa" do
|
69
|
+
expect(subject.parse('k=rsa')).to be == {
|
70
|
+
name: 'k',
|
71
|
+
value: {symbol: 'rsa'}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "n" do
|
77
|
+
subject { super().key_n_tag }
|
78
|
+
|
79
|
+
let(:notes) { %{A 1024 bit key} }
|
80
|
+
|
81
|
+
it "should parse n=..." do
|
82
|
+
expect(subject.parse("n=#{notes}")).to be == {
|
83
|
+
name: 'n',
|
84
|
+
value: notes
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "p" do
|
90
|
+
subject { super().key_p_tag }
|
91
|
+
|
92
|
+
let(:base64) { %{MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB} }
|
93
|
+
|
94
|
+
it "should parse p=..." do
|
95
|
+
expect(subject.parse("p=#{base64}")).to be == {
|
96
|
+
name: 'p',
|
97
|
+
value: {asn1: base64}
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "s" do
|
103
|
+
subject { super().key_s_tag }
|
104
|
+
|
105
|
+
it "should parse s=email" do
|
106
|
+
expect(subject.parse('s=email')).to be == {
|
107
|
+
name: 's',
|
108
|
+
value: {symbol: 'email'}
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should parse s=*" do
|
113
|
+
expect(subject.parse('s=*')).to be == {
|
114
|
+
name: 's',
|
115
|
+
value: {symbol: '*'}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should parse s=email:*" do
|
120
|
+
expect(subject.parse('s=email:*')).to be == {
|
121
|
+
name: 's',
|
122
|
+
value: [{symbol: 'email'}, {symbol: '*'}]
|
123
|
+
}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "t" do
|
128
|
+
subject { super().key_t_tag }
|
129
|
+
|
130
|
+
it "should parse t=y" do
|
131
|
+
expect(subject.parse('t=y')).to be == {
|
132
|
+
name: 't',
|
133
|
+
value: 'y'
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should parse t=s" do
|
138
|
+
expect(subject.parse('t=s')).to be == {
|
139
|
+
name: 't',
|
140
|
+
value: 's'
|
141
|
+
}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "rules" do
|
147
|
+
describe "dkim_quoted_printable" do
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "dkim_safe_char" do
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "hyphenated_word" do
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "base64string" do
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "qp_section" do
|
160
|
+
subject { super().qp_section }
|
161
|
+
|
162
|
+
it "should parse \"A\"" do
|
163
|
+
expect(subject.parse('A')).to be == 'A'
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should parse \"AAA\"" do
|
167
|
+
expect(subject.parse('AAA')).to be == 'AAA'
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should parse \" A\"" do
|
171
|
+
expect(subject.parse(' A')).to be == ' A'
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should parse \"A B\"" do
|
175
|
+
expect(subject.parse('A B')).to be == 'A B'
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should not parse \" \"" do
|
179
|
+
expect {
|
180
|
+
subject.parse(' ')
|
181
|
+
}.to raise_error(Parslet::ParseFailed)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should not parse \"A \"" do
|
185
|
+
expect {
|
186
|
+
subject.parse('A ')
|
187
|
+
}.to raise_error(Parslet::ParseFailed)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "hex_octet" do
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe Parser::Transform do
|
196
|
+
context "when given {symbol: ...}" do
|
197
|
+
let(:string) { 'foo' }
|
198
|
+
|
199
|
+
it "should convert the string into a Symbol" do
|
200
|
+
expect(subject.apply(symbol: string)).to be == string.to_sym
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "when given {tag: {name: ..., value: ...}}" do
|
205
|
+
let(:name) { 'foo' }
|
206
|
+
let(:value) { 'bar' }
|
207
|
+
|
208
|
+
it "should convert the string into Hash" do
|
209
|
+
expect(subject.apply(
|
210
|
+
{tag: {name: name, value: value}}
|
211
|
+
)).to be == {name.to_sym => value}
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context "when given {tag: {name: ..., value: [...]}}" do
|
216
|
+
let(:name) { 'foo' }
|
217
|
+
let(:value) { ['a', 'b'] }
|
218
|
+
|
219
|
+
it "should convert the string into Hash" do
|
220
|
+
expect(subject.apply(
|
221
|
+
{tag: {name: name, value: value}}
|
222
|
+
)).to be == {name.to_sym => value}
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "when {tag_list: {...}}" do
|
227
|
+
let(:hash) { {foo: 'bar'} }
|
228
|
+
|
229
|
+
it "should return the single Hash" do
|
230
|
+
expect(subject.apply({tag_list: hash})).to be == hash
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "when {tag_list: [{...}, ...]}" do
|
235
|
+
let(:hashes) { [{foo: 'bar'}, {baz: 'quix'}] }
|
236
|
+
let(:hash) { {foo: 'bar', baz: 'quix'} }
|
237
|
+
|
238
|
+
it "should merge the Hashes together" do
|
239
|
+
expect(subject.apply({tag_list: hashes})).to be == hash
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|