001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.tomcat.util.http;
018:
019: import org.apache.tomcat.util.buf.MessageBytes;
020: import org.apache.tomcat.util.buf.DateTool;
021: import java.text.*;
022: import java.io.*;
023: import java.util.*;
024:
025: /**
026: * Server-side cookie representation.
027: * Allows recycling and uses MessageBytes as low-level
028: * representation ( and thus the byte-> char conversion can be delayed
029: * until we know the charset ).
030: *
031: * Tomcat.core uses this recyclable object to represent cookies,
032: * and the facade will convert it to the external representation.
033: */
034: public class ServerCookie implements Serializable {
035: private MessageBytes name = MessageBytes.newInstance();
036: private MessageBytes value = MessageBytes.newInstance();
037:
038: private MessageBytes comment = MessageBytes.newInstance(); // ;Comment=VALUE
039: private MessageBytes domain = MessageBytes.newInstance(); // ;Domain=VALUE ...
040:
041: private int maxAge = -1; // ;Max-Age=VALUE
042: // ;Discard ... implied by maxAge < 0
043: // RFC2109: maxAge=0 will end a session
044: private MessageBytes path = MessageBytes.newInstance(); // ;Path=VALUE .
045: private boolean secure; // ;Secure
046: private int version = 0; // ;Version=1
047:
048: //XXX CommentURL, Port -> use notes ?
049:
050: public ServerCookie() {
051:
052: }
053:
054: public void recycle() {
055: path.recycle();
056: name.recycle();
057: value.recycle();
058: comment.recycle();
059: maxAge = -1;
060: path.recycle();
061: domain.recycle();
062: version = 0;
063: secure = false;
064: }
065:
066: public MessageBytes getComment() {
067: return comment;
068: }
069:
070: public MessageBytes getDomain() {
071: return domain;
072: }
073:
074: public void setMaxAge(int expiry) {
075: maxAge = expiry;
076: }
077:
078: public int getMaxAge() {
079: return maxAge;
080: }
081:
082: public MessageBytes getPath() {
083: return path;
084: }
085:
086: public void setSecure(boolean flag) {
087: secure = flag;
088: }
089:
090: public boolean getSecure() {
091: return secure;
092: }
093:
094: public MessageBytes getName() {
095: return name;
096: }
097:
098: public MessageBytes getValue() {
099: return value;
100: }
101:
102: public int getVersion() {
103: return version;
104: }
105:
106: public void setVersion(int v) {
107: version = v;
108: }
109:
110: // -------------------- utils --------------------
111:
112: public String toString() {
113: return "Cookie " + getName() + "=" + getValue() + " ; "
114: + getVersion() + " " + getPath() + " " + getDomain();
115: }
116:
117: // Note -- disabled for now to allow full Netscape compatibility
118: // from RFC 2068, token special case characters
119: //
120: // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
121: private static final String tspecials = ",;";
122:
123: /*
124: * Tests a string and returns true if the string counts as a
125: * reserved token in the Java language.
126: *
127: * @param value the <code>String</code> to be tested
128: *
129: * @return <code>true</code> if the <code>String</code> is
130: * a reserved token; <code>false</code>
131: * if it is not
132: */
133: public static boolean isToken(String value) {
134: if (value == null)
135: return true;
136: int len = value.length();
137:
138: for (int i = 0; i < len; i++) {
139: char c = value.charAt(i);
140:
141: if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1)
142: return false;
143: }
144: return true;
145: }
146:
147: public static boolean checkName(String name) {
148: if (!isToken(name)
149: || name.equalsIgnoreCase("Comment") // rfc2019
150: || name.equalsIgnoreCase("Discard") // 2019++
151: || name.equalsIgnoreCase("Domain")
152: || name.equalsIgnoreCase("Expires") // (old cookies)
153: || name.equalsIgnoreCase("Max-Age") // rfc2019
154: || name.equalsIgnoreCase("Path")
155: || name.equalsIgnoreCase("Secure")
156: || name.equalsIgnoreCase("Version")) {
157: return false;
158: }
159: return true;
160: }
161:
162: // -------------------- Cookie parsing tools
163:
164: /** Return the header name to set the cookie, based on cookie
165: * version
166: */
167: public String getCookieHeaderName() {
168: return getCookieHeaderName(version);
169: }
170:
171: /** Return the header name to set the cookie, based on cookie
172: * version
173: */
174: public static String getCookieHeaderName(int version) {
175: if (dbg > 0)
176: log((version == 1) ? "Set-Cookie2" : "Set-Cookie");
177: if (version == 1) {
178: // RFC2109
179: return "Set-Cookie";
180: // XXX RFC2965 is not standard yet, and Set-Cookie2
181: // is not supported by Netscape 4, 6, IE 3, 5 .
182: // It is supported by Lynx, and there is hope
183: // return "Set-Cookie2";
184: } else {
185: // Old Netscape
186: return "Set-Cookie";
187: }
188: }
189:
190: private static final String ancientDate = DateTool
191: .formatOldCookie(new Date(10000));
192:
193: public static void appendCookieValue(StringBuffer buf, int version,
194: String name, String value, String path, String domain,
195: String comment, int maxAge, boolean isSecure) {
196: // this part is the same for all cookies
197: buf.append(name);
198: buf.append("=");
199: maybeQuote(version, buf, value);
200:
201: // XXX Netscape cookie: "; "
202: // add version 1 specific information
203: if (version == 1) {
204: // Version=1 ... required
205: buf.append("; Version=1");
206:
207: // Comment=comment
208: if (comment != null) {
209: buf.append("; Comment=");
210: maybeQuote(version, buf, comment);
211: }
212: }
213:
214: // add domain information, if present
215:
216: if (domain != null) {
217: buf.append("; Domain=");
218: maybeQuote(version, buf, domain);
219: }
220:
221: // Max-Age=secs/Discard ... or use old "Expires" format
222: if (maxAge >= 0) {
223: if (version == 0) {
224: // XXX XXX XXX We need to send both, for
225: // interoperatibility (long word )
226: buf.append("; Expires=");
227: // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires netscape format )
228: // To expire we need to set the time back in future
229: // ( pfrieden@dChain.com )
230: if (maxAge == 0)
231: buf.append(ancientDate);
232: else
233: DateTool.formatOldCookie(new Date(System
234: .currentTimeMillis()
235: + maxAge * 1000L), buf,
236: new FieldPosition(0));
237:
238: } else {
239: buf.append("; Max-Age=");
240: buf.append(maxAge);
241: }
242: }
243:
244: // Path=path
245: if (path != null) {
246: buf.append("; Path=");
247: maybeQuote(version, buf, path);
248: }
249:
250: // Secure
251: if (isSecure) {
252: buf.append("; Secure");
253: }
254:
255: }
256:
257: public static void maybeQuote(int version, StringBuffer buf,
258: String value) {
259: // special case - a \n or \r shouldn't happen in any case
260: if (isToken(value))
261: buf.append(value);
262: else {
263: if (version == 0)
264: throw new IllegalArgumentException(value);
265: else {
266: buf.append('"');
267: buf.append(value);
268: buf.append('"');
269: }
270: }
271: }
272:
273: // log
274: static final int dbg = 1;
275:
276: public static void log(String s) {
277: System.out.println("ServerCookie: " + s);
278: }
279:
280: }
|