1   //========================================================================
2   //Copyright 2004-2008 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at 
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.jetty;
16  import javax.servlet.http.Cookie;
17  import javax.servlet.http.HttpServletRequest;
18  
19  import org.mortbay.log.Log;
20  import org.mortbay.util.LazyList;
21  import org.mortbay.util.URIUtil;
22  
23  
24  /* ------------------------------------------------------------ */
25  /** Cookie parser
26   * <p>Optimized stateful cookie parser.  Cookies fields are added with the
27   * {@link #addCookieField(String)} method and parsed on the next subsequent
28   * call to {@link #getCookies()}.
29   * If the added fields are identical to those last added (as strings), then the 
30   * cookies are not re parsed.
31   * @author gregw
32   *
33   */
34  public class CookieCutter
35  {
36      private static final byte STATE_DELIMITER = 1;
37      private static final byte STATE_NAME = 2;
38      private static final byte STATE_VALUE = 4;
39      private static final byte STATE_QUOTED_VALUE = 8;
40      private static final byte STATE_UNQUOTED_VALUE = 16;
41  
42      private Cookie[] _cookies;
43      private String[] _fields;
44      int _added=0;
45      boolean _dirty;
46      HttpServletRequest _request;
47      
48      public CookieCutter()
49      {
50          
51      }
52   
53      public CookieCutter(HttpServletRequest request)
54      {
55          _request = request;
56      }
57      
58      public Cookie[] getCookies()
59      {
60          if (_added>0) 
61          {
62              if (!_dirty && _added==_fields.length)
63              {
64                  // same cookies as last time!
65                  _added=0;
66                  return _cookies;
67              }
68              
69              parseFields();
70          }
71          return _cookies;
72      }
73      
74      public void setCookies(Cookie[] cookies)
75      {
76          _dirty=false;
77          _added=0;
78          _cookies=cookies;
79      }
80      
81      public void reset()
82      {
83          _fields=null;
84          _cookies=null;
85      }
86      
87      public void addCookieField(String f)
88      {
89          if (!_dirty &&
90              _fields!=null && 
91              _fields.length>_added &&
92              _fields[_added].equals(f))
93          {
94              _added++;
95              return;
96          }
97          
98          if (_dirty)
99          {
100             _added++;
101             _fields=(String[])LazyList.addToArray(_fields,f,String.class);
102         }
103         else
104         {
105             _dirty=true;
106             if (_added>0)
107             {
108                 String[] fields=new String[_added+1];
109                 System.arraycopy(_fields,0,fields,0,_added);
110                 fields[_added++]=f;
111                 _fields=fields;
112             }
113             else
114             {
115                 _fields = new String[]{f};
116                 _added=1;
117             }
118             
119         }
120     }
121     
122     protected void parseFields()
123     {
124         Object cookies = null;
125 
126         int version = 0;
127 
128         // For each cookie field
129         for (int f=0;f<_added;f++)
130         {
131             String hdr = _fields[f];
132             
133             // Parse the header
134             String name = null;
135             String value = null;
136 
137             Cookie cookie = null;
138 
139             byte state = STATE_NAME;
140             for (int i = 0, tokenstart = 0, length = hdr.length(); i < length; i++)
141             {
142                 char c = hdr.charAt(i);
143                 switch (c)
144                 {
145                     case ',':
146                     case ';':
147                         switch (state)
148                         {
149                             case STATE_DELIMITER:
150                                 state = STATE_NAME;
151                                 tokenstart = i + 1;
152                                 break;
153                             case STATE_UNQUOTED_VALUE:
154                                 state = STATE_NAME;
155                                 value = hdr.substring(tokenstart, i).trim();
156                                 if(_request!=null && _request.isRequestedSessionIdFromURL())
157                                     value = URIUtil.decodePath(value);
158                                 tokenstart = i + 1;
159                                 break;
160                             case STATE_NAME:
161                                 name = hdr.substring(tokenstart, i);
162                                 value = "";
163                                 tokenstart = i + 1;
164                                 break;
165                             case STATE_VALUE:
166                                 state = STATE_NAME;
167                                 value = "";
168                                 tokenstart = i + 1;
169                                 break;
170                         }
171                         break;
172                     case '=':
173                         switch (state)
174                         {
175                             case STATE_NAME:
176                                 state = STATE_VALUE;
177                                 name = hdr.substring(tokenstart, i);
178                                 tokenstart = i + 1;
179                                 break;
180                             case STATE_VALUE:
181                                 state = STATE_UNQUOTED_VALUE;
182                                 tokenstart = i;
183                                 break;
184                         }
185                         break;
186                     case '"':
187                         switch (state)
188                         {
189                             case STATE_VALUE:
190                                 state = STATE_QUOTED_VALUE;
191                                 tokenstart = i + 1;
192                                 break;
193                             case STATE_QUOTED_VALUE:
194                                 state = STATE_DELIMITER;
195                                 value = hdr.substring(tokenstart, i);
196                                 break;
197                         }
198                         break;
199                     case ' ':
200                     case '\t':
201                         break;
202                     default:
203                         switch (state)
204                         {
205                             case STATE_VALUE:
206                                 state = STATE_UNQUOTED_VALUE;
207                                 tokenstart = i;
208                                 break;
209                             case STATE_DELIMITER:
210                                 state = STATE_NAME;
211                                 tokenstart = i;
212                                 break;
213                         }
214                 }
215 
216                 if (i + 1 == length)
217                 {
218                     switch (state)
219                     {
220                         case STATE_UNQUOTED_VALUE:
221                             value = hdr.substring(tokenstart).trim();
222                             if(_request!=null && _request.isRequestedSessionIdFromURL())
223                                 value = URIUtil.decodePath(value);
224                             break;
225                         case STATE_NAME:
226                             name = hdr.substring(tokenstart);
227                             value = "";
228                             break;
229                         case STATE_VALUE:
230                             value = "";
231                             break;
232                     }
233                 }
234 
235                 if (name != null && value != null)
236                 {
237                     name = name.trim();
238 
239                     try
240                     {
241                         if (name.startsWith("$"))
242                         {
243                             String lowercaseName = name.toLowerCase();
244                             if ("$path".equals(lowercaseName))
245                             {
246                                 cookie.setPath(value);
247                             }
248                             else if ("$domain".equals(lowercaseName))
249                             {
250                                 cookie.setDomain(value);
251                             }
252                             else if ("$version".equals(lowercaseName))
253                             {
254                                 version = Integer.parseInt(value);
255                             }
256                         }
257                         else
258                         {
259                             cookie = new Cookie(name, value);
260 
261                             if (version > 0)
262                             {
263                                 cookie.setVersion(version);
264                             }
265 
266                             cookies = LazyList.add(cookies, cookie);
267                         }
268                     }
269                     catch (Exception e)
270                     {
271                         Log.ignore(e);
272                     }
273 
274                     name = null;
275                     value = null;
276                 }
277             }
278         }
279 
280         int l = LazyList.size(cookies);
281         if (l>0)
282         {
283             if (_cookies != null && _cookies.length == l) 
284             {
285                 for (int i = 0; i < l; i++)
286                     _cookies[i] = (Cookie) LazyList.get(cookies, i);
287             }
288             else
289                 _cookies = (Cookie[]) LazyList.toArray(cookies,Cookie.class);
290         }
291         
292         _added=0;
293         _dirty=false;
294         
295     }
296     
297 }