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 if (cookie.isDomainAttributeSpecified()
133 && (!cookie.getDomain().equals(host))) {
134
135
136 if (!cookie.getDomain().startsWith(".")) {
137 throw new MalformedCookieException("Domain attribute \""
138 + cookie.getDomain()
139 + "\" violates RFC 2109: domain must start with a dot");
140 }
141
142 int dotIndex = cookie.getDomain().indexOf('.', 1);
143 if (dotIndex < 0 || dotIndex == cookie.getDomain().length() - 1) {
144 throw new MalformedCookieException("Domain attribute \""
145 + cookie.getDomain()
146 + "\" violates RFC 2109: domain must contain an embedded dot");
147 }
148 host = host.toLowerCase();
149 if (!host.endsWith(cookie.getDomain())) {
150 throw new MalformedCookieException(
151 "Illegal domain attribute \"" + cookie.getDomain()
152 + "\". Domain of origin: \"" + host + "\"");
153 }
154
155 String hostWithoutDomain = host.substring(0, host.length()
156 - cookie.getDomain().length());
157 if (hostWithoutDomain.indexOf('.') != -1) {
158 throw new MalformedCookieException("Domain attribute \""
159 + cookie.getDomain()
160 + "\" violates RFC 2109: host minus domain may not contain any dots");
161 }
162 }
163 }
164
165 /***
166 * Performs domain-match as defined by the RFC2109.
167 * @param host The target host.
168 * @param domain The cookie domain attribute.
169 * @return true if the specified host matches the given domain.
170 *
171 * @since 3.0
172 */
173 public boolean domainMatch(String host, String domain) {
174 boolean match = host.equals(domain)
175 || (domain.startsWith(".") && host.endsWith(domain));
176
177 return match;
178 }
179
180 /***
181 * Return a name/value string suitable for sending in a <tt>"Cookie"</tt>
182 * header as defined in RFC 2109 for backward compatibility with cookie
183 * version 0
184 * @param name The name.
185 * @param value The value
186 * @param version The cookie version
187 * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
188 */
189
190 private String formatNameValuePair(
191 final String name, final String value, int version) {
192
193 final StringBuffer buffer = new StringBuffer();
194 if (version < 1) {
195 buffer.append(name);
196 buffer.append("=");
197 if (value != null) {
198 buffer.append(value);
199 }
200 } else {
201 buffer.append(name);
202 buffer.append("=\"");
203 if (value != null) {
204 buffer.append(value);
205 }
206 buffer.append("\"");
207 }
208 return buffer.toString();
209 }
210
211 /***
212 * Return a string suitable for sending in a <tt>"Cookie"</tt> header
213 * as defined in RFC 2109 for backward compatibility with cookie version 0
214 * @param cookie a {@link Cookie} to be formatted as string
215 * @param version The version to use.
216 * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
217 */
218 private String formatCookieAsVer(Cookie cookie, int version) {
219 LOG.trace("enter RFC2109Spec.formatCookieAsVer(Cookie)");
220 StringBuffer buf = new StringBuffer();
221 buf.append(formatNameValuePair(cookie.getName(),
222 cookie.getValue(), version));
223 if (cookie.getDomain() != null
224 && cookie.isDomainAttributeSpecified()) {
225
226 buf.append("; ");
227 buf.append(formatNameValuePair("$Domain",
228 cookie.getDomain(), version));
229 }
230 if (cookie.getPath() != null && cookie.isPathAttributeSpecified()) {
231 buf.append("; ");
232 buf.append(formatNameValuePair("$Path", cookie.getPath(), version));
233 }
234 return buf.toString();
235 }
236
237
238 /***
239 * Return a string suitable for sending in a <tt>"Cookie"</tt> header as
240 * defined in RFC 2109
241 * @param cookie a {@link Cookie} to be formatted as string
242 * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
243 */
244 public String formatCookie(Cookie cookie) {
245 LOG.trace("enter RFC2109Spec.formatCookie(Cookie)");
246 if (cookie == null) {
247 throw new IllegalArgumentException("Cookie may not be null");
248 }
249 int ver = cookie.getVersion();
250 StringBuffer buffer = new StringBuffer();
251 buffer.append(formatNameValuePair("$Version",
252 Integer.toString(ver), ver));
253 buffer.append("; ");
254 buffer.append(formatCookieAsVer(cookie, ver));
255 return buffer.toString();
256 }
257
258 /***
259 * Create a RFC 2109 compliant <tt>"Cookie"</tt> header value containing all
260 * {@link Cookie}s in <i>cookies</i> suitable for sending in a <tt>"Cookie"
261 * </tt> header
262 * @param cookies an array of {@link Cookie}s to be formatted
263 * @return a string suitable for sending in a Cookie header.
264 */
265 public String formatCookies(Cookie[] cookies) {
266 LOG.trace("enter RFC2109Spec.formatCookieHeader(Cookie[])");
267 int version = Integer.MAX_VALUE;
268
269 for (int i = 0; i < cookies.length; i++) {
270 Cookie cookie = cookies[i];
271 if (cookie.getVersion() < version) {
272 version = cookie.getVersion();
273 }
274 }
275 final StringBuffer buffer = new StringBuffer();
276 buffer.append(formatNameValuePair("$Version",
277 Integer.toString(version), version));
278 for (int i = 0; i < cookies.length; i++) {
279 buffer.append("; ");
280 buffer.append(formatCookieAsVer(cookies[i], version));
281 }
282 return buffer.toString();
283 }
284
285 }