001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: HttpCookieParser.java,v 1.2 2006-06-15 13:47:00 sinisa Exp $
022: */
023:
024: package com.lutris.http;
025:
026: import java.text.FieldPosition;
027: import java.text.ParsePosition;
028: import java.text.SimpleDateFormat;
029: import java.util.Date;
030: import java.util.Locale;
031: import java.util.StringTokenizer;
032: import java.util.TimeZone;
033: import java.util.Vector;
034:
035: import javax.servlet.http.Cookie;
036:
037: /**
038: * This class takes a cookie header and converts it into a
039: * set of http cookies. It can be used to parse request and
040: * response cookie headers as well as format response cookies.
041: *
042: * @author Kyle Clark
043: * @version $Revision: 1.2 $
044: * @since Harmony1.0
045: */
046: public class HttpCookieParser {
047:
048: private static final String EXPIRES = "expires";
049: private static final String MAXAGE = "max-age";
050: private static final String PATH = "path";
051: private static final String DOMAIN = "domain";
052: private static final String SECURE = "secure";
053:
054: /**
055: * Object for formatting dates.
056: */
057: private static final SimpleDateFormat dateFormatter = new SimpleDateFormat(
058: "EEEE, dd-MMM-yyyy HH:mm:ss z", Locale.US);
059:
060: /*
061: * Class constructor.
062: */
063: static {
064: dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
065: }
066:
067: /**
068: * Parses an http cookie request header and returns a vector
069: * of Cookie objects defined in the header. This method
070: * should only be used with the <b>request</b> cookie header "Cookie:".
071: *
072: * @param httpReqCookieHeader
073: * The http cookie request header to parse.
074: * @return
075: * vector containing the cookies defined in the header.
076: */
077: public static Vector parseRequestHeader(String httpReqCookieHeader) {
078: Cookie cookie;
079: Vector result = new Vector();
080:
081: // Cookie are separated by ';'
082: StringTokenizer cookieTokens = new StringTokenizer(
083: httpReqCookieHeader.trim(), ";");
084: while (cookieTokens.hasMoreTokens()) {
085: // Name is separated from value by '='
086: StringTokenizer t = new StringTokenizer(cookieTokens
087: .nextToken(), "=");
088: String name = t.nextToken().trim();
089: if (t.hasMoreTokens()) {
090: // Name has a value
091: String value = t.nextToken().trim();
092: result.addElement(new Cookie(name, value));
093: }
094: }
095: return result;
096: }
097:
098: /**
099: * Parses an http cookie request header and returns the first
100: * cookie that matches the specified name. This method
101: * should only be used with the <b>request</b> cookie header "Cookie:".
102: *
103: * @param httpReqCookieHeader
104: * The http cookie request header to parse.
105: * @param name
106: * The cookie name of interest.
107: * @return
108: * The first cookie that matches. null if no matching cookies
109: * are found.
110: */
111: public static Cookie parseRequestHeader(String httpReqCookieHeader,
112: String name) {
113: // Cookie are separated by ';'
114: StringTokenizer cookieTokens = new StringTokenizer(
115: httpReqCookieHeader.trim(), ";");
116: while (cookieTokens.hasMoreTokens()) {
117: // Name is separated from value by '='
118: StringTokenizer t = new StringTokenizer(cookieTokens
119: .nextToken(), "=");
120: String tokenName = t.nextToken().trim();
121: if (t.hasMoreTokens() && tokenName.equals(name)) {
122: // if name has a value
123: String value = t.nextToken().trim();
124: return new Cookie(name, value);
125: }
126: }
127: return null;
128: }
129:
130: /**
131: * Convert an old cookie expires data to maximum age; assuming now
132: * as the start.
133: *
134: * @param dateStr The string expiry date.
135: * @return The max age, in seconds.
136: */
137: public static int expiresToMaxAge(String dateStr) {
138: Date date = dateFormatter.parse(dateStr.trim(),
139: new ParsePosition(0));
140: Date current = new Date();
141: return (int) (current.getTime() - date.getTime()) * 1000;
142: }
143:
144: /**
145: * Convert amaximum age to a old cookie expires data; assuming now
146: * as the start.
147: *
148: * @param The max age, in seconds.
149: * @return The string expiry date.
150: */
151: public static String maxAgeToExpires(int maxAge) {
152: long maxInMillis = (long) maxAge * 1000;
153: Date date = new Date(new Date().getTime() + maxInMillis);
154: StringBuffer dateStr = new StringBuffer();
155: dateFormatter.format(date, dateStr, new FieldPosition(0));
156: return dateStr.toString();
157: }
158:
159: /**
160: * Parses an http cookie response header and returns a vector
161: * of Cookie objects defined in the header. This method
162: * only works with http <b>response</b> headers, (i.e. Set-Cookie).
163: *
164: * @param httpResponseCookieHeader
165: * The http cookie response header from which the cookies
166: * are constructed.
167: * @return
168: * Vector containing the cookies defined in the header.
169: */
170: public static Vector parseResponseHeader(
171: String httpResponseCookieHeader) {
172: Vector result = new Vector();
173: httpResponseCookieHeader = httpResponseCookieHeader.trim();
174: // Cookies are separated by ','
175: StringTokenizer cookieTokens = new StringTokenizer(
176: httpResponseCookieHeader, ",");
177: while (cookieTokens.hasMoreTokens()) {
178: // Cookie fields are separated by ';'
179: StringTokenizer tokens = new StringTokenizer(cookieTokens
180: .nextToken(), ";");
181: Cookie cookie = null;
182: while (tokens.hasMoreTokens()) {
183: // field name is separated from value by '='
184: StringTokenizer t = new StringTokenizer(tokens
185: .nextToken(), "=");
186: String name = t.nextToken().trim();
187: if (t.hasMoreTokens()) {
188: String value = t.nextToken().trim();
189: if (cookie != null) {
190: if (name.equalsIgnoreCase(EXPIRES)) {
191: cookie.setMaxAge(expiresToMaxAge(value));
192: } else if (name.equalsIgnoreCase(DOMAIN)) {
193: cookie.setDomain(value);
194: } else if (name.equalsIgnoreCase(PATH)) {
195: cookie.setPath(value);
196: }
197: } else { // New cookie
198: cookie = new Cookie(name, value);
199: result.addElement(cookie);
200: }
201: } else if ((cookie != null)
202: && name.equalsIgnoreCase(SECURE)) {
203: cookie.setSecure(true);
204: }
205: }
206: }
207: return result;
208: }
209:
210: public static Cookie parseCookieString(String cookieString) {
211: cookieString = cookieString.trim();
212: // Cookie fields are separated by ';'
213: StringTokenizer tokens = new StringTokenizer(cookieString, ";");
214: Cookie cookie = null;
215: while (tokens.hasMoreTokens()) {
216: // field name is separated from value by '='
217: StringTokenizer t = new StringTokenizer(tokens.nextToken(),
218: "=");
219: String name = t.nextToken().trim();
220: if (t.hasMoreTokens()) {
221: String value = t.nextToken().trim();
222: if (cookie != null) {
223: if (name.equalsIgnoreCase(EXPIRES)) {
224: cookie.setMaxAge(expiresToMaxAge(value));
225: } else if (name.equalsIgnoreCase(DOMAIN)) {
226: cookie.setDomain(value);
227: } else if (name.equalsIgnoreCase(PATH)) {
228: cookie.setPath(value);
229: } else if (name.equalsIgnoreCase(MAXAGE)) {
230: int maxAge = 0;
231: try {
232: maxAge = Integer.parseInt(value);
233: } catch (Exception e) {
234: }
235: cookie.setMaxAge(maxAge);
236: }
237: } else { // New cookie
238: cookie = new Cookie(name, value);
239: }
240: } else if ((cookie != null)
241: && name.equalsIgnoreCase(SECURE)) {
242: cookie.setSecure(true);
243: }
244: }
245: return cookie;
246: }
247:
248: /**
249: * Append a cookie keyword/value pair, with any necessary quoting of
250: * the value.
251: *
252: * @param buf String to append cookie arguments to.
253: * @param key Keyword to append.
254: * @param value Value to append, with quoting.
255: */
256: //FIXME: Turns out, old cookies didn't support quoted strings,
257: // So we need to check the version number. Right now, just don't
258: // do quoting.
259: private static void appendCookieArg(StringBuffer buf, String key,
260: String value) {
261: if (buf.length() > 0) {
262: buf.append("; ");
263: }
264: buf.append(key);
265: buf.append("=");
266: buf.append(value);
267: }
268:
269: /**
270: * Append a cookie keyword/value pair, with any necessary quoting of
271: * the value. Adjust case of keyword if its a version zero cookie.
272: * This is should not be used on the cookie name, as case is honored
273: * there.
274: *
275: * @param version Cookie version.
276: * @param buf String to append cookie arguments to.
277: * @param key Keyword to append.
278: * @param value Value to append, with quoting.
279: */
280: private static void appendCookieArg(int version, StringBuffer buf,
281: String key, String value) {
282: //FIXME: Don't think this is really needed; just put in as a possible
283: //hack for the page-reload problem.
284: if (version == 0) {
285: // Version 0 appears to like lower case keywords.
286: key = key.toLowerCase();
287: }
288: appendCookieArg(buf, key, value);
289: }
290:
291: /**
292: * Format a Cookie for a response header.
293: *
294: * @param The cookie to format.
295: * @return The value for the header (name is not included).
296: */
297: public static String formatResponseCookie(Cookie cookie) {
298: StringBuffer buf = new StringBuffer();
299: int version = cookie.getVersion();
300:
301: //FIXME: only 0.0 is supported; just got to figure out quoting.
302: if (version > 0) {
303: throw new Error(
304: "HttpCookieParser: only version 0 cookies supported right now");
305: }
306:
307: appendCookieArg(buf, cookie.getName(), cookie.getValue());
308: if ((version > 0) && (cookie.getComment() != null)) {
309: appendCookieArg(version, buf, "Comment", cookie
310: .getComment());
311: }
312: if (cookie.getDomain() != null) {
313: appendCookieArg(version, buf, "Domain", cookie.getDomain());
314: }
315: if (cookie.getMaxAge() >= 0) {
316: if (version > 0) {
317: appendCookieArg(version, buf, "Max-Age", Integer
318: .toString(cookie.getMaxAge()));
319: } else {
320: appendCookieArg(version, buf, "Expires",
321: maxAgeToExpires(cookie.getMaxAge()));
322: }
323: }
324: if (cookie.getPath() != null) {
325: appendCookieArg(version, buf, "Path", cookie.getPath());
326: }
327: if (cookie.getDomain() != null) {
328: appendCookieArg(version, buf, "Domain", cookie.getDomain());
329: }
330: return buf.toString();
331: }
332: }
|