Java Source Code: org.apache.commons.httpclient.cookie.NetscapeDraftSpec


   1: /*
   2:  * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/cookie/NetscapeDraftSpec.java,v 1.11 2004/05/13 04:02:00 mbecke Exp $
   3:  * $Revision: 480424 $
   4:  * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
   5:  *
   6:  * ====================================================================
   7:  *
   8:  *  Licensed to the Apache Software Foundation (ASF) under one or more
   9:  *  contributor license agreements.  See the NOTICE file distributed with
  10:  *  this work for additional information regarding copyright ownership.
  11:  *  The ASF licenses this file to You under the Apache License, Version 2.0
  12:  *  (the "License"); you may not use this file except in compliance with
  13:  *  the License.  You may obtain a copy of the License at
  14:  *
  15:  *      http://www.apache.org/licenses/LICENSE-2.0
  16:  *
  17:  *  Unless required by applicable law or agreed to in writing, software
  18:  *  distributed under the License is distributed on an "AS IS" BASIS,
  19:  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20:  *  See the License for the specific language governing permissions and
  21:  *  limitations under the License.
  22:  * ====================================================================
  23:  *
  24:  * This software consists of voluntary contributions made by many
  25:  * individuals on behalf of the Apache Software Foundation.  For more
  26:  * information on the Apache Software Foundation, please see
  27:  * <http://www.apache.org/>.
  28:  *
  29:  */
  30: 
  31: package org.apache.commons.httpclient.cookie;
  32: 
  33: import java.util.StringTokenizer;
  34: import java.util.Date;
  35: import java.util.Locale;   
  36: import java.text.DateFormat; 
  37: import java.text.SimpleDateFormat;  
  38: import java.text.ParseException; 
  39: 
  40: import org.apache.commons.httpclient.HeaderElement;
  41: import org.apache.commons.httpclient.NameValuePair;
  42: import org.apache.commons.httpclient.Cookie;
  43: 
  44: /**
  45:  * <P>Netscape cookie draft specific cookie management functions
  46:  *
  47:  * @author  B.C. Holmes
  48:  * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
  49:  * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
  50:  * @author Rod Waldhoff
  51:  * @author dIon Gillard
  52:  * @author Sean C. Sullivan
  53:  * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
  54:  * @author Marc A. Saegesser
  55:  * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  56:  * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  57:  * 
  58:  * @since 2.0 
  59:  */
  60: 
  61:	  public class NetscapeDraftSpec extends CookieSpecBase {
  62:
  63:    /** Default constructor */
  64:	      public NetscapeDraftSpec() {
  65:        super();
  66:    }
  67:
  68:    /**
  69:      * Parses the Set-Cookie value into an array of <tt>Cookie</tt>s.
  70:      *
  71:      * <p>Syntax of the Set-Cookie HTTP Response Header:</p>
  72:      * 
  73:      * <p>This is the format a CGI script would use to add to 
  74:      * the HTTP headers a new piece of data which is to be stored by 
  75:      * the client for later retrieval.</p>
  76:      *  
  77:      * <PRE>
  78:      *  Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
  79:      * </PRE>
  80:      *
  81:      * <p>Please note that Netscape draft specification does not fully 
  82:      * conform to the HTTP header format. Netscape draft does not specify 
  83:      * whether multiple cookies may be sent in one header. Hence, comma 
  84:      * character may be present in unquoted cookie value or unquoted 
  85:      * parameter value.</p>
  86:      * 
  87:      * @link http://wp.netscape.com/newsref/std/cookie_spec.html
  88:      * 
  89:      * @param host the host from which the <tt>Set-Cookie</tt> value was
  90:      * received
  91:      * @param port the port from which the <tt>Set-Cookie</tt> value was
  92:      * received
  93:      * @param path the path from which the <tt>Set-Cookie</tt> value was
  94:      * received
  95:      * @param secure <tt>true</tt> when the <tt>Set-Cookie</tt> value was
  96:      * received over secure conection
  97:      * @param header the <tt>Set-Cookie</tt> received from the server
  98:      * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie value
  99:      * @throws MalformedCookieException if an exception occurs during parsing
 100:      * 
 101:      * @since 3.0
 102:      */
 103:    public Cookie[] parse(String host, int port, String path, 
 104:        boolean secure, final String header) 
 105:	          throws MalformedCookieException {
 106:            
 107:        LOG.trace("enter NetscapeDraftSpec.parse(String, port, path, boolean, Header)");
 108:
 109:	          if (host == null) {
 110:            throw new IllegalArgumentException("Host of origin may not be null");
 111:        }
 112:	          if (host.trim().equals("")) {
 113:            throw new IllegalArgumentException("Host of origin may not be blank");
 114:        }
 115:	          if (port < 0) {
 116:            throw new IllegalArgumentException("Invalid port: " + port);
 117:        }
 118:	          if (path == null) {
 119:            throw new IllegalArgumentException("Path of origin may not be null.");
 120:        }
 121:	          if (header == null) {
 122:            throw new IllegalArgumentException("Header may not be null.");
 123:        }
 124:
 125:	          if (path.trim().equals("")) {
 126:            path = PATH_DELIM;
 127:        }
 128:        host = host.toLowerCase();
 129:
 130:        String defaultPath = path;    
 131:        int lastSlashIndex = defaultPath.lastIndexOf(PATH_DELIM);
 132:	          if (lastSlashIndex >= 0) {
 133:	              if (lastSlashIndex == 0) {
 134:                //Do not remove the very first slash
 135:                lastSlashIndex = 1;
 136:            }
 137:            defaultPath = defaultPath.substring(0, lastSlashIndex);
 138:        }
 139:        HeaderElement headerelement = new HeaderElement(header.toCharArray());
 140:        Cookie cookie = new Cookie(host,
 141:                       headerelement.getName(),
 142:                       headerelement.getValue(),
 143:                       defaultPath, 
 144:                       null,
 145:                       false);
 146:        // cycle through the parameters
 147:        NameValuePair[] parameters = headerelement.getParameters();
 148:        // could be null. In case only a header element and no parameters.
 149:	          if (parameters != null) {
 150:	              for (int j = 0; j < parameters.length; j++) {
 151:                parseAttribute(parameters[j], cookie);
 152:            }
 153:        }
 154:        return new Cookie[] {cookie};
 155:    }
 156:
 157:
 158:    /**
 159:      * Parse the cookie attribute and update the corresponsing {@link Cookie}
 160:      * properties as defined by the Netscape draft specification
 161:      *
 162:      * @param attribute {@link NameValuePair} cookie attribute from the
 163:      * <tt>Set- Cookie</tt>
 164:      * @param cookie {@link Cookie} to be updated
 165:      * @throws MalformedCookieException if an exception occurs during parsing
 166:      */
 167:    public void parseAttribute(
 168:        final NameValuePair attribute, final Cookie cookie)
 169:	          throws MalformedCookieException {
 170:            
 171:	          if (attribute == null) {
 172:            throw new IllegalArgumentException("Attribute may not be null.");
 173:        }
 174:	          if (cookie == null) {
 175:            throw new IllegalArgumentException("Cookie may not be null.");
 176:        }
 177:        final String paramName = attribute.getName().toLowerCase();
 178:        final String paramValue = attribute.getValue();
 179:
 180:	          if (paramName.equals("expires")) {
 181:
 182:	              if (paramValue == null) {
 183:                throw new MalformedCookieException(
 184:                    "Missing value for expires attribute");
 185:            }
 186:	              try {
 187:                DateFormat expiryFormat = new SimpleDateFormat(
 188:                    "EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US);
 189:                Date date = expiryFormat.parse(paramValue);
 190:                cookie.setExpiryDate(date);
 191:            } catch (ParseException e) {
 192:                throw new MalformedCookieException("Invalid expires "
 193:                    + "attribute: " + e.getMessage());
 194:            }
 195:        } else {
 196:            super.parseAttribute(attribute, cookie);
 197:        }
 198:    }
 199:
 200:    /**
 201:     * Performs domain-match as described in the Netscape draft.
 202:     * @param host The target host.
 203:     * @param domain The cookie domain attribute.
 204:     * @return true if the specified host matches the given domain.
 205:     */
 206:	      public boolean domainMatch(final String host, final String domain) {
 207:        return host.endsWith(domain);
 208:    }
 209:
 210:    /**
 211:      * Performs Netscape draft compliant {@link Cookie} validation
 212:      *
 213:      * @param host the host from which the {@link Cookie} was received
 214:      * @param port the port from which the {@link Cookie} was received
 215:      * @param path the path from which the {@link Cookie} was received
 216:      * @param secure <tt>true</tt> when the {@link Cookie} was received 
 217:      * using a secure connection
 218:      * @param cookie The cookie to validate.
 219:      * @throws MalformedCookieException if an exception occurs during
 220:      * validation
 221:      */
 222:    public void validate(String host, int port, String path, 
 223:        boolean secure, final Cookie cookie) 
 224:	          throws MalformedCookieException {
 225:            
 226:        LOG.trace("enterNetscapeDraftCookieProcessor "
 227:            + "RCF2109CookieProcessor.validate(Cookie)");
 228:        // Perform generic validation
 229:        super.validate(host, port, path, secure, cookie);
 230:        // Perform Netscape Cookie draft specific validation
 231:	          if (host.indexOf(".") >= 0) {
 232:            int domainParts = new StringTokenizer(cookie.getDomain(), ".")
 233:                .countTokens();
 234:
 235:	              if (isSpecialDomain(cookie.getDomain())) {
 236:	                  if (domainParts < 2) {
 237:                    throw new MalformedCookieException("Domain attribute \""
 238:                        + cookie.getDomain() 
 239:                        + "\" violates the Netscape cookie specification for "
 240:                        + "special domains");
 241:                }
 242:            } else {
 243:	                  if (domainParts < 3) {
 244:                    throw new MalformedCookieException("Domain attribute \""
 245:                        + cookie.getDomain() 
 246:                        + "\" violates the Netscape cookie specification");
 247:                }            
 248:            }
 249:        }
 250:    }
 251:    
 252:    /**
 253:     * Checks if the given domain is in one of the seven special
 254:     * top level domains defined by the Netscape cookie specification.
 255:     * @param domain The domain.
 256:     * @return True if the specified domain is "special"
 257:     */
 258:	      private static boolean isSpecialDomain(final String domain) {
 259:        final String ucDomain = domain.toUpperCase();
 260:        if (ucDomain.endsWith(".COM") 
 261:           || ucDomain.endsWith(".EDU")
 262:           || ucDomain.endsWith(".NET")
 263:           || ucDomain.endsWith(".GOV")
 264:           || ucDomain.endsWith(".MIL")
 265:           || ucDomain.endsWith(".ORG")
 266:	             || ucDomain.endsWith(".INT")) {
 267:            return true;
 268:        }
 269:        return false;
 270:    }
 271:}