Package org.apache.tomcat.util.http

Source Code of org.apache.tomcat.util.http.Parameters

/*
*  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.tomcat.util.http;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Hashtable;

import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.CharChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.buf.UDecoder;

/**
*
* @author Costin Manolache
*/
public final class Parameters {

   
    private static final org.apache.juli.logging.Log log=
        org.apache.juli.logging.LogFactory.getLog(Parameters.class );
   
    // Transition: we'll use the same Hashtable( String->String[] )
    // for the beginning. When we are sure all accesses happen through
    // this class - we can switch to MultiMap
    private Hashtable<String,String[]> paramHashStringArray =
        new Hashtable<String,String[]>();
    private boolean didQueryParameters=false;
   
    MessageBytes queryMB;

    UDecoder urlDec;
    MessageBytes decodedQuery=MessageBytes.newInstance();

    String encoding=null;
    String queryStringEncoding=null;
   
    /**
     *
     */
    public Parameters() {
    }

    public void setQuery( MessageBytes queryMB ) {
        this.queryMB=queryMB;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding( String s ) {
        encoding=s;
        if(log.isDebugEnabled()) {
            log.debug( "Set encoding to " + s );
        }
    }

    public void setQueryStringEncoding( String s ) {
        queryStringEncoding=s;
        if(log.isDebugEnabled()) {
            log.debug( "Set query string encoding to " + s );
        }
    }

    public void recycle() {
        paramHashStringArray.clear();
        didQueryParameters=false;
        encoding=null;
        decodedQuery.recycle();
    }
   
    // -------------------- Data access --------------------
    // Access to the current name/values, no side effect ( processing ).
    // You must explicitly call handleQueryParameters and the post methods.
   
    // This is the original data representation ( hash of String->String[])

    public void addParameterValues( String key, String[] newValues) {
        if ( key==null ) return;
        String values[];
        if (paramHashStringArray.containsKey(key)) {
            String oldValues[] = paramHashStringArray.get(key);
            values = new String[oldValues.length + newValues.length];
            for (int i = 0; i < oldValues.length; i++) {
                values[i] = oldValues[i];
            }
            for (int i = 0; i < newValues.length; i++) {
                values[i+ oldValues.length] = newValues[i];
            }
        } else {
            values = newValues;
        }

        paramHashStringArray.put(key, values);
    }

    public String[] getParameterValues(String name) {
        handleQueryParameters();
        // no "facade"
        String values[] = paramHashStringArray.get(name);
        return values;
    }
    public Enumeration<String> getParameterNames() {
        handleQueryParameters();
        return paramHashStringArray.keys();
    }

    // Shortcut.
    public String getParameter(String name ) {
        String[] values = getParameterValues(name);
        if (values != null) {
            if( values.length==0 ) return "";
            return values[0];
        } else {
            return null;
        }
    }
    // -------------------- Processing --------------------
    /** Process the query string into parameters
     */
    public void handleQueryParameters() {
        if( didQueryParameters ) return;

        didQueryParameters=true;

        if( queryMB==null || queryMB.isNull() )
            return;
       
        if(log.isDebugEnabled()) {
            log.debug("Decoding query " + decodedQuery + " " +
                    queryStringEncoding);
        }

        try {
            decodedQuery.duplicate( queryMB );
        } catch (IOException e) {
            // Can't happen, as decodedQuery can't overflow
            e.printStackTrace();
        }
        processParameters( decodedQuery, queryStringEncoding );
    }

    // incredibly inefficient data representation for parameters,
    // until we test the new one
    private void addParam( String key, String value ) {
        if( key==null ) return;
        String values[];
        if (paramHashStringArray.containsKey(key)) {
            String oldValues[] = paramHashStringArray.get(key);
            values = new String[oldValues.length + 1];
            for (int i = 0; i < oldValues.length; i++) {
                values[i] = oldValues[i];
            }
            values[oldValues.length] = value;
        } else {
            values = new String[1];
            values[0] = value;
        }
       
       
        paramHashStringArray.put(key, values);
    }

    public void setURLDecoder( UDecoder u ) {
        urlDec=u;
    }

    // -------------------- Parameter parsing --------------------
    // we are called from a single thread - we can do it the hard way
    // if needed
    ByteChunk tmpName=new ByteChunk();
    ByteChunk tmpValue=new ByteChunk();
    private ByteChunk origName=new ByteChunk();
    private ByteChunk origValue=new ByteChunk();
    CharChunk tmpNameC=new CharChunk(1024);
    public static final String DEFAULT_ENCODING = "ISO-8859-1";
   
    public void processParameters( byte bytes[], int start, int len ) {
        processParameters(bytes, start, len, encoding);
    }

    public void processParameters( byte bytes[], int start, int len,
                                   String enc ) {
        int end=start+len;
        int pos=start;
       
        if(log.isDebugEnabled()) {
            try {
                log.debug("Bytes: " +
                        new String(bytes, start, len, DEFAULT_ENCODING));
            } catch (UnsupportedEncodingException e) {
                // Should never happen...
                log.error("Unable to convert bytes", e);
            }
        }

        do {
            boolean noEq=false;
            int valStart=-1;
            int valEnd=-1;
           
            int nameStart=pos;
            int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
            // Workaround for a&b&c encoding
            int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
            if( (nameEnd2!=-1 ) &&
                ( nameEnd==-1 || nameEnd > nameEnd2) ) {
                nameEnd=nameEnd2;
                noEq=true;
                valStart=nameEnd;
                valEnd=nameEnd;
                if(log.isDebugEnabled()) {
                    try {
                        log.debug("no equal " + nameStart + " " + nameEnd + " " +
                                new String(bytes, nameStart, nameEnd-nameStart,
                                        DEFAULT_ENCODING) );
                    } catch (UnsupportedEncodingException e) {
                        // Should never happen...
                        log.error("Unable to convert bytes", e);
                    }
                }
            }
            if( nameEnd== -1 )
                nameEnd=end;

            if( ! noEq ) {
                valStart= (nameEnd < end) ? nameEnd+1 : end;
                valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
                if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
            }
           
            pos=valEnd+1;
           
            if( nameEnd<=nameStart ) {
                StringBuilder msg = new StringBuilder("Parameters: Invalid chunk ");
                // No name eg ...&=xx&... will trigger this
                if (valEnd >= nameStart) {
                    msg.append('\'');
                    try {
                        msg.append(new String(bytes, nameStart,
                                valEnd - nameStart, DEFAULT_ENCODING));
                    } catch (UnsupportedEncodingException e) {
                        // Should never happen...
                        log.error("Unable to convert bytes", e);
                    }
                    msg.append("' ");
                }
                msg.append("ignored.");
                log.warn(msg);
                continue;
                // invalid chunk - it's better to ignore
            }
            tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
            tmpValue.setBytes( bytes, valStart, valEnd-valStart );
           
            // Take copies as if anything goes wrong originals will be
            // corrupted. This means original values can be logged.
            // For performance - only done for debug
            if (log.isDebugEnabled()) {
                try {
                    origName.append(bytes, nameStart, nameEnd-nameStart);
                    origValue.append(bytes, valStart, valEnd-valStart);
                } catch (IOException ioe) {
                    // Should never happen...
                    log.error("Error copying parameters", ioe);
                }
            }
           
            try {
                addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
            } catch (IOException e) {
                StringBuilder msg =
                    new StringBuilder("Parameters: Character decoding failed.");
                msg.append(" Parameter '");
                if (log.isDebugEnabled()) {
                    msg.append(origName.toString());
                    msg.append("' with value '");
                    msg.append(origValue.toString());
                    msg.append("' has been ignored.");
                    log.debug(msg, e);
                } else {
                    msg.append(tmpName.toString());
                    msg.append("' with value '");
                    msg.append(tmpValue.toString());
                    msg.append("' has been ignored. Note that the name and ");
                    msg.append("value quoted here may be corrupted due to ");
                    msg.append("the failed decoding. Use debug level logging ");
                    msg.append("to see the original, non-corrupted values.");
                    log.warn(msg);
                }
            }

            tmpName.recycle();
            tmpValue.recycle();
            // Only recycle copies if we used them
            if (log.isDebugEnabled()) {
                origName.recycle();
                origValue.recycle();
            }
        } while( pos<end );
    }

    private String urlDecode(ByteChunk bc, String enc)
        throws IOException {
        if( urlDec==null ) {
            urlDec=new UDecoder();  
        }
        urlDec.convert(bc);
        String result = null;
        if (enc != null) {
            bc.setEncoding(enc);
            result = bc.toString();
        } else {
            CharChunk cc = tmpNameC;
            int length = bc.getLength();
            cc.allocate(length, -1);
            // Default encoding: fast conversion
            byte[] bbuf = bc.getBuffer();
            char[] cbuf = cc.getBuffer();
            int start = bc.getStart();
            for (int i = 0; i < length; i++) {
                cbuf[i] = (char) (bbuf[i + start] & 0xff);
            }
            cc.setChars(cbuf, 0, length);
            result = cc.toString();
            cc.recycle();
        }
        return result;
    }

    public void processParameters( MessageBytes data, String encoding ) {
        if( data==null || data.isNull() || data.getLength() <= 0 ) return;

        if( data.getType() != MessageBytes.T_BYTES ) {
            data.toBytes();
        }
        ByteChunk bc=data.getByteChunk();
        processParameters( bc.getBytes(), bc.getOffset(),
                           bc.getLength(), encoding);
    }

    /** Debug purpose
     */
    public String paramsAsString() {
        StringBuilder sb=new StringBuilder();
        Enumeration<String> en= paramHashStringArray.keys();
        while( en.hasMoreElements() ) {
            String k = en.nextElement();
            sb.append( k ).append("=");
            String v[] = paramHashStringArray.get( k );
            for( int i=0; i<v.length; i++ )
                sb.append( v[i] ).append(",");
            sb.append("\n");
        }
        return sb.toString();
    }

}
TOP

Related Classes of org.apache.tomcat.util.http.Parameters

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.