1 /*
2  * ====================================================================
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  * ====================================================================
20  *
21  * This software consists of voluntary contributions made by many
22  * individuals on behalf of the Apache Software Foundation.  For more
23  * information on the Apache Software Foundation, please see
24  * <http://www.apache.org/>.
25  *
26  */

27
28 package org.apache.http.impl.cookie;
29
30 import java.util.StringTokenizer;
31
32 import org.apache.http.annotation.Contract;
33 import org.apache.http.annotation.ThreadingBehavior;
34 import org.apache.http.cookie.ClientCookie;
35 import org.apache.http.cookie.CommonCookieAttributeHandler;
36 import org.apache.http.cookie.Cookie;
37 import org.apache.http.cookie.CookieOrigin;
38 import org.apache.http.cookie.CookieRestrictionViolationException;
39 import org.apache.http.cookie.MalformedCookieException;
40 import org.apache.http.cookie.SetCookie;
41 import org.apache.http.cookie.SetCookie2;
42 import org.apache.http.util.Args;
43
44 /**
45  * {@code "Port"} cookie attribute handler for RFC 2965 cookie spec.
46  *
47  * @since 4.0
48  */

49 @Contract(threading = ThreadingBehavior.IMMUTABLE)
50 public class RFC2965PortAttributeHandler implements CommonCookieAttributeHandler {
51
52     public RFC2965PortAttributeHandler() {
53         super();
54     }
55
56     /**
57      * Parses the given Port attribute value (e.g. "8000,8001,8002")
58      * into an array of ports.
59      *
60      * @param portValue port attribute value
61      * @return parsed array of ports
62      * @throws MalformedCookieException if there is a problem in
63      *          parsing due to invalid portValue.
64      */

65     private static int[] parsePortAttribute(final String portValue)
66             throws MalformedCookieException {
67         final StringTokenizer st = new StringTokenizer(portValue, ",");
68         final int[] ports = new int[st.countTokens()];
69         try {
70             int i = 0;
71             while(st.hasMoreTokens()) {
72                 ports[i] = Integer.parseInt(st.nextToken().trim());
73                 if (ports[i] < 0) {
74                   throw new MalformedCookieException ("Invalid Port attribute.");
75                 }
76                 ++i;
77             }
78         } catch (final NumberFormatException e) {
79             throw new MalformedCookieException ("Invalid Port "
80                                                 + "attribute: " + e.getMessage());
81         }
82         return ports;
83     }
84
85     /**
86      * Returns {@code trueif the given port exists in the given
87      * ports list.
88      *
89      * @param port port of host where cookie was received from or being sent to.
90      * @param ports port list
91      * @return true returns {@code trueif the given port exists in
92      *         the given ports list; {@code false} otherwise.
93      */

94     private static boolean portMatch(final int port, final int[] ports) {
95         boolean portInList = false;
96         for (final int port2 : ports) {
97             if (port == port2) {
98                 portInList = true;
99                 break;
100             }
101         }
102         return portInList;
103     }
104
105     /**
106      * Parse cookie port attribute.
107      */

108     @Override
109     public void parse(final SetCookie cookie, final String portValue)
110             throws MalformedCookieException {
111         Args.notNull(cookie, "Cookie");
112         if (cookie instanceof SetCookie2) {
113             final SetCookie2 cookie2 = (SetCookie2) cookie;
114             if (portValue != null && !portValue.trim().isEmpty()) {
115                 final int[] ports = parsePortAttribute(portValue);
116                 cookie2.setPorts(ports);
117             }
118         }
119     }
120
121     /**
122      * Validate cookie port attribute. If the Port attribute was specified
123      * in header, the request port must be in cookie's port list.
124      */

125     @Override
126     public void validate(final Cookie cookie, final CookieOrigin origin)
127             throws MalformedCookieException {
128         Args.notNull(cookie, "Cookie");
129         Args.notNull(origin, "Cookie origin");
130         final int port = origin.getPort();
131         if (cookie instanceof ClientCookie
132                 && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
133             if (!portMatch(port, cookie.getPorts())) {
134                 throw new CookieRestrictionViolationException(
135                         "Port attribute violates RFC 2965: "
136                         + "Request port not found in cookie's port list.");
137             }
138         }
139     }
140
141     /**
142      * Match cookie port attribute. If the Port attribute is not specified
143      * in header, the cookie can be sent to any port. Otherwise, the request port
144      * must be in the cookie's port list.
145      */

146     @Override
147     public boolean match(final Cookie cookie, final CookieOrigin origin) {
148         Args.notNull(cookie, "Cookie");
149         Args.notNull(origin, "Cookie origin");
150         final int port = origin.getPort();
151         if (cookie instanceof ClientCookie
152                 && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
153             if (cookie.getPorts() == null) {
154                 // Invalid cookie state: port not specified
155                 return false;
156             }
157             if (!portMatch(port, cookie.getPorts())) {
158                 return false;
159             }
160         }
161         return true;
162     }
163
164     @Override
165     public String getAttributeName() {
166         return ClientCookie.PORT_ATTR;
167     }
168
169 }
170