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