Added:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java?rev=822372&view=auto
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
(added)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
Tue Oct 6 17:45:01 2009
@@ -0,0 +1,193 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jdkim.tagvalue;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.james.jdkim.PublicKeyRecord;
+import org.apache.james.jdkim.SignatureRecord;
+
+public class PublicKeyRecordImpl extends TagValue implements PublicKeyRecord {
+
+ private static final String atom = "[a-zA-Z0-9!#$%&'*+/=?^_`{}|~-]+";
+ // TODO this should support CFWS: are they supported in DKIM for real?
+ private static final String dotAtomText = "("+atom+")?\\*?("+atom+")?";
+ private static final Pattern granularityPattern =
Pattern.compile("^"+dotAtomText+"$");
+
+ // SPEC: hyphenated-word = ALPHA [ *(ALPHA / DIGIT / "-") (ALPHA /
DIGIT) ]
+ private static Pattern hyphenatedWordPattern =
Pattern.compile("^[a-zA-Z]([a-zA-Z0-9-]*[a-zA-Z0-9])?$");
+
+ public PublicKeyRecordImpl(String data) {
+ super(data);
+ }
+
+ protected void init() {
+ // extensions may override this to use TreeMaps in order to
keep track of orders
+ tagValues = new LinkedHashMap();
+ mandatoryTags.add("p");
+ defaults.put("v", "DKIM1");
+ defaults.put("g", "*");
+ defaults.put("h", ANY);
+ defaults.put("k", "rsa");
+ defaults.put("s", "*");
+ defaults.put("t", "");
+ }
+
+ // TODO do we treat v=NONDKIM1 records, syntax error records and
v=DKIM1 in the middle records
+ // in the same way?
+ public void validate() {
+ super.validate();
+ if (tagValues.containsKey("v")) {
+ // if "v" is specified it must be the first tag
+ String firstKey = (String) ((LinkedHashMap)
tagValues).keySet().iterator().next();
+ if (!"v".equals(firstKey)) throw new
IllegalStateException("Existing v= tag MUST be the first in the record list
("+firstKey+")");
+ }
+ if (!"DKIM1".equals(getValue("v"))) throw new
IllegalStateException("Unknown version for v= (expected DKIM1):
"+getValue("v"));
+ if ("".equals(getValue("p"))) throw new
IllegalStateException("Revoked key. 'p=' in record");
+ }
+
+ /**
+ * @see
org.apache.james.jdkim.PublicKeyRecord#isHashMethodSupported(java.lang.CharSequence)
+ */
+ public boolean isHashMethodSupported(CharSequence hash) {
+ List hashes = getAcceptableHashMethods();
+ if (hashes == null) return true;
+ return isInListCaseInsensitive(hash, hashes);
+ }
+
+ /**
+ * @see
org.apache.james.jdkim.PublicKeyRecord#isKeyTypeSupported(java.lang.CharSequence)
+ */
+ public boolean isKeyTypeSupported(CharSequence hash) {
+ List hashes = getAcceptableKeyTypes();
+ return isInListCaseInsensitive(hash, hashes);
+ }
+
+ /**
+ * @see
org.apache.james.jdkim.PublicKeyRecord#getAcceptableHashMethods()
+ */
+ public List/* String */ getAcceptableHashMethods() {
+ if (ANY.equals(getValue("h"))) return null;
+ return stringToColonSeparatedList(getValue("h").toString(),
hyphenatedWordPattern);
+ }
+ /**
+ * @see org.apache.james.jdkim.PublicKeyRecord#getAcceptableKeyTypes()
+ */
+ public List/* String */ getAcceptableKeyTypes() {
+ return stringToColonSeparatedList(getValue("k").toString(),
hyphenatedWordPattern);
+ }
+
+
+ /**
+ * @see org.apache.james.jdkim.PublicKeyRecord#getGranularityPattern()
+ */
+ public Pattern getGranularityPattern() {
+ String g = getValue("g").toString();
+ int pStar = g.indexOf('*');
+ if (VALIDATION) {
+ if (!granularityPattern.matcher(g).matches()) throw new
IllegalStateException("Syntax error in granularity: "+g);
+ }
+ if (g.length() == 0) {
+ // TODO this works but smells too much as an hack.
+ // in case of "g=" with nothing specified then we
return a pattern that won't match
+ // SPEC: An empty "g=" value never matches any
addresses.
+ return Pattern.compile("@");
+ } else if (pStar != -1) {
+ if (g.indexOf('*',pStar+1) != -1) throw new
IllegalStateException("Invalid granularity using more than one wildcard: "+g);
+ String pattern = "^"+Pattern.quote(g.subSequence(0,
pStar).toString())+".*"+Pattern.quote(g.subSequence(pStar+1,
g.length()).toString())+"$";
+ return Pattern.compile(pattern);
+ } else {
+ return Pattern.compile("^"+Pattern.quote(g)+"$");
+ }
+ }
+
+ public List getFlags() {
+ String flags = getValue("t").toString();
+ String[] flagsStrings = flags.split(":");
+ List res = new ArrayList();
+ for (int i = 0; i < flagsStrings.length; i++) {
+ res.add(trimFWS(flagsStrings[i], 0,
flagsStrings[i].length()-1, true).toString());
+ }
+ return res;
+ }
+
+ public boolean isDenySubdomains() {
+ return getFlags().contains("s");
+ }
+
+ public boolean isTesting() {
+ return getFlags().contains("y");
+ }
+
+
+ /**
+ * @see org.apache.james.jdkim.PublicKeyRecord#getPublicKey()
+ */
+ public PublicKey getPublicKey() {
+ try {
+ String p = getValue("p").toString();
+ byte[] key = Base64.decodeBase64( p.getBytes() );
+ KeyFactory keyFactory;
+ keyFactory =
KeyFactory.getInstance(getValue("k").toString());
+ X509EncodedKeySpec pubSpec = new
X509EncodedKeySpec(key);
+ RSAPublicKey rsaKey;
+ rsaKey = (RSAPublicKey)
keyFactory.generatePublic(pubSpec);
+ return rsaKey;
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("Unknown algorithm",e);
+ } catch (InvalidKeySpecException e) {
+ throw new IllegalStateException("Invalid key spec",e);
+ }
+ }
+
+ /**
+ * @see
org.apache.james.jdkim.PublicKeyRecord#apply(org.apache.james.jdkim.SignatureRecord)
+ */
+ public void apply(SignatureRecord sign) {
+ if
(!getGranularityPattern().matcher(sign.getIdentityLocalPart()).matches()) {
+ throw new IllegalStateException("inapplicable key for
g="+getValue("g")+" and identity local="+sign.getIdentityLocalPart()+" Pattern:
"+getGranularityPattern().pattern());
+ }
+
+ if (!isHashMethodSupported(sign.getHashMethod())) {
+ throw new IllegalStateException("inappropriate hash
method h="+getValue("h")+" and
a="+sign.getHashKeyType()+"/"+sign.getHashMethod());
+ }
+ if (!isKeyTypeSupported(sign.getHashKeyType())) {
+ throw new IllegalStateException("inappropriate key type
k="+getValue("k")+" and a="+sign.getHashKeyType()+"/"+sign.getHashMethod());
+ }
+
+ if (isDenySubdomains()) {
+ if
(!sign.getIdentity().toString().toLowerCase().endsWith(("@"+sign.getDToken()).toLowerCase()))
{
+ throw new IllegalStateException("AUID in
subdomain of SDID is not allowed by the public key record.");
+ }
+ }
+
+ }
+
+}
Propchange:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java?rev=822372&view=auto
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
(added)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
Tue Oct 6 17:45:01 2009
@@ -0,0 +1,237 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jdkim.tagvalue;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.james.jdkim.CodecUtil;
+import org.apache.james.jdkim.SignatureRecord;
+
+import
com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
+import com.sun.org.apache.xml.internal.security.utils.Base64;
+
+
+public class SignatureRecordImpl extends TagValue implements SignatureRecord {
+
+ // TODO ftext is defined as a sequence of at least one in %d33-57 or
%d59-126
+ private static Pattern hdrNamePattern = Pattern.compile("^[^:
\r\n\t]+$");
+
+ public SignatureRecordImpl(String data) {
+ super(data);
+ }
+
+ protected void init() {
+ super.init();
+
+ mandatoryTags.add("v");
+ mandatoryTags.add("a");
+ mandatoryTags.add("b");
+ mandatoryTags.add("bh");
+ mandatoryTags.add("d");
+ mandatoryTags.add("h");
+ mandatoryTags.add("s");
+
+ defaults.put("c", "simple/simple");
+ defaults.put("l", ALL);
+ defaults.put("q", "dns/txt");
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#validate()
+ */
+ public void validate() throws IllegalStateException {
+ super.validate();
+ // TODO: what about v=0.5 and no v= at all?
+ // do specs allow parsing? what should we check?
+ if (!"1".equals(getValue("v"))) throw new
IllegalStateException("Invalid DKIM-Signature version (expected '1'):
"+getValue("v"));
+ if (getValue("h").length() == 0) throw new
IllegalStateException("Tag h= cannot be empty.");
+ if
(!getIdentity().toString().toLowerCase().endsWith(("@"+getValue("d")).toLowerCase())
+ &&
!getIdentity().toString().toLowerCase().endsWith(("."+getValue("d")).toLowerCase()))
throw new IllegalStateException("Domain mismatch");
+
+ // when "x=" exists and signature expired then return PERMFAIL
(signature expired)
+ if (getValue("x") != null) {
+ long expiration =
Long.parseLong(getValue("x").toString());
+ long lifetime = (expiration -
System.currentTimeMillis() / 1000);
+ String measure = "s";
+ if (lifetime < 0) {
+ lifetime = -lifetime;
+ if (lifetime > 600) {
+ lifetime = lifetime / 60;
+ measure = "m";
+ if (lifetime > 600) {
+ lifetime = lifetime / 60;
+ measure = "h";
+ if (lifetime > 120) {
+ lifetime = lifetime /
24;
+ measure = "d";
+ if (lifetime > 90) {
+ lifetime =
lifetime / 30;
+ measure ="
months";
+ if (lifetime >
24) {
+
lifetime = lifetime / 12;
+ measure
= " years";
+ }
+ }
+ }
+ }
+ }
+ throw new IllegalStateException("Signature is
expired since "+lifetime+measure+".");
+ }
+ }
+
+ // when "h=" does not contain "from" return PERMFAIL (From
field not signed).
+ if (!isInListCaseInsensitive("from", getHeaders())) throw new
IllegalStateException("From field not signed");
+ // TODO support ignoring signature for certain d values
(externally to this class).
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#getHeaders()
+ */
+ public List/* CharSequence */ getHeaders() {
+ return stringToColonSeparatedList(getValue("h").toString(),
hdrNamePattern);
+ }
+
+ // If i= is unspecified the default is @d
+ protected CharSequence getDefault(String tag) {
+ if ("i".equals(tag)) {
+ return "@"+getValue("d");
+ } else return super.getDefault(tag);
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#getIdentityLocalPart()
+ */
+ public CharSequence getIdentityLocalPart() {
+ String identity = getIdentity().toString();
+ int pAt = identity.indexOf('@');
+ return identity.subSequence(0, pAt);
+ }
+
+ public CharSequence getIdentity() {
+ return CodecUtil.dkimQuotedPrintableDecode(getValue("i"));
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#getHashKeyType()
+ */
+ public CharSequence getHashKeyType() {
+ String a = getValue("a").toString();
+ int pHyphen = a.indexOf('-');
+ // TODO x-sig-a-tag-h = ALPHA *(ALPHA / DIGIT)
+ if (pHyphen == -1) throw new IllegalStateException("Invalid
hash algorythm (key type): "+a);
+ return a.subSequence(0, pHyphen);
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#getHashMethod()
+ */
+ public CharSequence getHashMethod() {
+ String a = getValue("a").toString();
+ int pHyphen = a.indexOf('-');
+ // TODO x-sig-a-tag-h = ALPHA *(ALPHA / DIGIT)
+ if (pHyphen == -1) throw new IllegalStateException("Invalid
hash method: "+a);
+ return a.subSequence(pHyphen+1, a.length());
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#getHashAlgo()
+ */
+ public CharSequence getHashAlgo() {
+ String a = getValue("a").toString();
+ int pHyphen = a.indexOf('-');
+ if (pHyphen == -1) throw new IllegalStateException("Invalid
hash method: "+a);
+ if (a.length() > pHyphen+3 && a.charAt(pHyphen+1) == 's' &&
a.charAt(pHyphen+2) == 'h' && a.charAt(pHyphen+3) == 'a') {
+ return "sha-"+a.subSequence(pHyphen+4, a.length());
+ } else return a.subSequence(pHyphen+1, a.length());
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#getSelector()
+ */
+ public CharSequence getSelector() {
+ return getValue("s");
+ }
+
+ /**
+ * @see org.apache.james.jdkim.SignatureRecord#getDToken()
+ */
+ public CharSequence getDToken() {
+ return getValue("d");
+ }
+
+ public byte[] getBodyHash() {
+ try {
+ return
Base64.decode(getValue("bh").toString().getBytes());
+ } catch (Base64DecodingException e) {
+ // TODO not the best thing
+ throw new IllegalStateException("Base64.decode.failed",
e);
+ }
+ }
+
+ public byte[] getSignature() {
+ try {
+ return
Base64.decode(getValue("b").toString().getBytes());
+ } catch (Base64DecodingException e) {
+ // TODO not the best thing
+ throw new IllegalStateException("Base64.decode.failed",
e);
+ }
+ }
+
+ public int getBodyHashLimit() {
+ String limit = getValue("l").toString();
+ if (ALL.equals(limit)) return -1;
+ else return Integer.parseInt(limit);
+ }
+
+ public String getBodyCanonicalisationMethod() {
+ String c = getValue("c").toString();
+ int pSlash = c.toString().indexOf("/");
+ if (pSlash != -1) {
+ return c.substring(pSlash+1);
+ } else {
+ return "simple";
+ }
+ }
+
+ public String getHeaderCanonicalisationMethod() {
+ String c = getValue("c").toString();
+ int pSlash = c.toString().indexOf("/");
+ if (pSlash != -1) {
+ return c.substring(0, pSlash);
+ } else {
+ return c;
+ }
+ }
+
+ public List getRecordLookupMethods() {
+ String flags = getValue("q").toString();
+ String[] flagsStrings = flags.split(":");
+ List res = new LinkedList();
+ for (int i = 0; i < flagsStrings.length; i++) {
+ // TODO add validation method[/option]
+ // if (VALIDATION)
+ res.add(trimFWS(flagsStrings[i], 0,
flagsStrings[i].length()-1, true).toString());
+ }
+ return res;
+ }
+
+}
Propchange:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java?rev=822372&view=auto
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
(added)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
Tue Oct 6 17:45:01 2009
@@ -0,0 +1,225 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jdkim.tagvalue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * This class handle a tag=value list string as defined by DKIM specification
+ * It also supports mandatoryTags and default values as a commodity to
subclasses.
+ */
+public class TagValue {
+
+ private static final boolean DEBUG = false;
+ protected static final boolean VALIDATION = true;
+
+ private static Pattern tagPattern =
Pattern.compile("^[A-Za-z][A-Za-z0-9_]*$");
+ private static final String tval = "[^; \t\r\n]+";
+ // validate value chars
+ private static Pattern valuePattern =
Pattern.compile("^("+tval+"((\r\n[\t ]|[\t ])+"+tval+")*)?$");
+
+ // we may use a TreeMap because we may need to know original order.
+ protected Map/* String, CharSequence */ tagValues;
+
+ protected Set/* String */ mandatoryTags = new HashSet();
+ protected Map/* String, CharSequence */ defaults = new HashMap();
+
+
+ protected CharSequence trimFWS(CharSequence data, int tStart, int
tStop, boolean trimWSP) {
+ if (DEBUG)
System.out.println("1["+data+"]"+tStart+"|"+tStop+"="+data.subSequence(tStart,
tStop+1)+"]");
+ // rimozione di FWS a inizio selezione
+ while (tStart < tStop && (data.charAt(tStart) == ' ' ||
data.charAt(tStart) == '\t') ||
+ (tStart < tStop - 2 && data.charAt(tStart) ==
'\r' && data.charAt(tStart+1) == '\n' && (data.charAt(tStart+2) == ' ' ||
data.charAt(tStart+2) == '\t'))) {
+ if (data.charAt(tStart) == '\r') tStart += 3;
+ else tStart++;
+ }
+
+ if (DEBUG)
System.out.println("2["+data+"]"+tStart+"|"+tStop+"="+data.subSequence(tStart,
tStop+1)+"]");
+ // rimozione di FWS a fine selezione.
+ while (tStart < tStop && (data.charAt(tStop) == ' ' ||
data.charAt(tStop) == '\t')) {
+ tStop--;
+ if ((tStart <= tStop-1 && data.charAt(tStop) == '\n' &&
data.charAt(tStop-1) == '\r') || (tStart < tStop && (data.charAt(tStop) == ' '
|| data.charAt(tStop) == '\t'))) {
+ if (data.charAt(tStop) == '\n') tStop -= 2;
+ else tStop--;
+ }
+ }
+
+ if (DEBUG)
System.out.println("3["+data+"]"+tStart+"|"+tStop+"="+data.subSequence(tStart,
tStop+1)+"]");
+ if (trimWSP) {
+ return trimWSP(data, tStart, tStop);
+ } else {
+ return data.subSequence(tStart, tStop+1);
+ }
+ }
+
+ private CharSequence trimWSP(CharSequence data, int vStart, int vStop) {
+ if (vStop < vStart-1) throw new IllegalArgumentException("Stop
must be >= than start");
+ while (vStart <= vStop && (data.charAt(vStart) == ' ' ||
data.charAt(vStart) == '\t')) vStart++;
+ while (vStart <= vStop && (data.charAt(vStop) == ' ' ||
data.charAt(vStop) == '\t')) vStop--;
+ return data.subSequence(vStart, vStop+1);
+ }
+
+ public TagValue(String data) {
+ init();
+ parse(data);
+ }
+
+ protected void init() {
+ // extensions may override this to use TreeMaps in order to
keep track of orders
+ tagValues = new HashMap();
+ }
+
+ /**
+ * subclasses have to make sure tagValues is initialized during init().
+ * @param data the string to be parsed
+ */
+ protected void parse(String data) {
+ for (int i = 0; i < data.length(); i++) {
+ int equal = data.indexOf('=', i);
+ if (equal == -1) {
+ // TODO check whether this is correct or not
+ // this allow FWS/WSP after the final ";"
+ String rest = data.substring(i);
+ if (rest.length() > 0 && trimFWS(rest, 0,
rest.length()-1, true).length() > 0) {
+ throw new
IllegalStateException("Unexpected termination at position "+i+": "+data);
+ }
+ i = data.length();
+ continue;
+ }
+ // we could start from "equals" but we start from "i"
in
+ // order to spot invalid values before validation.
+ int next = data.indexOf(';', i);
+ if (next == -1) {
+ next = data.length();
+ }
+
+ if (equal > next) {
+ throw new IllegalStateException("Found ';'
before '=' in "+data);
+ }
+
+ CharSequence tag = trimFWS(data, i, equal-1,
true).toString();
+ if (VALIDATION && !tagPattern.matcher(tag).matches()) {
+ throw new IllegalStateException("Syntax error
in tag: "+tag);
+ }
+ String tagString = tag.toString();
+ if (tagValues.containsKey(tagString)) {
+ throw new IllegalStateException("Syntax error
(duplicate tag): "+tag);
+ }
+
+ CharSequence value = trimFWS(data, equal+1, next-1,
true);
+ if (VALIDATION &&
!valuePattern.matcher(value).matches()) {
+ throw new IllegalStateException("Syntax error
in value: "+value);
+ }
+
+ tagValues.put(tagString, value);
+ i = next;
+ }
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((tagValues == null) ? 0 :
tagValues.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TagValue other = (TagValue) obj;
+ if (tagValues == null) {
+ if (other.tagValues != null)
+ return false;
+ } else if (!tagValues.equals(other.tagValues))
+ return false;
+ return true;
+ }
+
+ public Set getTags() {
+ return tagValues.keySet();
+ }
+
+ protected CharSequence getValue(String key) {
+ CharSequence val = (CharSequence) tagValues.get(key);
+ if (val == null) return getDefault(key);
+ else return val;
+ }
+
+ protected CharSequence getDefault(String key) {
+ return (CharSequence) defaults.get(key);
+ }
+
+ public void validate() {
+ // check mandatory fields
+ for (Iterator i = mandatoryTags.iterator(); i.hasNext(); ) {
+ String tag = (String) i.next();
+ if (getValue(tag)==null) throw new
IllegalStateException("Missing mandatory tag: "+tag);
+ }
+ }
+
+ protected List stringToColonSeparatedList(String h, Pattern pattern) {
+ List headers = new ArrayList();
+ for (int i = 0; i < h.length(); i++) {
+ int p = h.indexOf(':', i);
+ if (p == -1) p = h.length();
+ CharSequence cs = trimFWS(h, i, p-1, false);
+ if (VALIDATION) {
+ if (!pattern.matcher(cs).matches()) throw new
IllegalStateException("Syntax error in field name: "+cs);
+ }
+ headers.add(cs);
+ i = p;
+ }
+ return headers;
+ }
+
+ protected boolean isInListCaseInsensitive(CharSequence hash, List
hashes) {
+ for (Iterator i = hashes.iterator(); i.hasNext(); ) {
+ CharSequence suppHash = (CharSequence) i.next();
+ if
(hash.toString().equalsIgnoreCase(suppHash.toString())) return true;
+ }
+ return false;
+ }
+
+ public String toString() {
+ StringBuffer res = new StringBuffer();
+ Set s = getTags();
+ for (Iterator i = s.iterator(); i.hasNext(); ) {
+ String tag = (String) i.next();
+ res.append(tag);
+ res.append("=");
+ res.append(getValue(tag));
+ res.append("; ");
+ }
+ return res.toString();
+ }
+
+}
Propchange:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]