Return-Path: Delivered-To: apmail-james-mime4j-dev-archive@minotaur.apache.org Received: (qmail 40732 invoked from network); 3 Jan 2010 21:54:10 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 3 Jan 2010 21:54:10 -0000 Received: (qmail 88753 invoked by uid 500); 3 Jan 2010 21:54:10 -0000 Delivered-To: apmail-james-mime4j-dev-archive@james.apache.org Received: (qmail 88720 invoked by uid 500); 3 Jan 2010 21:54:09 -0000 Mailing-List: contact mime4j-dev-help@james.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: mime4j-dev@james.apache.org Delivered-To: mailing list mime4j-dev@james.apache.org Received: (qmail 88710 invoked by uid 99); 3 Jan 2010 21:54:09 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 03 Jan 2010 21:54:09 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 03 Jan 2010 21:54:04 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 8167F23889EB; Sun, 3 Jan 2010 21:53:44 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r895488 - in /james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j: parser/impl/DefaultBodyDescriptor.java parser/impl/MaximalBodyDescriptor.java util/MimeUtil.java Date: Sun, 03 Jan 2010 21:53:43 -0000 To: mime4j-dev@james.apache.org From: bago@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100103215344.8167F23889EB@eris.apache.org> Author: bago Date: Sun Jan 3 21:53:42 2010 New Revision: 895488 URL: http://svn.apache.org/viewvc?rev=895488&view=rev Log: MimeUtil.getHeaderParams moved temporarily to DefaultFieldParser that was the only client of that method (this helps organizing code. but I think that the entry points for field parsing still needs improvement) (MIME4J-157) Modified: james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/DefaultBodyDescriptor.java james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/MaximalBodyDescriptor.java james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java Modified: james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/DefaultBodyDescriptor.java URL: http://svn.apache.org/viewvc/james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/DefaultBodyDescriptor.java?rev=895488&r1=895487&r2=895488&view=diff ============================================================================== --- james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/DefaultBodyDescriptor.java (original) +++ james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/DefaultBodyDescriptor.java Sun Jan 3 21:53:42 2010 @@ -126,7 +126,7 @@ private void parseContentType(String value) { contentTypeSet = true; - Map params = MimeUtil.getHeaderParams(value); + Map params = DefaultBodyDescriptor.getHeaderParams(value); String main = params.get(""); String type = null; @@ -246,4 +246,185 @@ public String getSubType() { return subType; } + + /** + *

Parses a complex field value into a map of key/value pairs. You may + * use this, for example, to parse a definition like + *

+     *   text/plain; charset=UTF-8; boundary=foobar
+     * 
+ * The above example would return a map with the keys "", "charset", + * and "boundary", and the values "text/plain", "UTF-8", and "foobar". + *

+ * Header value will be unfolded and excess white space trimmed. + *

+ * @param pValue The field value to parse. + * @return The result map; use the key "" to retrieve the first value. + */ + public static Map getHeaderParams(String pValue) { + pValue = pValue.trim(); + + Map result = new HashMap(); + + // split main value and parameters + String main; + String rest; + if (pValue.indexOf(";") == -1) { + main = pValue; + rest = null; + } else { + main = pValue.substring(0, pValue.indexOf(";")); + rest = pValue.substring(main.length() + 1); + } + + result.put("", main); + if (rest != null) { + char[] chars = rest.toCharArray(); + StringBuilder paramName = new StringBuilder(64); + StringBuilder paramValue = new StringBuilder(64); + + final byte READY_FOR_NAME = 0; + final byte IN_NAME = 1; + final byte READY_FOR_VALUE = 2; + final byte IN_VALUE = 3; + final byte IN_QUOTED_VALUE = 4; + final byte VALUE_DONE = 5; + final byte ERROR = 99; + + byte state = READY_FOR_NAME; + boolean escaped = false; + for (char c : chars) { + switch (state) { + case ERROR: + if (c == ';') + state = READY_FOR_NAME; + break; + + case READY_FOR_NAME: + if (c == '=') { + log.error("Expected header param name, got '='"); + state = ERROR; + break; + } + + paramName.setLength(0); + paramValue.setLength(0); + + state = IN_NAME; + // fall-through + + case IN_NAME: + if (c == '=') { + if (paramName.length() == 0) + state = ERROR; + else + state = READY_FOR_VALUE; + break; + } + + // not '='... just add to name + paramName.append(c); + break; + + case READY_FOR_VALUE: + boolean fallThrough = false; + switch (c) { + case ' ': + case '\t': + break; // ignore spaces, especially before '"' + + case '"': + state = IN_QUOTED_VALUE; + break; + + default: + state = IN_VALUE; + fallThrough = true; + break; + } + if (!fallThrough) + break; + + // fall-through + + case IN_VALUE: + fallThrough = false; + switch (c) { + case ';': + case ' ': + case '\t': + result.put( + paramName.toString().trim().toLowerCase(), + paramValue.toString().trim()); + state = VALUE_DONE; + fallThrough = true; + break; + default: + paramValue.append(c); + break; + } + if (!fallThrough) + break; + + case VALUE_DONE: + switch (c) { + case ';': + state = READY_FOR_NAME; + break; + + case ' ': + case '\t': + break; + + default: + state = ERROR; + break; + } + break; + + case IN_QUOTED_VALUE: + switch (c) { + case '"': + if (!escaped) { + // don't trim quoted strings; the spaces could be intentional. + result.put( + paramName.toString().trim().toLowerCase(), + paramValue.toString()); + state = VALUE_DONE; + } else { + escaped = false; + paramValue.append(c); + } + break; + + case '\\': + if (escaped) { + paramValue.append('\\'); + } + escaped = !escaped; + break; + + default: + if (escaped) { + paramValue.append('\\'); + } + escaped = false; + paramValue.append(c); + break; + } + break; + + } + } + + // done looping. check if anything is left over. + if (state == IN_VALUE) { + result.put( + paramName.toString().trim().toLowerCase(), + paramValue.toString().trim()); + } + } + + return result; + } } Modified: james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/MaximalBodyDescriptor.java URL: http://svn.apache.org/viewvc/james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/MaximalBodyDescriptor.java?rev=895488&r1=895487&r2=895488&view=diff ============================================================================== --- james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/MaximalBodyDescriptor.java (original) +++ james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/parser/impl/MaximalBodyDescriptor.java Sun Jan 3 21:53:42 2010 @@ -175,7 +175,7 @@ private void parseContentDisposition(final String value) { isContentDispositionSet = true; - contentDispositionParameters = MimeUtil.getHeaderParams(value); + contentDispositionParameters = DefaultBodyDescriptor.getHeaderParams(value); contentDispositionType = contentDispositionParameters.get(""); final String contentDispositionModificationDate Modified: james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java URL: http://svn.apache.org/viewvc/james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java?rev=895488&r1=895487&r2=895488&view=diff ============================================================================== --- james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java (original) +++ james/mime4j/branches/cycleclean/core/src/main/java/org/apache/james/mime4j/util/MimeUtil.java Sun Jan 3 21:53:42 2010 @@ -24,20 +24,14 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; import java.util.Random; import java.util.TimeZone; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - /** * A utility class, which provides some MIME related application logic. */ public final class MimeUtil { - private static final Log log = LogFactory.getLog(MimeUtil.class); /** * The quoted-printable encoding. @@ -159,193 +153,21 @@ } /** - *

Parses a complex field value into a map of key/value pairs. You may - * use this, for example, to parse a definition like - *

-     *   text/plain; charset=UTF-8; boundary=foobar
-     * 
- * The above example would return a map with the keys "", "charset", - * and "boundary", and the values "text/plain", "UTF-8", and "foobar". - *

- * Header value will be unfolded and excess white space trimmed. - *

- * @param pValue The field value to parse. - * @return The result map; use the key "" to retrieve the first value. - */ - @SuppressWarnings("fallthrough") - public static Map getHeaderParams(String pValue) { - pValue = pValue.trim(); - - Map result = new HashMap(); - - // split main value and parameters - String main; - String rest; - if (pValue.indexOf(";") == -1) { - main = pValue; - rest = null; - } else { - main = pValue.substring(0, pValue.indexOf(";")); - rest = pValue.substring(main.length() + 1); - } - - result.put("", main); - if (rest != null) { - char[] chars = rest.toCharArray(); - StringBuilder paramName = new StringBuilder(64); - StringBuilder paramValue = new StringBuilder(64); - - final byte READY_FOR_NAME = 0; - final byte IN_NAME = 1; - final byte READY_FOR_VALUE = 2; - final byte IN_VALUE = 3; - final byte IN_QUOTED_VALUE = 4; - final byte VALUE_DONE = 5; - final byte ERROR = 99; - - byte state = READY_FOR_NAME; - boolean escaped = false; - for (char c : chars) { - switch (state) { - case ERROR: - if (c == ';') - state = READY_FOR_NAME; - break; - - case READY_FOR_NAME: - if (c == '=') { - log.error("Expected header param name, got '='"); - state = ERROR; - break; - } - - paramName.setLength(0); - paramValue.setLength(0); - - state = IN_NAME; - // fall-through - - case IN_NAME: - if (c == '=') { - if (paramName.length() == 0) - state = ERROR; - else - state = READY_FOR_VALUE; - break; - } - - // not '='... just add to name - paramName.append(c); - break; - - case READY_FOR_VALUE: - boolean fallThrough = false; - switch (c) { - case ' ': - case '\t': - break; // ignore spaces, especially before '"' - - case '"': - state = IN_QUOTED_VALUE; - break; - - default: - state = IN_VALUE; - fallThrough = true; - break; - } - if (!fallThrough) - break; - - // fall-through - - case IN_VALUE: - fallThrough = false; - switch (c) { - case ';': - case ' ': - case '\t': - result.put( - paramName.toString().trim().toLowerCase(), - paramValue.toString().trim()); - state = VALUE_DONE; - fallThrough = true; - break; - default: - paramValue.append(c); - break; - } - if (!fallThrough) - break; - - case VALUE_DONE: - switch (c) { - case ';': - state = READY_FOR_NAME; - break; - - case ' ': - case '\t': - break; - - default: - state = ERROR; - break; - } - break; - - case IN_QUOTED_VALUE: - switch (c) { - case '"': - if (!escaped) { - // don't trim quoted strings; the spaces could be intentional. - result.put( - paramName.toString().trim().toLowerCase(), - paramValue.toString()); - state = VALUE_DONE; - } else { - escaped = false; - paramValue.append(c); - } - break; - - case '\\': - if (escaped) { - paramValue.append('\\'); - } - escaped = !escaped; - break; - - default: - if (escaped) { - paramValue.append('\\'); - } - escaped = false; - paramValue.append(c); - break; - } - break; - - } - } - - // done looping. check if anything is left over. - if (state == IN_VALUE) { - result.put( - paramName.toString().trim().toLowerCase(), - paramValue.toString().trim()); - } - } - - return result; - } - - /** * Creates a new unique message boundary string that can be used as boundary * parameter for the Content-Type header field of a message. * * @return a new unique message boundary string. */ + /* TODO - From rfc2045: + * Since the hyphen character ("-") may be represented as itself in the + * Quoted-Printable encoding, care must be taken, when encapsulating a + * quoted-printable encoded body inside one or more multipart entities, + * to ensure that the boundary delimiter does not appear anywhere in the + * encoded body. (A good strategy is to choose a boundary that includes + * a character sequence such as "=_" which can never appear in a + * quoted-printable body. See the definition of multipart messages in + * RFC 2046.) + */ public static String createUniqueBoundary() { StringBuilder sb = new StringBuilder(); sb.append("-=Part.");