1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.apache.commons.httpclient.cookie;
33
34 import org.apache.commons.httpclient.NameValuePair;
35 import org.apache.commons.httpclient.Cookie;
36
37 /***
38 * <p>RFC 2109 specific cookie management functions
39 *
40 * @author B.C. Holmes
41 * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
42 * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
43 * @author Rod Waldhoff
44 * @author dIon Gillard
45 * @author Sean C. Sullivan
46 * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
47 * @author Marc A. Saegesser
48 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
49 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
50 *
51 * @since 2.0
52 */
53
54 public class RFC2109Spec extends CookieSpecBase {
55
56 /*** Default constructor */
57 public RFC2109Spec() {
58 super();
59 }
60
61
62 /***
63 * Parse RFC 2109 specific cookie attribute and update the corresponsing
64 * {@link Cookie} properties.
65 *
66 * @param attribute {@link NameValuePair} cookie attribute from the
67 * <tt>Set- Cookie</tt>
68 * @param cookie {@link Cookie} to be updated
69 * @throws MalformedCookieException if an exception occurs during parsing
70 */
71 public void parseAttribute(
72 final NameValuePair attribute, final Cookie cookie)
73 throws MalformedCookieException {
74
75 if (attribute == null) {
76 throw new IllegalArgumentException("Attribute may not be null.");
77 }
78 if (cookie == null) {
79 throw new IllegalArgumentException("Cookie may not be null.");
80 }
81 final String paramName = attribute.getName().toLowerCase();
82 final String paramValue = attribute.getValue();
83
84 if (paramName.equals("path")) {
85 if (paramValue == null) {
86 throw new MalformedCookieException(
87 "Missing value for path attribute");
88 }
89 if (paramValue.trim().equals("")) {
90 throw new MalformedCookieException(
91 "Blank value for path attribute");
92 }
93 cookie.setPath(paramValue);
94 cookie.setPathAttributeSpecified(true);
95 } else if (paramName.equals("version")) {
96
97 if (paramValue == null) {
98 throw new MalformedCookieException(
99 "Missing value for version attribute");
100 }
101 try {
102 cookie.setVersion(Integer.parseInt(paramValue));
103 } catch (NumberFormatException e) {
104 throw new MalformedCookieException("Invalid version: "
105 + e.getMessage());
106 }
107
108 } else {
109 super.parseAttribute(attribute, cookie);
110 }
111 }
112
113 /***
114 * Performs RFC 2109 compliant {@link Cookie} validation
115 *
116 * @param host the host from which the {@link Cookie} was received
117 * @param port the port from which the {@link Cookie} was received
118 * @param path the path from which the {@link Cookie} was received
119 * @param secure <tt>true</tt> when the {@link Cookie} was received using a
120 * secure connection
121 * @param cookie The cookie to validate
122 * @throws MalformedCookieException if an exception occurs during
123 * validation
124 */
125 public void validate(String host, int port, String path,
126 boolean secure, final Cookie cookie) throws MalformedCookieException {
127
128 LOG.trace("enter RFC2109Spec.validate(String, int, String, "
129 + "boolean, Cookie)");
130
131
132 super.validate(host, port, path, secure, cookie);
133
134
135 if (cookie.getName().indexOf(' ') != -1) {
136 throw new MalformedCookieException("Cookie name may not contain blanks");
137 }
138 if (cookie.getName().startsWith("$")) {
139 throw new MalformedCookieException("Cookie name may not start with $");
140 }
141
142 if (cookie.isDomainAttributeSpecified()
143 && (!cookie.getDomain().equals(host))) {
144
145
146 if (!cookie.getDomain().startsWith(".")) {
147 throw new MalformedCookieException("Domain attribute \""
148 + cookie.getDomain()
149 + "\" violates RFC 2109: domain must start with a dot");
150 }
151
152 int dotIndex = cookie.getDomain().indexOf('.', 1);
153 if (dotIndex < 0 || dotIndex == cookie.getDomain().length() - 1) {
154 throw new MalformedCookieException("Domain attribute \""
155 + cookie.getDomain()
156 + "\" violates RFC 2109: domain must contain an embedded dot");
157 }
158 host = host.toLowerCase();
159 if (host.indexOf('.') >= 0) {
160 if (!host.endsWith(cookie.getDomain())) {
161 throw new MalformedCookieException(
162 "Illegal domain attribute \"" + cookie.getDomain()
163 + "\". Domain of origin: \"" + host + "\"");
164 }
165
166 String hostWithoutDomain = host.substring(0, host.length()
167 - cookie.getDomain().length());
168 if (hostWithoutDomain.indexOf('.') != -1) {
169 throw new MalformedCookieException("Domain attribute \""
170 + cookie.getDomain()
171 + "\" violates RFC 2109: host minus domain may not contain any dots");
172 }
173 }
174 }
175 }
176
177
178 /***
179 * Return a name/value string suitable for sending in a <tt>"Cookie"</tt>
180 * header as defined in RFC 2109 for backward compatibility with cookie
181 * version 0
182 * @param name The name.
183 * @param value The value
184 * @param version The cookie version
185 * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
186 */
187
188 private String formatNameValuePair(
189 final String name, final String value, int version) {
190
191 final StringBuffer buffer = new StringBuffer();
192 if (version < 1) {
193 buffer.append(name);
194 buffer.append("=");
195 if (value != null) {
196 buffer.append(value);
197 }
198 } else {
199 buffer.append(name);
200 buffer.append("=\"");
201 if (value != null) {
202 buffer.append(value);
203 }
204 buffer.append("\"");
205 }
206 return buffer.toString();
207 }
208
209 /***
210 * Return a string suitable for sending in a <tt>"Cookie"</tt> header
211 * as defined in RFC 2109 for backward compatibility with cookie version 0
212 * @param cookie a {@link Cookie} to be formatted as string
213 * @param version The version to use.
214 * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
215 */
216 private String formatCookieAsVer(Cookie cookie, int version) {
217 LOG.trace("enter RFC2109Spec.formatCookieAsVer(Cookie)");
218 if (cookie == null) {
219 throw new IllegalArgumentException("Cookie may not be null");
220 }
221 StringBuffer buf = new StringBuffer();
222 buf.append(formatNameValuePair(cookie.getName(),
223 cookie.getValue(), version));
224 if (cookie.getDomain() != null
225 && cookie.isDomainAttributeSpecified()) {
226
227 buf.append("; ");
228 buf.append(formatNameValuePair("$Domain",
229 cookie.getDomain(), version));
230 }
231 if (cookie.getPath() != null && cookie.isPathAttributeSpecified()) {
232 buf.append("; ");
233 buf.append(formatNameValuePair("$Path", cookie.getPath(), version));
234 }
235 return buf.toString();
236 }
237
238
239 /***
240 * Return a string suitable for sending in a <tt>"Cookie"</tt> header as
241 * defined in RFC 2109
242 * @param cookie a {@link Cookie} to be formatted as string
243 * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
244 */
245 public String formatCookie(Cookie cookie) {
246 LOG.trace("enter RFC2109Spec.formatCookie(Cookie)");
247 if (cookie == null) {
248 throw new IllegalArgumentException("Cookie may not be null");
249 }
250 int ver = cookie.getVersion();
251 StringBuffer buffer = new StringBuffer();
252 buffer.append(formatNameValuePair("$Version",
253 Integer.toString(ver), ver));
254 buffer.append("; ");
255 buffer.append(formatCookieAsVer(cookie, ver));
256 return buffer.toString();
257 }
258
259 /***
260 * Create a RFC 2109 compliant <tt>"Cookie"</tt> header value containing all
261 * {@link Cookie}s in <i>cookies</i> suitable for sending in a <tt>"Cookie"
262 * </tt> header
263 * @param cookies an array of {@link Cookie}s to be formatted
264 * @return a string suitable for sending in a Cookie header.
265 */
266 public String formatCookies(Cookie[] cookies) {
267 LOG.trace("enter RFC2109Spec.formatCookieHeader(Cookie[])");
268 int version = Integer.MAX_VALUE;
269
270 for (int i = 0; i < cookies.length; i++) {
271 Cookie cookie = cookies[i];
272 if (cookie.getVersion() < version) {
273 version = cookie.getVersion();
274 }
275 }
276 final StringBuffer buffer = new StringBuffer();
277 buffer.append(formatNameValuePair("$Version",
278 Integer.toString(version), version));
279 for (int i = 0; i < cookies.length; i++) {
280 buffer.append("; ");
281 buffer.append(formatCookieAsVer(cookies[i], version));
282 }
283 return buffer.toString();
284 }
285 }