EMMA Coverage Report (generated Thu Nov 19 17:07:02 CET 2009)
[all classes][org.apache.james.jdkim.tagvalue]

COVERAGE SUMMARY FOR SOURCE FILE [PublicKeyRecordImpl.java]

nameclass, %method, %block, %line, %
PublicKeyRecordImpl.java100% (1/1)100% (14/14)100% (364/364)100% (63/63)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class PublicKeyRecordImpl100% (1/1)100% (14/14)100% (364/364)100% (63/63)
<static initializer> 100% (1/1)100% (7/7)100% (2/2)
PublicKeyRecordImpl (String): void 100% (1/1)100% (4/4)100% (2/2)
getAcceptableHashMethods (): List 100% (1/1)100% (16/16)100% (3/3)
getAcceptableKeyTypes (): List 100% (1/1)100% (8/8)100% (1/1)
getFlags (): List 100% (1/1)100% (40/40)100% (6/6)
getGranularityPattern (): Pattern 100% (1/1)100% (96/96)100% (12/12)
getPublicKey (): PublicKey 100% (1/1)100% (55/55)100% (10/10)
init (): void 100% (1/1)100% (42/42)100% (8/8)
isDenySubdomains (): boolean 100% (1/1)100% (5/5)100% (1/1)
isHashMethodSupported (CharSequence): boolean 100% (1/1)100% (12/12)100% (4/4)
isKeyTypeSupported (CharSequence): boolean 100% (1/1)100% (8/8)100% (2/2)
isTesting (): boolean 100% (1/1)100% (5/5)100% (1/1)
newTagValue (): Map 100% (1/1)100% (4/4)100% (1/1)
validate (): void 100% (1/1)100% (62/62)100% (10/10)

1/****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one   *
3 * or more contributor license agreements.  See the NOTICE file *
4 * distributed with this work for additional information        *
5 * regarding copyright ownership.  The ASF licenses this file   *
6 * to you under the Apache License, Version 2.0 (the            *
7 * "License"); you may not use this file except in compliance   *
8 * with the License.  You may obtain a copy of the License at   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
12 * Unless required by applicable law or agreed to in writing,   *
13 * software distributed under the License is distributed on an  *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15 * KIND, either express or implied.  See the License for the    *
16 * specific language governing permissions and limitations      *
17 * under the License.                                           *
18 ****************************************************************/
19 
20package org.apache.james.jdkim.tagvalue;
21 
22import java.security.KeyFactory;
23import java.security.NoSuchAlgorithmException;
24import java.security.PublicKey;
25import java.security.interfaces.RSAPublicKey;
26import java.security.spec.InvalidKeySpecException;
27import java.security.spec.X509EncodedKeySpec;
28import java.util.ArrayList;
29import java.util.LinkedHashMap;
30import java.util.List;
31import java.util.Map;
32import java.util.regex.Pattern;
33 
34import org.apache.commons.codec.binary.Base64;
35import org.apache.james.jdkim.api.PublicKeyRecord;
36 
37public class PublicKeyRecordImpl extends TagValue implements PublicKeyRecord {
38 
39    private static final String atom = "[a-zA-Z0-9!#$%&'*+/=?^_`{}|~-]+";
40    // TODO this should support CFWS: are they supported in DKIM for real?
41    private static final String dotAtomText = "(" + atom + ")?\\*?(" + atom
42            + ")?";
43    private static final Pattern granularityPattern = Pattern.compile("^"
44            + dotAtomText + "$");
45 
46    // SPEC: hyphenated-word = ALPHA [ *(ALPHA / DIGIT / "-") (ALPHA / DIGIT) ]
47    private static Pattern hyphenatedWordPattern = Pattern
48            .compile("^[a-zA-Z]([a-zA-Z0-9-]*[a-zA-Z0-9])?$");
49 
50    public PublicKeyRecordImpl(String data) {
51        super(data);
52    }
53 
54    protected Map newTagValue() {
55        // extensions may override this to use TreeMaps in order to keep track
56        // of orders
57        return new LinkedHashMap();
58    }
59 
60    protected void init() {
61        mandatoryTags.add("p");
62        defaults.put("v", "DKIM1");
63        defaults.put("g", "*");
64        defaults.put("h", ANY);
65        defaults.put("k", "rsa");
66        defaults.put("s", "*");
67        defaults.put("t", "");
68    }
69 
70    // TODO do we treat v=NONDKIM1 records, syntax error records and v=DKIM1 in
71    // the middle records in the same way?
72    public void validate() {
73        super.validate();
74        if (containsTag("v")) {
75            // if "v" is specified it must be the first tag
76            String firstKey = (String) tagSet().iterator().next();
77            if (!"v".equals(firstKey))
78                throw new IllegalStateException(
79                        "Existing v= tag MUST be the first in the record list ("
80                                + firstKey + ")");
81        }
82        if (!"DKIM1".equals(getValue("v")))
83            throw new IllegalStateException(
84                    "Unknown version for v= (expected DKIM1): " + getValue("v"));
85        if ("".equals(getValue("p")))
86            throw new IllegalStateException("Revoked key. 'p=' in record");
87    }
88 
89    /**
90     * @see org.apache.james.jdkim.api.PublicKeyRecord#isHashMethodSupported(java.lang.CharSequence)
91     */
92    public boolean isHashMethodSupported(CharSequence hash) {
93        List hashes = getAcceptableHashMethods();
94        if (hashes == null)
95            return true;
96        return isInListCaseInsensitive(hash, hashes);
97    }
98 
99    /**
100     * @see org.apache.james.jdkim.api.PublicKeyRecord#isKeyTypeSupported(java.lang.CharSequence)
101     */
102    public boolean isKeyTypeSupported(CharSequence hash) {
103        List hashes = getAcceptableKeyTypes();
104        return isInListCaseInsensitive(hash, hashes);
105    }
106 
107    /**
108     * @see org.apache.james.jdkim.api.PublicKeyRecord#getAcceptableHashMethods()
109     */
110    public List/* String */getAcceptableHashMethods() {
111        if (ANY.equals(getValue("h")))
112            return null;
113        return stringToColonSeparatedList(getValue("h").toString(),
114                hyphenatedWordPattern);
115    }
116 
117    /**
118     * @see org.apache.james.jdkim.api.PublicKeyRecord#getAcceptableKeyTypes()
119     */
120    public List/* String */getAcceptableKeyTypes() {
121        return stringToColonSeparatedList(getValue("k").toString(),
122                hyphenatedWordPattern);
123    }
124 
125    /**
126     * @see org.apache.james.jdkim.api.PublicKeyRecord#getGranularityPattern()
127     */
128    public Pattern getGranularityPattern() {
129        String g = getValue("g").toString();
130        int pStar = g.indexOf('*');
131        if (VALIDATION) {
132            if (!granularityPattern.matcher(g).matches())
133                throw new IllegalStateException("Syntax error in granularity: "
134                        + g);
135        }
136        if (g.length() == 0) {
137            // TODO this works but smells too much as an hack.
138            // in case of "g=" with nothing specified then we return a pattern
139            // that won't match
140            // SPEC: An empty "g=" value never matches any addresses.
141            return Pattern.compile("@");
142        } else if (pStar != -1) {
143            if (g.indexOf('*', pStar + 1) != -1)
144                throw new IllegalStateException(
145                        "Invalid granularity using more than one wildcard: "
146                                + g);
147            String pattern = "^\\Q" + g.subSequence(0, pStar).toString()
148                    + "\\E.*\\Q"
149                    + g.subSequence(pStar + 1, g.length()).toString() + "\\E$";
150            return Pattern.compile(pattern);
151        } else {
152            // TODO we need some escaping. On Java 5 we have Pattern.quote that
153            // is better
154            return Pattern.compile("^\\Q" + g + "\\E$");
155        }
156    }
157 
158    public List getFlags() {
159        String flags = getValue("t").toString();
160        String[] flagsStrings = flags.split(":");
161        List res = new ArrayList();
162        for (int i = 0; i < flagsStrings.length; i++) {
163            res.add(trimFWS(flagsStrings[i], 0, flagsStrings[i].length() - 1,
164                    true).toString());
165        }
166        return res;
167    }
168 
169    public boolean isDenySubdomains() {
170        return getFlags().contains("s");
171    }
172 
173    public boolean isTesting() {
174        return getFlags().contains("y");
175    }
176 
177    /**
178     * @see org.apache.james.jdkim.api.PublicKeyRecord#getPublicKey()
179     */
180    public PublicKey getPublicKey() {
181        try {
182            String p = getValue("p").toString();
183            byte[] key = Base64.decodeBase64(p.getBytes());
184            KeyFactory keyFactory;
185            keyFactory = KeyFactory.getInstance(getValue("k").toString());
186            X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(key);
187            RSAPublicKey rsaKey;
188            rsaKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);
189            return rsaKey;
190        } catch (NoSuchAlgorithmException e) {
191            throw new IllegalStateException("Unknown algorithm: "
192                    + e.getMessage());
193        } catch (InvalidKeySpecException e) {
194            throw new IllegalStateException("Invalid key spec: "
195                    + e.getMessage());
196        }
197    }
198 
199}

[all classes][org.apache.james.jdkim.tagvalue]
EMMA 2.0.5312 (C) Vladimir Roubtsov