1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpec.java $
3 * $Revision: 657334 $
4 * $Date: 2008-05-17 04:44:16 -0700 (Sat, 17 May 2008) $
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements.  See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership.  The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License.  You may obtain a copy of the License at
14 *
15 *   http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied.  See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 *
25 * This software consists of voluntary contributions made by many
26 * individuals on behalf of the Apache Software Foundation.  For more
27 * information on the Apache Software Foundation, please see
28 * <http://www.apache.org/>.
29 *
30 */
31
32package org.apache.http.impl.cookie;
33
34import java.util.ArrayList;
35import java.util.List;
36import java.util.Locale;
37
38import org.apache.http.FormattedHeader;
39import org.apache.http.Header;
40import org.apache.http.HeaderElement;
41import org.apache.http.cookie.ClientCookie;
42import org.apache.http.cookie.Cookie;
43import org.apache.http.cookie.CookieOrigin;
44import org.apache.http.cookie.MalformedCookieException;
45import org.apache.http.cookie.SM;
46import org.apache.http.message.BufferedHeader;
47import org.apache.http.message.ParserCursor;
48import org.apache.http.util.CharArrayBuffer;
49
50/**
51 * Cookie specification that strives to closely mimic (mis)behavior of
52 * common web browser applications such as Microsoft Internet Explorer
53 * and Mozilla FireFox.
54 *
55 * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
56 *
57 * @since 4.0
58 */
59public class BrowserCompatSpec extends CookieSpecBase {
60
61    /** Valid date patterns used per default */
62    protected static final String[] DATE_PATTERNS = new String[] {
63            DateUtils.PATTERN_RFC1123,
64            DateUtils.PATTERN_RFC1036,
65            DateUtils.PATTERN_ASCTIME,
66            "EEE, dd-MMM-yyyy HH:mm:ss z",
67            "EEE, dd-MMM-yyyy HH-mm-ss z",
68            "EEE, dd MMM yy HH:mm:ss z",
69            "EEE dd-MMM-yyyy HH:mm:ss z",
70            "EEE dd MMM yyyy HH:mm:ss z",
71            "EEE dd-MMM-yyyy HH-mm-ss z",
72            "EEE dd-MMM-yy HH:mm:ss z",
73            "EEE dd MMM yy HH:mm:ss z",
74            "EEE,dd-MMM-yy HH:mm:ss z",
75            "EEE,dd-MMM-yyyy HH:mm:ss z",
76            "EEE, dd-MM-yyyy HH:mm:ss z",
77        };
78
79    private final String[] datepatterns;
80
81    /** Default constructor */
82    public BrowserCompatSpec(final String[] datepatterns) {
83        super();
84        if (datepatterns != null) {
85            this.datepatterns = datepatterns.clone();
86        } else {
87            this.datepatterns = DATE_PATTERNS;
88        }
89        registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler());
90        registerAttribHandler(ClientCookie.DOMAIN_ATTR, new BasicDomainHandler());
91        registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler());
92        registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler());
93        registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler());
94        registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler(
95                this.datepatterns));
96    }
97
98    /** Default constructor */
99    public BrowserCompatSpec() {
100        this(null);
101    }
102
103    public List<Cookie> parse(final Header header, final CookieOrigin origin)
104            throws MalformedCookieException {
105        if (header == null) {
106            throw new IllegalArgumentException("Header may not be null");
107        }
108        if (origin == null) {
109            throw new IllegalArgumentException("Cookie origin may not be null");
110        }
111        String headervalue = header.getValue();
112        boolean isNetscapeCookie = false;
113        int i1 = headervalue.toLowerCase(Locale.ENGLISH).indexOf("expires=");
114        if (i1 != -1) {
115            i1 += "expires=".length();
116            int i2 = headervalue.indexOf(';', i1);
117            if (i2 == -1) {
118                i2 = headervalue.length();
119            }
120            try {
121                DateUtils.parseDate(headervalue.substring(i1, i2), this.datepatterns);
122                isNetscapeCookie = true;
123            } catch (DateParseException e) {
124                // Does not look like a valid expiry date
125            }
126        }
127        HeaderElement[] elems = null;
128        if (isNetscapeCookie) {
129            NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT;
130            CharArrayBuffer buffer;
131            ParserCursor cursor;
132            if (header instanceof FormattedHeader) {
133                buffer = ((FormattedHeader) header).getBuffer();
134                cursor = new ParserCursor(
135                        ((FormattedHeader) header).getValuePos(),
136                        buffer.length());
137            } else {
138                String s = header.getValue();
139                if (s == null) {
140                    throw new MalformedCookieException("Header value is null");
141                }
142                buffer = new CharArrayBuffer(s.length());
143                buffer.append(s);
144                cursor = new ParserCursor(0, buffer.length());
145            }
146            elems = new HeaderElement[] { parser.parseHeader(buffer, cursor) };
147        } else {
148            elems = header.getElements();
149        }
150        return parse(elems, origin);
151    }
152
153    public List<Header> formatCookies(final List<Cookie> cookies) {
154        if (cookies == null) {
155            throw new IllegalArgumentException("List of cookies may not be null");
156        }
157        if (cookies.isEmpty()) {
158            throw new IllegalArgumentException("List of cookies may not be empty");
159        }
160        CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size());
161        buffer.append(SM.COOKIE);
162        buffer.append(": ");
163        for (int i = 0; i < cookies.size(); i++) {
164            Cookie cookie = cookies.get(i);
165            if (i > 0) {
166                buffer.append("; ");
167            }
168            buffer.append(cookie.getName());
169            buffer.append("=");
170            String s = cookie.getValue();
171            if (s != null) {
172                buffer.append(s);
173            }
174        }
175        List<Header> headers = new ArrayList<Header>(1);
176        headers.add(new BufferedHeader(buffer));
177        return headers;
178    }
179
180    public int getVersion() {
181        return 0;
182    }
183
184    public Header getVersionHeader() {
185        return null;
186    }
187
188}
189