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