You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2006/08/16 08:50:46 UTC

svn commit: r431846 [5/6] - in /tomcat/sandbox/tomcat-lite: ./ bin/ java/ java/org/apache/coyote/adapters/ java/org/apache/coyote/http11/ java/org/apache/coyote/servlet/mapper/ java/org/apache/coyote/servlet/util/ java/org/apache/coyote/standalone/ jav...

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/DigestAuthServlet.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/DigestAuthServlet.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/DigestAuthServlet.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/DigestAuthServlet.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,405 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.sec;
+
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+// TODO: the pass file should store H(A1) - for both simple and digest
+
+// TODO: stale=TRUE
+
+/**
+ * Implements Digest (and Basic) authentication protocols.
+ * You can force digest-only, or basic only.
+ * 
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class DigestAuthServlet extends SimpleAuthServlet {
+
+    private static Log log = LogFactory.getLog(DigestAuthServlet.class);
+    
+    // -------------------------------------------------------------- Constants
+
+    /**
+     * The MD5 helper object for this class.
+     */
+    protected static final MD5Encoder md5Encoder = new MD5Encoder();
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.DigestAuthenticator/1.0";
+
+    /**
+     * MD5 message digest provider.
+     */
+    MessageDigest md5Helper;
+    public static final String DIGEST_METHOD = "DIGEST";
+
+    /**
+     * Private key. - not so private unless you set it 
+     */
+    protected String key = "Tomcat-Lite";
+
+    
+    // ----------------------------------------------------------- Constructors
+
+    // If not set - a random one will be used.
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public void init() throws ServletException {
+        //super.init();
+        try {
+            //
+            //if (key == null) key = Long.toString(new Random().nextLong());
+            if (md5Helper == null)
+                md5Helper = MessageDigest.getInstance("MD5");
+            users.put("test", "test");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            throw new IllegalStateException();
+        }
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+        throws IOException {
+        processRequest(request,response);
+    }
+        
+    public void processRequest(HttpServletRequest request,
+                               HttpServletResponse response)
+            throws IOException {
+
+        if (realm == null)
+            realm = request.getServerName() + ":" + request.getServerPort();
+
+        // Validate any credentials already included with this request
+        String authorization = request.getHeader("authorization");
+        if (authorization != null) {
+            Principal principal = processDigestHeader(request, authorization);
+            if (principal != null) {
+                request.setAttribute(AccessFilter.A_PRINCIPAL,
+                        principal);
+                
+                // TODO: return the result, cache auth
+                return;
+            }
+        }
+
+        String domain = request.getContextPath();
+        String authHeader = getAuthenticateHeader(domain, false);
+        
+        response.setHeader("WWW-Authenticate", authHeader);
+        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+    }
+
+    public String authenticate(String authorization) {
+        
+        return "";
+    }
+    
+    
+    /** Generate the auth header
+     */
+    protected String getAuthenticateHeader(String domain,
+                                           boolean stale) {
+
+        long currentTime = System.currentTimeMillis();
+
+        // Does't use remote addr - it may change due to proxy 
+        // ( and we want to validate )
+        String opaque = Long.toString(currentTime);
+        String nOnce = opaque + ":" + key;
+
+        byte[] buffer = null;
+        synchronized (md5Helper) {
+            buffer = md5Helper.digest(nOnce.getBytes());
+        }
+        nOnce = md5Encoder.encode(buffer);
+
+        buffer = md5Helper.digest(nOnce.getBytes());
+        
+        StringBuffer authSB = new StringBuffer();
+        authSB.append("Digest realm=\"").append(realm);
+        authSB.append("\", qop=\"auth\", nonce=\"").append(nOnce);
+        authSB.append("\", domain=\"").append(domain);
+        if (stale) 
+            authSB.append("\", stale=\"true");
+        authSB.append("\", opaque=\"").append(opaque);
+        authSB.append("\"");
+        
+        return authSB.toString();
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parse the specified authorization credentials, and return the
+     * associated Principal that these credentials authenticate (if any)
+     * from the specified Realm.  If there is no such Principal, return
+     * <code>null</code>.
+     *
+     * @param request HTTP servlet request
+     * @param authorization Authorization credentials from this request
+     * @param realm Realm used to authenticate Principals
+     */
+    protected Principal processDigestHeader(HttpServletRequest request,
+                                            String authorization) {
+
+        if (authorization == null || !authorization.startsWith("Digest "))
+            return (null);
+        authorization = authorization.substring(7).trim();
+
+        // Bugzilla 37132: http://issues.apache.org/bugzilla/show_bug.cgi?id=37132
+        String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
+
+        String userName = null;
+        String realmName = null;
+        String nOnce = null;
+        String nc = null;
+        String cnonce = null;
+        String qop = null;
+        String uri = null;
+        String response = null;
+        String method = request.getMethod();
+
+        for (int i = 0; i < tokens.length; i++) {
+            String currentToken = tokens[i];
+            if (currentToken.length() == 0)
+                continue;
+            int equalSign = currentToken.indexOf('=');
+            if (equalSign < 0)
+                return null;
+            String currentTokenName =
+                currentToken.substring(0, equalSign).trim();
+            String currentTokenValue =
+                currentToken.substring(equalSign + 1).trim();
+            if ("username".equals(currentTokenName))
+                userName = removeQuotes(currentTokenValue);
+            if ("realm".equals(currentTokenName))
+                realmName = removeQuotes(currentTokenValue, true);
+            if ("nonce".equals(currentTokenName))
+                nOnce = removeQuotes(currentTokenValue);
+            if ("nc".equals(currentTokenName))
+                nc = removeQuotes(currentTokenValue);
+            if ("cnonce".equals(currentTokenName))
+                cnonce = removeQuotes(currentTokenValue);
+            if ("qop".equals(currentTokenName))
+                qop = removeQuotes(currentTokenValue);
+            if ("uri".equals(currentTokenName))
+                uri = removeQuotes(currentTokenValue);
+            if ("response".equals(currentTokenName))
+                response = removeQuotes(currentTokenValue);
+        }
+
+        if ( (userName == null) || (realmName == null) || (nOnce == null)
+             || (uri == null) || (response == null) )
+            return null;
+
+        // Second MD5 digest used to calculate the digest :
+        // MD5(Method + ":" + uri)
+        String a2 = method + ":" + uri;
+        byte[] buffer = null;
+        synchronized (md5Helper) {
+            buffer = md5Helper.digest(a2.getBytes());
+        }
+        String md5a2 = md5Encoder.encode(buffer);
+        
+        // TODO: verify nonce, expiration
+
+        return authenticate(userName, response, nOnce, nc, cnonce, qop,
+                                   realmName, md5a2);
+
+    }
+
+
+    public Principal authenticate(final String username, String clientDigest,
+                                  String nOnce, String nc, String cnonce,
+                                  String qop, String realm,
+                                  String md5a2) {
+
+        String md5a1 = getDigest(username, realm);
+        if (md5a1 == null) return null; // user not found
+
+        String serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":"
+            + cnonce + ":" + qop + ":" + md5a2;
+        
+        byte[] valueBytes = null;
+        if(getDigestEncoding() == null) {
+            valueBytes = serverDigestValue.getBytes();
+        } else {
+            try {
+                valueBytes = serverDigestValue.getBytes(getDigestEncoding());
+            } catch (UnsupportedEncodingException uee) {
+                log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
+                throw new IllegalArgumentException(uee.getMessage());
+            }
+        }
+        
+        String serverDigest = null;
+        // Bugzilla 32137
+        synchronized(md5Helper) {
+            serverDigest = md5Encoder.encode(md5Helper.digest(valueBytes));
+        }
+        
+        if (log.isDebugEnabled()) {
+            log.debug("Digest : " + clientDigest + " Username:" + username 
+                    + " ClientSigest:" + clientDigest + " nOnce:" + nOnce 
+                    + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop 
+                    + " realm:" + realm + "md5a2:" + md5a2 
+                    + " Server digest:" + serverDigest);
+        }
+        
+        if (serverDigest.equals(clientDigest)) {
+            return new Principal() {
+                public String getName() {
+                    return username;
+                }
+            };
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Removes the quotes on a string. RFC2617 states quotes are optional for
+     * all parameters except realm.
+     */
+    protected static String removeQuotes(String quotedString,
+                                         boolean quotesRequired) {
+        //support both quoted and non-quoted
+        if (quotedString.length() > 0 && quotedString.charAt(0) != '"' &&
+                !quotesRequired) {
+            return quotedString;
+        } else if (quotedString.length() > 2) {
+            return quotedString.substring(1, quotedString.length() - 1);
+        } else {
+            return new String();
+        }
+    }
+
+    /**
+     * Removes the quotes on a string.
+     */
+    protected static String removeQuotes(String quotedString) {
+        return removeQuotes(quotedString, false);
+    }
+
+    
+    public static class MD5Encoder {
+
+
+        // ----------------------------------------------------- Instance Variables
+
+
+        private static final char[] hexadecimal =
+        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+         'a', 'b', 'c', 'd', 'e', 'f'};
+
+
+        // --------------------------------------------------------- Public Methods
+
+
+        /**
+         * Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
+         *
+         * @param binaryData Array containing the digest
+         * @return Encoded MD5, or null if encoding failed
+         */
+        public String encode( byte[] binaryData ) {
+
+            if (binaryData.length != 16)
+                return null;
+
+            char[] buffer = new char[32];
+
+            for (int i=0; i<16; i++) {
+                int low = (int) (binaryData[i] & 0x0f);
+                int high = (int) ((binaryData[i] & 0xf0) >> 4);
+                buffer[i*2] = hexadecimal[high];
+                buffer[i*2 + 1] = hexadecimal[low];
+            }
+
+            return new String(buffer);
+
+        }
+    }
+
+    private String getPassword(String user) {
+        return (String)users.get(user);
+    }
+    
+    protected String getDigest(String username, String realmName) {
+        String secret = getPassword(username);
+        if (hasMessageDigest) {
+            return secret;
+        } else {
+            String digestValue = username + ":" + realmName + ":"
+                + secret;
+            
+            byte[] valueBytes = null;
+            if(getDigestEncoding() == null) {
+                valueBytes = digestValue.getBytes();
+            } else {
+                try {
+                    valueBytes = digestValue.getBytes(getDigestEncoding());
+                } catch (UnsupportedEncodingException uee) {
+                    log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
+                    throw new IllegalArgumentException(uee.getMessage());
+                }
+            }
+            
+            byte[] digest = null;
+            // Bugzilla 32137
+            synchronized(md5Helper) {
+                digest = md5Helper.digest(valueBytes);
+            }
+            return md5Encoder.encode(digest);
+        }
+    }
+
+    protected String digestEncoding = null;
+
+    public String getDigestEncoding() {
+        return digestEncoding;
+    }
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/JAASAuthServlet.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/JAASAuthServlet.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/JAASAuthServlet.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/JAASAuthServlet.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,134 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.sec;
+
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Iterator;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AccountExpiredException;
+import javax.security.auth.login.CredentialExpiredException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Authentication using JAAS
+ * 
+ * @author Costin Manolache
+ */
+public class JAASAuthServlet extends HttpServlet {
+    String appName = "Tomcat";
+
+    public Principal authenticate(HttpServletRequest req) {
+        LoginContext loginContext = null;
+        
+        // TODO: ThreadLocal for callback ?
+        try {
+            JaasCallback jaasCallback = new JaasCallback(req);
+            loginContext = new LoginContext(appName, jaasCallback);
+            
+            loginContext.login();
+            
+            Subject subject = loginContext.getSubject();
+            if (subject == null) {
+                return (null);
+            }
+            final String userName = jaasCallback.getUsername();
+            Iterator principals = subject.getPrincipals().iterator();
+            Principal principal = null;
+            while (principals.hasNext()) {
+                Principal p = (Principal) principals.next();
+                if (p.getName().equals(userName)) {
+                    principal = p;
+                }
+            }
+            if (principal == null) {
+                principal = new Principal() {
+                    public String getName() {
+                        return userName;
+                    }
+                };
+            }
+            return (principal);
+        } catch (AccountExpiredException e) {
+            return (null);
+        } catch (CredentialExpiredException e) {
+            return (null);
+        } catch (FailedLoginException e) {
+            return (null);
+        } catch (LoginException e) {
+            return (null);
+        } catch (Throwable e) {
+            return (null);
+        }
+    }
+
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
+            throws ServletException, IOException {
+        
+    }   
+
+
+    public void init() throws ServletException {
+        super.init();
+    }
+    
+    public static class JaasCallback implements CallbackHandler {
+
+        private HttpServletRequest req;
+
+        public JaasCallback(HttpServletRequest req) {
+            this.req = req;
+        }
+
+        public String getUsername() {
+            return req.getParameter("user");
+        }
+        
+        public void handle(Callback[] callbacks) 
+                throws IOException, UnsupportedCallbackException {
+
+            for (int i = 0; i < callbacks.length; i++) {
+                if (callbacks[i] instanceof NameCallback) {
+                    ((NameCallback) callbacks[i]).setName(getUsername());
+                } else if (callbacks[i] instanceof PasswordCallback) {
+                    final char[] passwordcontents;
+                    // TODO: any digest, nonce, etc
+                    //passwordcontents = password.toCharArray();
+                    //((PasswordCallback) callbacks[i]).setPassword(passwordcontents);
+                } else {
+                    throw new UnsupportedCallbackException(callbacks[i]);
+                }
+            }
+
+        }
+    }
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/SimpleAuthServlet.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/SimpleAuthServlet.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/SimpleAuthServlet.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/sec/SimpleAuthServlet.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.sec;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+// TODO: right now only in-process auth is implemented, the protocol
+// and URLConnection calls must be defined and implemented
+
+/**
+ * Tomcat-lite uses a split model for authentication: the 'realm', i.e. 
+ * user and password management module, is no longer an interface but a 
+ * regular servlet, with a simple http API ( and an attribute-based substitute
+ * for faster response if it runs in the same VM ). The realm is also 
+ * responsible for the entire authentication protocol - generating nonce, 
+ * verify cookie or authorization headers. Other auth protocols could be
+ * implemented, and more data could be used in making the decision. 
+ * 
+ * See AccessFilter for the other side of the protocol and what informations
+ * it can pass.
+ * 
+ * The realm can run on the same tomcat instance, or can be on a separate
+ * server, dedicated to auth. This will allow more flexibility and security.
+ * 
+ * This is a simple example servlet - can be used as a base class, or replaced
+ * with something implementing the same protocol. The usernames, pass, group
+ * membership and other user options are either passed in init params or in a 
+ * properties file.
+ * 
+ * Protocol:
+ *  
+ * 
+ * 
+ * 
+ * @author Costin Manolache
+ */
+public class SimpleAuthServlet extends HttpServlet {
+    private static final String USER_PREFIX = "u.";
+    private static final String ROLE_PREFIX = "r.";
+    
+    HashMap users = new HashMap();
+    HashMap roles = new HashMap();
+    
+    boolean hasMessageDigest = false;
+    String realm = null;
+    
+    public void addUser(String name, String pass) {
+        users.put(name, pass);
+    }
+    
+    public void addRole(String user, String value) {
+        String[] userRoles = value.split(",");
+        roles.put(user, userRoles);
+    }
+    
+    public void setFilename(String fileName) {
+        
+    }
+
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
+            throws ServletException, IOException {
+        // TODO: decode the params, dispatch
+    }   
+
+    public void init() throws ServletException {
+        super.init();
+        Enumeration names = getServletConfig().getInitParameterNames();
+        while (names.hasMoreElements()) {
+            String name = (String)names.nextElement();
+            String value = getServletConfig().getInitParameter(name);
+            if (name.startsWith(USER_PREFIX)) {
+                addUser(name.substring(USER_PREFIX.length()), value);
+            }
+            if (name.startsWith(ROLE_PREFIX)) {
+                addRole(name.substring(ROLE_PREFIX.length()), value);
+            }
+        }
+    }
+
+    public void checkAuth(String method, String cookie) {
+        
+    }
+
+
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/CopyUtils.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/CopyUtils.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/CopyUtils.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/CopyUtils.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,257 @@
+package org.apache.tomcat.servlets.util;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+
+import javax.servlet.ServletOutputStream;
+
+public class CopyUtils {
+    protected static int input = 2048;
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The resource info
+     * @param writer The writer to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public static void copy(InputStream is, 
+                            PrintWriter writer,
+                            String fileEncoding)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = is;
+
+        Reader reader;
+        if (fileEncoding == null) {
+            reader = new InputStreamReader(resourceInputStream);
+        } else {
+            reader = new InputStreamReader(resourceInputStream,
+                                           fileEncoding);
+        }
+
+        // Copy the input stream to the output stream
+        exception = copyRange(reader, writer);
+
+        // Clean up the reader
+        try {
+            reader.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The resource information
+     * @param ostream The output stream to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public static void copy(InputStream is, OutputStream ostream)
+            throws IOException {
+
+        IOException exception = null;
+        InputStream resourceInputStream = null;
+
+        resourceInputStream = is;
+
+        InputStream istream = new BufferedInputStream
+            (resourceInputStream, input);
+
+        // Copy the input stream to the output stream
+        exception = CopyUtils.copyRange(istream, ostream);
+
+        // Clean up the input stream
+        try {
+            istream.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+    }
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param istream The input stream to read from
+     * @param ostream The output stream to write to
+     * @return Exception which occurred during processing
+     */
+    public static IOException copyRange(InputStream istream,
+                                        OutputStream ostream) {
+
+        // Copy the input stream to the output stream
+        IOException exception = null;
+        byte buffer[] = new byte[input];
+        int len = buffer.length;
+        while (true) {
+            try {
+                len = istream.read(buffer);
+                if (len == -1)
+                    break;
+                ostream.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+                break;
+            }
+        }
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param reader The reader to read from
+     * @param writer The writer to write to
+     * @return Exception which occurred during processing
+     */
+    public static IOException copyRange(Reader reader, PrintWriter writer) {
+
+        // Copy the input stream to the output stream
+        IOException exception = null;
+        char buffer[] = new char[input];
+        int len = buffer.length;
+        while (true) {
+            try {
+                len = reader.read(buffer);
+                if (len == -1)
+                    break;
+                writer.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+                break;
+            }
+        }
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param istream The input stream to read from
+     * @param ostream The output stream to write to
+     * @param start Start of the range which will be copied
+     * @param end End of the range which will be copied
+     * @return Exception which occurred during processing
+     */
+    public static IOException copyRange(InputStream istream,
+                                  ServletOutputStream ostream,
+                                  long start, long end) {
+
+        try {
+            istream.skip(start);
+        } catch (IOException e) {
+            return e;
+        }
+
+        IOException exception = null;
+        long bytesToRead = end - start + 1;
+
+        byte buffer[] = new byte[input];
+        int len = buffer.length;
+        while ( (bytesToRead > 0) && (len >= buffer.length)) {
+            try {
+                len = istream.read(buffer);
+                if (bytesToRead >= len) {
+                    ostream.write(buffer, 0, len);
+                    bytesToRead -= len;
+                } else {
+                    ostream.write(buffer, 0, (int) bytesToRead);
+                    bytesToRead = 0;
+                }
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+            }
+            if (len < buffer.length)
+                break;
+        }
+
+        return exception;
+
+    }
+
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param reader The reader to read from
+     * @param writer The writer to write to
+     * @param start Start of the range which will be copied
+     * @param end End of the range which will be copied
+     * @return Exception which occurred during processing
+     */
+    public static IOException copyRange(Reader reader, PrintWriter writer,
+                                  long start, long end) {
+
+        try {
+            reader.skip(start);
+        } catch (IOException e) {
+            return e;
+        }
+
+        IOException exception = null;
+        long bytesToRead = end - start + 1;
+
+        char buffer[] = new char[input];
+        int len = buffer.length;
+        while ( (bytesToRead > 0) && (len >= buffer.length)) {
+            try {
+                len = reader.read(buffer);
+                if (bytesToRead >= len) {
+                    writer.write(buffer, 0, len);
+                    bytesToRead -= len;
+                } else {
+                    writer.write(buffer, 0, (int) bytesToRead);
+                    bytesToRead = 0;
+                }
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+            }
+            if (len < buffer.length)
+                break;
+        }
+
+        return exception;
+
+    }
+
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/DOMWriter.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/DOMWriter.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/DOMWriter.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/DOMWriter.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,287 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.util;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A sample DOM writer. This sample program illustrates how to
+ * traverse a DOM tree in order to print a document that is parsed.
+ */
+public class DOMWriter {
+
+   /** Output qualified names */
+   private boolean qualifiedNames = true;
+
+   /** Print writer. */
+   protected PrintWriter out;
+
+   /** Canonical output. */
+   protected boolean canonical;
+
+
+   public DOMWriter(String encoding, boolean canonical)
+   throws UnsupportedEncodingException {
+      out = new PrintWriter(new OutputStreamWriter(System.out, encoding));
+      this.canonical = canonical;
+   } // <init>(String,boolean)
+
+   //
+   // Constructors
+   //
+
+   /** Default constructor. */
+   public DOMWriter(boolean canonical) throws UnsupportedEncodingException {
+      this( "UTF8", canonical);
+   }
+
+    public DOMWriter(Writer writer, boolean canonical) {
+        out = new PrintWriter(writer);
+        this.canonical = canonical;
+    }
+
+   public boolean getQualifiedNames() {
+      return this.qualifiedNames;
+   }
+
+   public void setQualifiedNames(boolean qualifiedNames) {
+      this.qualifiedNames = qualifiedNames;
+   }
+
+   /** Prints the specified node, recursively. */
+   public void print(Node node) {
+
+      // is there anything to do?
+      if ( node == null ) {
+         return;
+      }
+
+      int type = node.getNodeType();
+      switch ( type ) {
+         // print document
+         case Node.DOCUMENT_NODE: {
+               if ( !canonical ) {
+                  String Encoding = "UTF-8";
+
+                  out.println("<?xml version=\"1.0\" encoding=\""+
+                           Encoding + "\"?>");
+               }
+               print(((Document)node).getDocumentElement());
+               out.flush();
+               break;
+            }
+
+            // print element with attributes
+         case Node.ELEMENT_NODE: {
+               out.print('<');
+               if (this.qualifiedNames) { 
+                  out.print(node.getNodeName());
+               } else {
+                  out.print(node.getLocalName());
+               }
+               Attr attrs[] = sortAttributes(node.getAttributes());
+               for ( int i = 0; i < attrs.length; i++ ) {
+                  Attr attr = attrs[i];
+                  out.print(' ');
+                  if (this.qualifiedNames) {
+                     out.print(attr.getNodeName());
+                  } else {
+                     out.print(attr.getLocalName());
+                  }
+                  
+                  out.print("=\"");
+                  out.print(normalize(attr.getNodeValue()));
+                  out.print('"');
+               }
+               out.print('>');
+               NodeList children = node.getChildNodes();
+               if ( children != null ) {
+                  int len = children.getLength();
+                  for ( int i = 0; i < len; i++ ) {
+                     print(children.item(i));
+                  }
+               }
+               break;
+            }
+
+            // handle entity reference nodes
+         case Node.ENTITY_REFERENCE_NODE: {
+               if ( canonical ) {
+                  NodeList children = node.getChildNodes();
+                  if ( children != null ) {
+                     int len = children.getLength();
+                     for ( int i = 0; i < len; i++ ) {
+                        print(children.item(i));
+                     }
+                  }
+               } else {
+                  out.print('&');
+                  if (this.qualifiedNames) {
+                     out.print(node.getNodeName());
+                  } else {
+                     out.print(node.getLocalName());
+                  }
+                  out.print(';');
+               }
+               break;
+            }
+
+            // print cdata sections
+         case Node.CDATA_SECTION_NODE: {
+               if ( canonical ) {
+                  out.print(normalize(node.getNodeValue()));
+               } else {
+                  out.print("<![CDATA[");
+                  out.print(node.getNodeValue());
+                  out.print("]]>");
+               }
+               break;
+            }
+
+            // print text
+         case Node.TEXT_NODE: {
+               out.print(normalize(node.getNodeValue()));
+               break;
+            }
+
+            // print processing instruction
+         case Node.PROCESSING_INSTRUCTION_NODE: {
+               out.print("<?");
+               if (this.qualifiedNames) {
+                  out.print(node.getNodeName());
+               } else {
+                  out.print(node.getLocalName());
+               }
+               
+               String data = node.getNodeValue();
+               if ( data != null && data.length() > 0 ) {
+                  out.print(' ');
+                  out.print(data);
+               }
+               out.print("?>");
+               break;
+            }
+      }
+
+      if ( type == Node.ELEMENT_NODE ) {
+         out.print("</");
+         if (this.qualifiedNames) {
+            out.print(node.getNodeName());
+         } else {
+            out.print(node.getLocalName());
+         }
+         out.print('>');
+      }
+
+      out.flush();
+
+   } // print(Node)
+
+   /** Returns a sorted list of attributes. */
+   protected Attr[] sortAttributes(NamedNodeMap attrs) {
+
+      int len = (attrs != null) ? attrs.getLength() : 0;
+      Attr array[] = new Attr[len];
+      for ( int i = 0; i < len; i++ ) {
+         array[i] = (Attr)attrs.item(i);
+      }
+      for ( int i = 0; i < len - 1; i++ ) {
+         String name = null;
+         if (this.qualifiedNames) {
+            name  = array[i].getNodeName();
+         } else {
+            name  = array[i].getLocalName();
+         }
+         int    index = i;
+         for ( int j = i + 1; j < len; j++ ) {
+            String curName = null;
+            if (this.qualifiedNames) {
+               curName = array[j].getNodeName();
+            } else {
+               curName = array[j].getLocalName();
+            }
+            if ( curName.compareTo(name) < 0 ) {
+               name  = curName;
+               index = j;
+            }
+         }
+         if ( index != i ) {
+            Attr temp    = array[i];
+            array[i]     = array[index];
+            array[index] = temp;
+         }
+      }
+
+      return (array);
+
+   } // sortAttributes(NamedNodeMap):Attr[]
+
+
+   /** Normalizes the given string. */
+   protected String normalize(String s) {
+      StringBuffer str = new StringBuffer();
+
+      int len = (s != null) ? s.length() : 0;
+      for ( int i = 0; i < len; i++ ) {
+         char ch = s.charAt(i);
+         switch ( ch ) {
+            case '<': {
+                  str.append("&lt;");
+                  break;
+               }
+            case '>': {
+                  str.append("&gt;");
+                  break;
+               }
+            case '&': {
+                  str.append("&amp;");
+                  break;
+               }
+            case '"': {
+                  str.append("&quot;");
+                  break;
+               }
+            case '\r':
+            case '\n': {
+                  if ( canonical ) {
+                     str.append("&#");
+                     str.append(Integer.toString(ch));
+                     str.append(';');
+                     break;
+                  }
+                  // else, default append char
+               }
+            default: {
+                  str.append(ch);
+               }
+         }
+      }
+
+      return (str.toString());
+
+   } // normalize(String):String
+
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,173 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.util;
+
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+
+/**
+ * Adapter class that wraps an <code>Enumeration</code> around a Java2
+ * collection classes object <code>Iterator</code> so that existing APIs
+ * returning Enumerations can easily run on top of the new collections.
+ * Constructors are provided to easliy create such wrappers.
+ *
+ * @author Craig R. McClanahan
+ */
+public final class Enumerator implements Enumeration {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     */
+    public Enumerator(Collection collection) {
+
+        this(collection.iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Collection collection, boolean clone) {
+
+        this(collection.iterator(), clone);
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     */
+    public Enumerator(Iterator iterator) {
+
+        super();
+        this.iterator = iterator;
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Iterator iterator, boolean clone) {
+
+        super();
+        if (!clone) {
+            this.iterator = iterator;
+        } else {
+            List list = new ArrayList();
+            while (iterator.hasNext()) {
+                list.add(iterator.next());
+            }
+            this.iterator = list.iterator();   
+        }
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     */
+    public Enumerator(Map map) {
+
+        this(map.values().iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Map map, boolean clone) {
+
+        this(map.values().iterator(), clone);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Iterator</code> over which the <code>Enumeration</code>
+     * represented by this class actually operates.
+     */
+    private Iterator iterator = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Tests if this enumeration contains more elements.
+     *
+     * @return <code>true</code> if and only if this enumeration object
+     *  contains at least one more element to provide, <code>false</code>
+     *  otherwise
+     */
+    public boolean hasMoreElements() {
+
+        return (iterator.hasNext());
+
+    }
+
+
+    /**
+     * Returns the next element of this enumeration if this enumeration
+     * has at least one more element to provide.
+     *
+     * @return the next element of this enumeration
+     *
+     * @exception NoSuchElementException if no more elements exist
+     */
+    public Object nextElement() throws NoSuchElementException {
+
+        return (iterator.next());
+
+    }
+
+
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/FileCopyUtils.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/FileCopyUtils.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/FileCopyUtils.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/FileCopyUtils.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,110 @@
+package org.apache.tomcat.servlets.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+
+import javax.servlet.ServletOutputStream;
+
+
+/** Like CopyUtils, but with File as source.
+ *  
+ *  This has the potential to be optimized with JNI
+ */
+public class FileCopyUtils {
+    protected static int input = 2048;
+
+    public static void copy(File f, OutputStream ostream)
+            throws IOException {
+        CopyUtils.copy(new FileInputStream(f), ostream);
+    }
+
+
+    public static void copy(File cacheEntry, 
+                            PrintWriter writer,
+                            String fileEncoding)
+            throws IOException {
+        CopyUtils.copy(new FileInputStream(cacheEntry), writer, fileEncoding);
+    }
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The ResourceInfo object
+     * @param ostream The output stream to write to
+     * @param range Range the client wanted to retrieve
+     * @exception IOException if an input/output error occurs
+     */
+    public static void copy(File cacheEntry, ServletOutputStream ostream,
+                      Range range)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = new FileInputStream(cacheEntry);
+        InputStream istream =
+            new BufferedInputStream(resourceInputStream, input);
+        exception = CopyUtils.copyRange(istream, ostream, range.start, range.end);
+
+        // Clean up the input stream
+        try {
+            istream.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param resourceInfo The ResourceInfo object
+     * @param writer The writer to write to
+     * @param range Range the client wanted to retrieve
+     * @exception IOException if an input/output error occurs
+     */
+    public static void copy(File cacheEntry, PrintWriter writer,
+                            Range range, String fileEncoding)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = new FileInputStream(cacheEntry);
+
+        Reader reader;
+        if (fileEncoding == null) {
+            reader = new InputStreamReader(resourceInputStream);
+        } else {
+            reader = new InputStreamReader(resourceInputStream,
+                                           fileEncoding);
+        }
+
+        exception = CopyUtils.copyRange(reader, writer, range.start, range.end);
+
+        // Clean up the input stream
+        try {
+            reader.close();
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MD5Encoder.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MD5Encoder.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MD5Encoder.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MD5Encoder.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.util;
+
+
+/**
+ * Encode an MD5 digest into a String.
+ * <p>
+ * The 128 bit MD5 hash is converted into a 32 character long String.
+ * Each character of the String is the hexadecimal representation of 4 bits
+ * of the digest.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ */
+
+public final class MD5Encoder {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'a', 'b', 'c', 'd', 'e', 'f'};
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
+     *
+     * @param binaryData Array containing the digest
+     * @return Encoded MD5, or null if encoding failed
+     */
+    public String encode( byte[] binaryData ) {
+
+        if (binaryData.length != 16)
+            return null;
+
+        char[] buffer = new char[32];
+
+        for (int i=0; i<16; i++) {
+            int low = (int) (binaryData[i] & 0x0f);
+            int high = (int) ((binaryData[i] & 0xf0) >> 4);
+            buffer[i*2] = hexadecimal[high];
+            buffer[i*2 + 1] = hexadecimal[low];
+        }
+
+        return new String(buffer);
+
+    }
+
+
+}
+

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MIME2Java.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MIME2Java.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MIME2Java.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/MIME2Java.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,602 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.util;
+
+import java.util.*;
+
+/**
+ * MIME2Java is a convenience class which handles conversions between MIME charset names
+ * and Java encoding names.
+ * <p>The supported XML encodings are the intersection of XML-supported code sets and those
+ * supported in JDK 1.1.
+ * <p>MIME charset names are used on <var>xmlEncoding</var> parameters to methods such
+ * as <code>TXDocument#setEncoding</code> and <code>DTD#setEncoding</code>.
+ * <p>Java encoding names are used on <var>encoding</var> parameters to
+ * methods such as <code>TXDocument#printWithFormat</code> and <code>DTD#printExternal</code>.
+ * <P>
+ * <TABLE BORDER="0" WIDTH="100%">
+ *  <TR>
+ *      <TD WIDTH="33%">
+ *          <P ALIGN="CENTER"><B>Common Name</B>
+ *      </TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER"><B>Use this name in XML files</B>
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER"><B>Name Type</B>
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B>
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">8 bit Unicode</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">UTF-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">UTF8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 1</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 2</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 3</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 4</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Greek</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: US</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-us
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Canada</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ca
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Netherlands</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-nl
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Denmark</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-dk
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Norway</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-no
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Finland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fi
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Sweden</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-se
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Italy</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-it
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp280
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-es
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp284
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Great Britain</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-gb
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp285
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: France</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp297
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp420
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-he
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp424
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Switzerland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ch
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp500
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Roece</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-roece
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Yogoslavia</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-yu
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Iceland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-is
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp871
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Urdu</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp918
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">gb2312
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">GB2312
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">eucjis
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: iso-2022-jp</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">iso-2020-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">JIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: Shift JIS</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Shift_JIS
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">SJIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese: Big5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-kr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">iso2022kr
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *  </TR>
+ * </TABLE>
+ *
+ * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
+ */
+public class MIME2Java {
+
+    static private Hashtable s_enchash;
+    static private Hashtable s_revhash;
+
+    static {
+        s_enchash = new Hashtable();
+        //    <preferred MIME name>, <Java encoding name>
+        s_enchash.put("UTF-8", "UTF8");
+        s_enchash.put("US-ASCII",        "8859_1");    // ?
+        s_enchash.put("ISO-8859-1",      "8859_1");
+        s_enchash.put("ISO-8859-2",      "8859_2");
+        s_enchash.put("ISO-8859-3",      "8859_3");
+        s_enchash.put("ISO-8859-4",      "8859_4");
+        s_enchash.put("ISO-8859-5",      "8859_5");
+        s_enchash.put("ISO-8859-6",      "8859_6");
+        s_enchash.put("ISO-8859-7",      "8859_7");
+        s_enchash.put("ISO-8859-8",      "8859_8");
+        s_enchash.put("ISO-8859-9",      "8859_9");
+        s_enchash.put("ISO-2022-JP",     "JIS");
+        s_enchash.put("SHIFT_JIS",       "SJIS");
+        s_enchash.put("EUC-JP",          "EUCJIS");
+        s_enchash.put("GB2312",          "GB2312");
+        s_enchash.put("BIG5",            "Big5");
+        s_enchash.put("EUC-KR",          "KSC5601");
+        s_enchash.put("ISO-2022-KR",     "ISO2022KR");
+        s_enchash.put("KOI8-R",          "KOI8_R");
+
+        s_enchash.put("EBCDIC-CP-US",    "CP037");
+        s_enchash.put("EBCDIC-CP-CA",    "CP037");
+        s_enchash.put("EBCDIC-CP-NL",    "CP037");
+        s_enchash.put("EBCDIC-CP-DK",    "CP277");
+        s_enchash.put("EBCDIC-CP-NO",    "CP277");
+        s_enchash.put("EBCDIC-CP-FI",    "CP278");
+        s_enchash.put("EBCDIC-CP-SE",    "CP278");
+        s_enchash.put("EBCDIC-CP-IT",    "CP280");
+        s_enchash.put("EBCDIC-CP-ES",    "CP284");
+        s_enchash.put("EBCDIC-CP-GB",    "CP285");
+        s_enchash.put("EBCDIC-CP-FR",    "CP297");
+        s_enchash.put("EBCDIC-CP-AR1",   "CP420");
+        s_enchash.put("EBCDIC-CP-HE",    "CP424");
+        s_enchash.put("EBCDIC-CP-CH",    "CP500");
+        s_enchash.put("EBCDIC-CP-ROECE", "CP870");
+        s_enchash.put("EBCDIC-CP-YU",    "CP870");
+        s_enchash.put("EBCDIC-CP-IS",    "CP871");
+        s_enchash.put("EBCDIC-CP-AR2",   "CP918");
+
+                                                // j:CNS11643 -> EUC-TW?
+                                                // ISO-2022-CN? ISO-2022-CN-EXT?
+
+        s_revhash = new Hashtable();
+        //    <Java encoding name>, <preferred MIME name>
+        s_revhash.put("UTF8", "UTF-8");
+        //s_revhash.put("8859_1", "US-ASCII");    // ?
+        s_revhash.put("8859_1", "ISO-8859-1");
+        s_revhash.put("8859_2", "ISO-8859-2");
+        s_revhash.put("8859_3", "ISO-8859-3");
+        s_revhash.put("8859_4", "ISO-8859-4");
+        s_revhash.put("8859_5", "ISO-8859-5");
+        s_revhash.put("8859_6", "ISO-8859-6");
+        s_revhash.put("8859_7", "ISO-8859-7");
+        s_revhash.put("8859_8", "ISO-8859-8");
+        s_revhash.put("8859_9", "ISO-8859-9");
+        s_revhash.put("JIS", "ISO-2022-JP");
+        s_revhash.put("SJIS", "Shift_JIS");
+        s_revhash.put("EUCJIS", "EUC-JP");
+        s_revhash.put("GB2312", "GB2312");
+        s_revhash.put("BIG5", "Big5");
+        s_revhash.put("KSC5601", "EUC-KR");
+        s_revhash.put("ISO2022KR", "ISO-2022-KR");
+        s_revhash.put("KOI8_R", "KOI8-R");
+
+        s_revhash.put("CP037", "EBCDIC-CP-US");
+        s_revhash.put("CP037", "EBCDIC-CP-CA");
+        s_revhash.put("CP037", "EBCDIC-CP-NL");
+        s_revhash.put("CP277", "EBCDIC-CP-DK");
+        s_revhash.put("CP277", "EBCDIC-CP-NO");
+        s_revhash.put("CP278", "EBCDIC-CP-FI");
+        s_revhash.put("CP278", "EBCDIC-CP-SE");
+        s_revhash.put("CP280", "EBCDIC-CP-IT");
+        s_revhash.put("CP284", "EBCDIC-CP-ES");
+        s_revhash.put("CP285", "EBCDIC-CP-GB");
+        s_revhash.put("CP297", "EBCDIC-CP-FR");
+        s_revhash.put("CP420", "EBCDIC-CP-AR1");
+        s_revhash.put("CP424", "EBCDIC-CP-HE");
+        s_revhash.put("CP500", "EBCDIC-CP-CH");
+        s_revhash.put("CP870", "EBCDIC-CP-ROECE");
+        s_revhash.put("CP870", "EBCDIC-CP-YU");
+        s_revhash.put("CP871", "EBCDIC-CP-IS");
+        s_revhash.put("CP918", "EBCDIC-CP-AR2");
+    }
+
+    private MIME2Java() {
+    }
+
+    /**
+     * Convert a MIME charset name, also known as an XML encoding name, to a Java encoding name.
+     * @param   mimeCharsetName Case insensitive MIME charset name: <code>UTF-8, US-ASCII, ISO-8859-1,
+     *                          ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6,
+     *                          ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-2022-JP, Shift_JIS,
+     *                          EUC-JP, GB2312, Big5, EUC-KR, ISO-2022-KR, KOI8-R,
+     *                          EBCDIC-CP-US, EBCDIC-CP-CA, EBCDIC-CP-NL, EBCDIC-CP-DK,
+     *                          EBCDIC-CP-NO, EBCDIC-CP-FI, EBCDIC-CP-SE, EBCDIC-CP-IT,
+     *                          EBCDIC-CP-ES, EBCDIC-CP-GB, EBCDIC-CP-FR, EBCDIC-CP-AR1,
+     *                          EBCDIC-CP-HE, EBCDIC-CP-CH, EBCDIC-CP-ROECE, EBCDIC-CP-YU,
+     *                          EBCDIC-CP-IS and EBCDIC-CP-AR2</code>.
+     * @return                  Java encoding name, or <var>null</var> if <var>mimeCharsetName</var>
+     *                          is unknown.
+     * @see #reverse
+     */
+    public static String convert(String mimeCharsetName) {
+        return (String)s_enchash.get(mimeCharsetName.toUpperCase());
+    }
+
+    /**
+     * Convert a Java encoding name to MIME charset name.
+     * Available values of <i>encoding</i> are "UTF8", "8859_1", "8859_2", "8859_3", "8859_4",
+     * "8859_5", "8859_6", "8859_7", "8859_8", "8859_9", "JIS", "SJIS", "EUCJIS",
+     * "GB2312", "BIG5", "KSC5601", "ISO2022KR",  "KOI8_R", "CP037", "CP277", "CP278",
+     * "CP280", "CP284", "CP285", "CP297", "CP420", "CP424", "CP500", "CP870", "CP871" and "CP918".
+     * @param   encoding    Case insensitive Java encoding name: <code>UTF8, 8859_1, 8859_2, 8859_3,
+     *                      8859_4, 8859_5, 8859_6, 8859_7, 8859_8, 8859_9, JIS, SJIS, EUCJIS,
+     *                      GB2312, BIG5, KSC5601, ISO2022KR, KOI8_R, CP037, CP277, CP278,
+     *                      CP280, CP284, CP285, CP297, CP420, CP424, CP500, CP870, CP871
+     *                      and CP918</code>.
+     * @return              MIME charset name, or <var>null</var> if <var>encoding</var> is unknown.
+     * @see #convert
+     */
+    public static String reverse(String encoding) {
+        return (String)s_revhash.get(encoding.toUpperCase());
+    }
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java?rev=431846&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java Tue Aug 15 23:50:43 2006
@@ -0,0 +1,146 @@
+/**
+ * 
+ */
+package org.apache.tomcat.servlets.util;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/** 
+ * Utils to process HTTP/1.1 ranges. Used by default servlet, could 
+ * be used by any servlet that needs to deal with ranges.
+ * 
+ * It is very good to support ranges if you have large content. In most
+ * cases supporting one range is enough - getting multiple ranges doesn't
+ * seem very common, and it's complex (multipart response).
+ * 
+ * @author Costin Manolache
+ * @author Remy Maucherat 
+ * @author - see DefaultServlet in Catalin for other contributors
+ */
+public class Range {
+
+    public long start;
+    public long end;
+    public long length;
+
+    /**
+     * Validate range.
+     */
+    public boolean validate() {
+        if (end >= length)
+            end = length - 1;
+        return ( (start >= 0) && (end >= 0) && (start <= end)
+                 && (length > 0) );
+    }
+
+    public void recycle() {
+        start = 0;
+        end = 0;
+        length = 0;
+    }
+    
+    /** Parse ranges. 
+     * 
+     * @return null if the range is invalid or can't be parsed
+     */
+    public static ArrayList parseRanges(long fileLength, 
+                                        String rangeHeader) throws IOException {
+        ArrayList result = new ArrayList();
+        StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
+
+        // Parsing the range list
+        while (commaTokenizer.hasMoreTokens()) {
+            String rangeDefinition = commaTokenizer.nextToken().trim();
+
+            Range currentRange = new Range();
+            currentRange.length = fileLength;
+
+            int dashPos = rangeDefinition.indexOf('-');
+
+            if (dashPos == -1) {
+                return null;
+            }
+
+            if (dashPos == 0) {
+                try {
+                    long offset = Long.parseLong(rangeDefinition);
+                    currentRange.start = fileLength + offset;
+                    currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    return null;
+                }
+            } else {
+
+                try {
+                    currentRange.start = Long.parseLong
+                        (rangeDefinition.substring(0, dashPos));
+                    if (dashPos < rangeDefinition.length() - 1)
+                        currentRange.end = Long.parseLong
+                            (rangeDefinition.substring
+                             (dashPos + 1, rangeDefinition.length()));
+                    else
+                        currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    return null;
+                }
+
+            }
+            if (!currentRange.validate()) {
+                return null;
+            }
+            result.add(currentRange);
+        }
+        return result;
+    }
+
+
+    /**
+     * Parse the Content-Range header. Used with PUT or in response.
+     * 
+     * @return Range
+     */
+    public static Range parseContentRange(String rangeHeader)
+            throws IOException {
+        if (rangeHeader == null)
+            return null;
+
+        // bytes is the only range unit supported
+        if (!rangeHeader.startsWith("bytes")) {
+            return null;
+        }
+
+        rangeHeader = rangeHeader.substring(6).trim();
+
+        int dashPos = rangeHeader.indexOf('-');
+        int slashPos = rangeHeader.indexOf('/');
+
+        if (dashPos == -1) {
+            return null;
+        }
+
+        if (slashPos == -1) {
+            return null;
+        }
+
+        Range range = new Range();
+
+        try {
+            range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
+            range.end =
+                Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
+            range.length = Long.parseLong
+                (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+
+        if (!range.validate()) {
+            return null;
+        }
+
+        return range;
+    }
+
+}
\ No newline at end of file



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org