1 package atg.taglib.json.util;
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 * Convert a web browser cookie specification to a JSONObject and back.
29 * JSON and Cookies are both notations for name/value pairs.
30 * @author JSON.org
31 * @version 2
32 */
33 public class Cookie {
34
35 /**
36 * Produce a copy of a string in which the characters '+', '%', '=', ';'
37 * and control characters are replaced with "%hh". This is a gentle form
38 * of URL encoding, attempting to cause as little distortion to the
39 * string as possible. The characters '=' and ';' are meta characters in
40 * cookies. By convention, they are escaped using the URL-encoding. This is
41 * only a convention, not a standard. Often, cookies are expected to have
42 * encoded values. We encode '=' and ';' because we must. We encode '%' and
43 * '+' because they are meta characters in URL encoding.
44 * @param string The source string.
45 * @return The escaped result.
46 */
47 public static String escape(String string) {
48 char c;
49 String s = string.trim();
50 StringBuffer sb = new StringBuffer();
51 int len = s.length();
52 for (int i = 0; i < len; i += 1) {
53 c = s.charAt(i);
54 if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
55 sb.append('%');
56 sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
57 sb.append(Character.forDigit((char)(c & 0x0f), 16));
58 } else {
59 sb.append(c);
60 }
61 }
62 return sb.toString();
63 }
64
65
66 /**
67 * Convert a cookie specification string into a JSONObject. The string
68 * will contain a name value pair separated by '='. The name and the value
69 * will be unescaped, possibly converting '+' and '%' sequences. The
70 * cookie properties may follow, separated by ';', also represented as
71 * name=value (except the secure property, which does not have a value).
72 * The name will be stored under the key "name", and the value will be
73 * stored under the key "value". This method does not do checking or
74 * validation of the parameters. It only converts the cookie string into
75 * a JSONObject.
76 * @param string The cookie specification string.
77 * @return A JSONObject containing "name", "value", and possibly other
78 * members.
79 * @throws JSONException
80 */
81 public static JSONObject toJSONObject(String string) throws JSONException {
82 String n;
83 JSONObject o = new JSONObject();
84 Object v;
85 JSONTokener x = new JSONTokener(string);
86 o.put("name", x.nextTo('='));
87 x.next('=');
88 o.put("value", x.nextTo(';'));
89 x.next();
90 while (x.more()) {
91 n = unescape(x.nextTo("=;"));
92 if (x.next() != '=') {
93 if (n.equals("secure")) {
94 v = Boolean.TRUE;
95 } else {
96 throw x.syntaxError("Missing '=' in cookie parameter.");
97 }
98 } else {
99 v = unescape(x.nextTo(';'));
100 x.next();
101 }
102 o.put(n, v);
103 }
104 return o;
105 }
106
107
108 /**
109 * Convert a JSONObject into a cookie specification string. The JSONObject
110 * must contain "name" and "value" members.
111 * If the JSONObject contains "expires", "domain", "path", or "secure"
112 * members, they will be appended to the cookie specification string.
113 * All other members are ignored.
114 * @param o A JSONObject
115 * @return A cookie specification string
116 * @throws JSONException
117 */
118 public static String toString(JSONObject o) throws JSONException {
119 StringBuffer sb = new StringBuffer();
120
121 sb.append(escape(o.getString("name")));
122 sb.append("=");
123 sb.append(escape(o.getString("value")));
124 if (o.has("expires")) {
125 sb.append(";expires=");
126 sb.append(o.getString("expires"));
127 }
128 if (o.has("domain")) {
129 sb.append(";domain=");
130 sb.append(escape(o.getString("domain")));
131 }
132 if (o.has("path")) {
133 sb.append(";path=");
134 sb.append(escape(o.getString("path")));
135 }
136 if (o.optBoolean("secure")) {
137 sb.append(";secure");
138 }
139 return sb.toString();
140 }
141
142 /**
143 * Convert <code>%</code><i>hh</i> sequences to single characters, and
144 * convert plus to space.
145 * @param s A string that may contain
146 * <code>+</code> <small>(plus)</small> and
147 * <code>%</code><i>hh</i> sequences.
148 * @return The unescaped string.
149 */
150 public static String unescape(String s) {
151 int len = s.length();
152 StringBuffer b = new StringBuffer();
153 for (int i = 0; i < len; ++i) {
154 char c = s.charAt(i);
155 if (c == '+') {
156 c = ' ';
157 } else if (c == '%' && i + 2 < len) {
158 int d = JSONTokener.dehexchar(s.charAt(i + 1));
159 int e = JSONTokener.dehexchar(s.charAt(i + 2));
160 if (d >= 0 && e >= 0) {
161 c = (char)(d * 16 + e);
162 i += 2;
163 }
164 }
165 b.append(c);
166 }
167 return b.toString();
168 }
169 }