Cookie2.java
Upload User: demmber
Upload Date: 2007-12-22
Package Size: 717k
Code Size: 15k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * @(#)Cookie2.java 0.3-3 06/05/2001
  3.  *
  4.  *  This file is part of the HTTPClient package
  5.  *  Copyright (C) 1996-2001 Ronald Tschal鋜
  6.  *
  7.  *  This library is free software; you can redistribute it and/or
  8.  *  modify it under the terms of the GNU Lesser General Public
  9.  *  License as published by the Free Software Foundation; either
  10.  *  version 2 of the License, or (at your option) any later version.
  11.  *
  12.  *  This library is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  *  Lesser General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU Lesser General Public
  18.  *  License along with this library; if not, write to the Free
  19.  *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20.  *  MA 02111-1307, USA
  21.  *
  22.  *  For questions, suggestions, bug-reports, enhancement-requests etc.
  23.  *  I may be contacted at:
  24.  *
  25.  *  ronald@innovation.ch
  26.  *
  27.  *  The HTTPClient's home page is located at:
  28.  *
  29.  *  http://www.innovation.ch/java/HTTPClient/ 
  30.  *
  31.  */
  32. package HTTPClient;
  33. import java.io.UnsupportedEncodingException;
  34. import java.net.ProtocolException;
  35. import java.util.Date;
  36. import java.util.Vector;
  37. import java.util.StringTokenizer;
  38. /**
  39.  * This class represents an http cookie as specified in the <A
  40.  * HREF="http://www.ietf.org/rfc/rfc2965.txt">HTTP State Management Mechanism spec</A>
  41.  * (also known as a version 1 cookie).
  42.  *
  43.  * @version 0.3-3  06/05/2001
  44.  * @author Ronald Tschal鋜
  45.  * @since V0.3
  46.  */
  47. public class Cookie2 extends Cookie
  48. {
  49.     /** Make this compatible with V0.3-2 */
  50.     private static final long serialVersionUID = 2208203902820875917L;
  51.     protected int     version;
  52.     protected boolean discard;
  53.     protected String  comment;
  54.     protected URI     comment_url;
  55.     protected int[]   port_list;
  56.     protected String  port_list_str;
  57.     protected boolean path_set;
  58.     protected boolean port_set;
  59.     protected boolean domain_set;
  60.     /**
  61.      * Create a cookie.
  62.      *
  63.      * @param name      the cookie name
  64.      * @param value     the cookie value
  65.      * @param domain    the host this cookie will be sent to
  66.      * @param port_list an array of allowed server ports for this cookie,
  67.      *                  or null if the the cookie may be sent to any port
  68.      * @param path      the path prefix for which this cookie will be sent
  69.      * @param epxires   the Date this cookie expires, or null if never
  70.      * @param discard   if true then the cookie will be discarded at the
  71.      *                  end of the session regardless of expiry
  72.      * @param secure    if true this cookie will only be over secure connections
  73.      * @param comment   the comment associated with this cookie, or null if none
  74.      * @param comment_url the comment URL associated with this cookie, or null
  75.      *                    if none
  76.      * @exception NullPointerException if <var>name</var>, <var>value</var>,
  77.      *                                 <var>domain</var>, or <var>path</var>
  78.      *                                 is null
  79.      */
  80.     public Cookie2(String name, String value, String domain, int[] port_list,
  81.    String path, Date expires, boolean discard, boolean secure,
  82.    String comment, URI comment_url)
  83.     {
  84. super(name, value, domain, path, expires, secure);
  85. this.discard     = discard;
  86. this.port_list   = port_list;
  87. this.comment     = comment;
  88. this.comment_url = comment_url;
  89. path_set   = true;
  90. domain_set = true;
  91. if (port_list != null  &&  port_list.length > 0)
  92. {
  93.     StringBuffer tmp = new StringBuffer();
  94.     tmp.append(port_list[0]);
  95.     for (int idx=1; idx<port_list.length; idx++)
  96.     {
  97. tmp.append(',');
  98. tmp.append(port_list[idx]);
  99.     }
  100.     port_list_str = tmp.toString();
  101.     port_set      = true;
  102. }
  103. version = 1;
  104.     }
  105.     /**
  106.      * Use <code>parse()</code> to create cookies.
  107.      *
  108.      * @see #parse(java.lang.String, HTTPClient.RoRequest)
  109.      */
  110.     protected Cookie2(RoRequest req)
  111.     {
  112. super(req);
  113. path = Util.getPath(req.getRequestURI());
  114. int slash = path.lastIndexOf('/');
  115. if (slash != -1)  path = path.substring(0, slash+1);
  116. if (domain.indexOf('.') == -1)  domain += ".local";
  117. version       = -1;
  118. discard       = false;
  119. comment       = null;
  120. comment_url   = null;
  121. port_list     = null;
  122. port_list_str = null;
  123. path_set      = false;
  124. port_set      = false;
  125. domain_set    = false;
  126.     }
  127.     /**
  128.      * Parses the Set-Cookie2 header into an array of Cookies.
  129.      *
  130.      * @param set_cookie the Set-Cookie2 header received from the server
  131.      * @param req the request used
  132.      * @return an array of Cookies as parsed from the Set-Cookie2 header
  133.      * @exception ProtocolException if an error occurs during parsing
  134.      */
  135.     protected static Cookie[] parse(String set_cookie, RoRequest req)
  136. throws ProtocolException
  137.     {
  138. Vector cookies;
  139. try
  140.     { cookies = Util.parseHeader(set_cookie); }
  141. catch (ParseException pe)
  142.     { throw new ProtocolException(pe.getMessage()); }
  143.         Cookie cookie_arr[] = new Cookie[cookies.size()];
  144. int cidx=0;
  145. for (int idx=0; idx<cookie_arr.length; idx++)
  146. {
  147.     HttpHeaderElement c_elem =
  148. (HttpHeaderElement) cookies.elementAt(idx);
  149.     // set NAME and VALUE
  150.     if (c_elem.getValue() == null)
  151. throw new ProtocolException("Bad Set-Cookie2 header: " +
  152.     set_cookie + "nMissing value " +
  153.     "for cookie '" + c_elem.getName() +
  154.     "'");
  155.     Cookie2 curr = new Cookie2(req);
  156.     curr.name    = c_elem.getName();
  157.     curr.value   = c_elem.getValue();
  158.     // set all params
  159.     NVPair[] params = c_elem.getParams();
  160.     boolean discard_set = false, secure_set = false;
  161.     for (int idx2=0; idx2<params.length; idx2++)
  162.     {
  163. String name = params[idx2].getName().toLowerCase();
  164. // check for required value parts
  165. if ((name.equals("version")  ||  name.equals("max-age")  ||
  166.      name.equals("domain")  ||  name.equals("path")  ||
  167.      name.equals("comment")  ||  name.equals("commenturl"))  &&
  168.     params[idx2].getValue() == null)
  169. {
  170.     throw new ProtocolException("Bad Set-Cookie2 header: " +
  171. set_cookie + "nMissing value "+
  172. "for " + params[idx2].getName()+
  173. " attribute in cookie '" +
  174. c_elem.getName() + "'");
  175. }
  176. if (name.equals("version")) // Version
  177. {
  178.     if (curr.version != -1)  continue;
  179.     try
  180.     {
  181. curr.version =
  182. Integer.parseInt(params[idx2].getValue());
  183.     }
  184.     catch (NumberFormatException nfe)
  185.     {
  186. throw new ProtocolException("Bad Set-Cookie2 header: " +
  187.     set_cookie + "nVersion '" +
  188.     params[idx2].getValue() +
  189.     "' not a number");
  190.     }
  191. }
  192. else if (name.equals("path")) // Path
  193. {
  194.     if (curr.path_set)  continue;
  195.     curr.path = params[idx2].getValue();
  196.     curr.path_set = true;
  197. }
  198. else if (name.equals("domain")) // Domain
  199. {
  200.     if (curr.domain_set)  continue;
  201.     String d = params[idx2].getValue().toLowerCase();
  202.     // add leading dot if not present and if domain is
  203.     // not the full host name
  204.     if (d.charAt(0) != '.'  &&  !d.equals(curr.domain))
  205. curr.domain = "." + d;
  206.     else
  207. curr.domain = d;
  208.     curr.domain_set = true;
  209. }
  210. else if (name.equals("max-age")) // Max-Age
  211. {
  212.     if (curr.expires != null)  continue;
  213.     int age;
  214.     try
  215. { age = Integer.parseInt(params[idx2].getValue()); }
  216.     catch (NumberFormatException nfe)
  217.     {
  218. throw new ProtocolException("Bad Set-Cookie2 header: " +
  219.     set_cookie + "nMax-Age '" +
  220.     params[idx2].getValue() +
  221.     "' not a number");
  222.     }
  223.     curr.expires =
  224.     new Date(System.currentTimeMillis() + age*1000L);
  225. }
  226. else if (name.equals("port")) // Port
  227. {
  228.     if (curr.port_set)  continue;
  229.     if (params[idx2].getValue() == null)
  230.     {
  231. curr.port_list    = new int[1];
  232. curr.port_list[0] = req.getConnection().getPort();
  233. curr.port_set     = true;
  234. continue;
  235.     }
  236.     curr.port_list_str = params[idx2].getValue();
  237.     StringTokenizer tok =
  238.     new StringTokenizer(params[idx2].getValue(), ",");
  239.     curr.port_list = new int[tok.countTokens()];
  240.     for (int idx3=0; idx3<curr.port_list.length; idx3++)
  241.     {
  242. String port = tok.nextToken().trim();
  243. try
  244.     { curr.port_list[idx3] = Integer.parseInt(port); }
  245. catch (NumberFormatException nfe)
  246. {
  247.     throw new ProtocolException("Bad Set-Cookie2 header: " +
  248.     set_cookie + "nPort '" +
  249.     port + "' not a number");
  250. }
  251.     }
  252.     curr.port_set = true;
  253. }
  254. else if (name.equals("discard")) // Domain
  255. {
  256.     if (discard_set)  continue;
  257.     curr.discard = true;
  258.     discard_set  = true;
  259. }
  260. else if (name.equals("secure")) // Secure
  261. {
  262.     if (secure_set)  continue;
  263.     curr.secure = true;
  264.     secure_set  = true;
  265. }
  266. else if (name.equals("comment")) // Comment
  267. {
  268.     if (curr.comment != null)  continue;
  269.     try
  270.     {
  271. curr.comment =
  272.     new String(params[idx2].getValue().getBytes("8859_1"), "UTF8");
  273.     }
  274.     catch (UnsupportedEncodingException usee)
  275. { throw new Error(usee.toString()); /* shouldn't happen */ }
  276. }
  277. else if (name.equals("commenturl")) // CommentURL
  278. {
  279.     if (curr.comment_url != null)  continue;
  280.     try
  281. { curr.comment_url = new URI(params[idx2].getValue()); }
  282.     catch (ParseException pe)
  283.     {
  284. throw new ProtocolException("Bad Set-Cookie2 header: " +
  285. set_cookie + "nCommentURL '" +
  286. params[idx2].getValue() +
  287. "' not a valid URL");
  288.     }
  289. }
  290. // ignore unknown element
  291.     }
  292.     // check version
  293.     if (curr.version == -1)  continue;
  294.     // setup defaults
  295.     if (curr.expires == null)  curr.discard = true;
  296.     // check validity
  297.     // path attribute must be a prefix of the request-URI
  298.     if (!Util.getPath(req.getRequestURI()).startsWith(curr.path))
  299.     {
  300. Log.write(Log.COOKI, "Cook2: Bad Set-Cookie2 header: " +
  301.      set_cookie + "n       path `" +
  302.      curr.path + "' is not a prefix of the " +
  303.      "request uri `" + req.getRequestURI() +
  304.      "'");
  305. continue;
  306.     }
  307.     // if host name is simple (i.e w/o a domain) then append .local
  308.     String eff_host = req.getConnection().getHost();
  309.     if (eff_host.indexOf('.') == -1)  eff_host += ".local";
  310.     // domain must be either .local or must contain at least two dots
  311.     if (!curr.domain.equals(".local")  &&
  312. curr.domain.indexOf('.', 1) == -1)
  313.     {
  314. Log.write(Log.COOKI, "Cook2: Bad Set-Cookie2 header: " +
  315.      set_cookie + "n       domain `" +
  316.      curr.domain + "' is not `.local' and " +
  317.      "doesn't contain two `.'s");
  318. continue;
  319.     }
  320.     // domain must domain match host
  321.     if (!eff_host.endsWith(curr.domain))
  322.     {
  323. Log.write(Log.COOKI, "Cook2: Bad Set-Cookie2 header: " +
  324.      set_cookie + "n       domain `" +
  325.      curr.domain + "' does not match current" +
  326.      "host `" + eff_host + "'"); 
  327. continue;
  328.     }
  329.     // host minus domain may not contain any dots
  330.     if (eff_host.substring(0, eff_host.length()-curr.domain.length()).
  331. indexOf('.') != -1)
  332.     {
  333. Log.write(Log.COOKI, "Cook2: Bad Set-Cookie2 header: " +
  334.      set_cookie + "n       domain `" +
  335.      curr.domain + "' is more than one `.'" +
  336.      "away from host `" + eff_host + "'"); 
  337. continue;
  338.     }
  339.     // if a port list is given it must include the current port
  340.     if (curr.port_set)
  341.     {
  342. int idx2=0;
  343. for (idx2=0; idx2<curr.port_list.length; idx2++)
  344.     if (curr.port_list[idx2] == req.getConnection().getPort())
  345. break;
  346. if (idx2 == curr.port_list.length)
  347. {
  348.     Log.write(Log.COOKI, "Cook2: Bad Set-Cookie2 header: " +
  349.  set_cookie + "n       port list " +
  350.  "does include current port " +
  351.  req.getConnection().getPort());
  352.     continue;
  353. }
  354.     }
  355.     // looks ok
  356.     cookie_arr[cidx++] = curr;
  357. }
  358. if (cidx < cookie_arr.length)
  359.     cookie_arr = Util.resizeArray(cookie_arr, cidx);
  360. return cookie_arr;
  361.     }
  362.     /**
  363.      * @return the version as an int
  364.      */
  365.     public int getVersion()
  366.     {
  367. return version;
  368.     }
  369.  
  370.     /**
  371.      * @return the comment string, or null if none was set
  372.      */
  373.     public String getComment()
  374.     {
  375. return comment;
  376.     }
  377.  
  378.     /**
  379.      * @return the comment url
  380.      */
  381.     public URI getCommentURL()
  382.     {
  383. return comment_url;
  384.     }
  385.  
  386.     /**
  387.      * @return the array of ports
  388.      */
  389.     public int[] getPorts()
  390.     {
  391. return port_list;
  392.     }
  393.  
  394.     /**
  395.      * @return true if the cookie should be discarded at the end of the
  396.      *         session; false otherwise
  397.      */
  398.     public boolean discard()
  399.     {
  400. return discard;
  401.     }
  402.  
  403.     /**
  404.      * @param  req  the request to be sent
  405.      * @return true if this cookie should be sent with the request
  406.      */
  407.     protected boolean sendWith(RoRequest req)
  408.     {
  409. HTTPConnection con = req.getConnection();
  410. boolean port_match = !port_set;
  411. if (port_set)
  412.     for (int idx=0; idx<port_list.length; idx++)
  413. if (port_list[idx] == con.getPort())
  414. {
  415.     port_match = true;
  416.     break;
  417. }
  418. String eff_host = con.getHost();
  419. if (eff_host.indexOf('.') == -1)  eff_host += ".local";
  420. return ((domain.charAt(0) == '.'  &&  eff_host.endsWith(domain)  ||
  421.  domain.charAt(0) != '.'  &&  eff_host.equals(domain))  &&
  422. port_match  &&
  423. Util.getPath(req.getRequestURI()).startsWith(path)  &&
  424. (!secure || con.getProtocol().equals("https") ||
  425.  con.getProtocol().equals("shttp")));
  426.     }
  427.  
  428.     protected String toExternalForm()
  429.     {
  430. StringBuffer cookie = new StringBuffer();
  431. if (version == 1)
  432. {
  433.     /*
  434.     cookie.append("$Version=");
  435.     cookie.append(version);
  436.     cookie.append("; ");
  437.     */
  438.     cookie.append(name);
  439.     cookie.append("=");
  440.     cookie.append(value);
  441.     if (path_set)
  442.     {
  443. cookie.append("; ");
  444. cookie.append("$Path=");
  445. cookie.append(path);
  446.     }
  447.     if (domain_set)
  448.     {
  449. cookie.append("; ");
  450. cookie.append("$Domain=");
  451. cookie.append(domain);
  452.     }
  453.     if (port_set)
  454.     {
  455. cookie.append("; ");
  456. cookie.append("$Port");
  457. if (port_list_str != null)
  458. {
  459.     cookie.append("="");
  460.     cookie.append(port_list_str);
  461.     cookie.append('"');
  462. }
  463.     }
  464. }
  465. else
  466.     throw new Error("Internal Error: unknown version " + version);
  467. return cookie.toString();
  468.     }
  469.     /**
  470.      * Create a string containing all the cookie fields. The format is that
  471.      * used in the Set-Cookie header.
  472.      */
  473.     public String toString()
  474.     {
  475. StringBuffer res = new StringBuffer(name.length() + value.length() + 50);
  476. res.append(name).append('=').append(value);
  477. if (version == 1)
  478. {
  479.     res.append("; Version=").append(version);
  480.     res.append("; Path=").append(path);
  481.     res.append("; Domain=").append(domain);
  482.     if (port_set)
  483.     {
  484. res.append("; Port="").append(port_list[0]);
  485. for (int idx=1; idx<port_list.length; idx++)
  486.     res.append(',').append(port_list[idx]);
  487. res.append('"');
  488.     }
  489.     if (expires != null)
  490. res.append("; Max-Age=").append(
  491.     ((expires.getTime() - System.currentTimeMillis()) / 1000L));
  492.     if (discard)           res.append("; Discard");
  493.     if (secure)            res.append("; Secure");
  494.     if (comment != null)   res.append("; Comment="").append(comment).append('"');
  495.     if (comment_url != null)
  496. res.append("; CommentURL="").append(comment_url).append('"');
  497. }
  498. else
  499.     throw new Error("Internal Error: unknown version " + version);
  500. return res.toString();
  501.     }
  502. }