diff --git a/src/main/java/org/json/Cookie.java b/src/main/java/org/json/Cookie.java
index 5da423a..ddd1c69 100644
--- a/src/main/java/org/json/Cookie.java
+++ b/src/main/java/org/json/Cookie.java
@@ -27,6 +27,7 @@ SOFTWARE.
/**
* Convert a web browser cookie specification to a JSONObject and back.
* JSON and Cookies are both notations for name/value pairs.
+ * See also: https://tools.ietf.org/html/rfc6265
* @author JSON.org
* @version 2015-12-09
*/
@@ -65,10 +66,11 @@ public class Cookie {
/**
* Convert a cookie specification string into a JSONObject. The string
- * will contain a name value pair separated by '='. The name and the value
+ * must contain a name value pair separated by '='. The name and the value
* will be unescaped, possibly converting '+' and '%' sequences. The
* cookie properties may follow, separated by ';', also represented as
- * name=value (except the secure property, which does not have a value).
+ * name=value (except the Attribute properties like "Secure" or "HttpOnly",
+ * which do not have a value. The value {@link Boolean#TRUE} will be used for these).
* The name will be stored under the key "name", and the value will be
* stored under the key "value". This method does not do checking or
* validation of the parameters. It only converts the cookie string into
@@ -76,30 +78,51 @@ public class Cookie {
* @param string The cookie specification string.
* @return A JSONObject containing "name", "value", and possibly other
* members.
- * @throws JSONException if a called function fails or a syntax error
+ * @throws JSONException If there is an error parsing the Cookie String.
+ * Cookie strings must have at least one '=' character and the 'name'
+ * portion of the cookie must not be blank.
*/
- public static JSONObject toJSONObject(String string) throws JSONException {
+ public static JSONObject toJSONObject(String string) {
+ final JSONObject jo = new JSONObject();
String name;
- JSONObject jo = new JSONObject();
Object value;
+
+
JSONTokener x = new JSONTokener(string);
- jo.put("name", x.nextTo('='));
+
+ name = unescape(x.nextTo('=').trim());
+ //per RFC6265, if the name is blank, the cookie should be ignored.
+ if("".equals(name)) {
+ throw new JSONException("Cookies must have a 'name'");
+ }
+ jo.put("name", name);
+ // per RFC6265, if there is no '=', the cookie should be ignored.
+ // the 'next' call here throws an exception if the '=' is not found.
x.next('=');
- jo.put("value", x.nextTo(';'));
+ jo.put("value", unescape(x.nextTo(';')).trim());
+ // discard the ';'
x.next();
+ // parse the remaining cookie attributes
while (x.more()) {
- name = unescape(x.nextTo("=;"));
+ name = unescape(x.nextTo("=;")).trim();
+ // don't allow a cookies attributes to overwrite it's name or value.
+ if("name".equalsIgnoreCase(name)) {
+ throw new JSONException("Illegal attribute name: 'name'");
+ }
+ if("value".equalsIgnoreCase(name)) {
+ throw new JSONException("Illegal attribute name: 'value'");
+ }
+ // check to see if it's a flag property
if (x.next() != '=') {
- if (name.equals("secure")) {
- value = Boolean.TRUE;
- } else {
- throw x.syntaxError("Missing '=' in cookie parameter.");
- }
+ value = Boolean.TRUE;
} else {
- value = unescape(x.nextTo(';'));
+ value = unescape(x.nextTo(';')).trim();
x.next();
}
- jo.put(name, value);
+ // only store non-blank attributes
+ if(!"".equals(name) && !"".equals(value)) {
+ jo.put(name, value);
+ }
}
return jo;
}
@@ -108,9 +131,10 @@ public class Cookie {
/**
* Convert a JSONObject into a cookie specification string. The JSONObject
* must contain "name" and "value" members.
- * If the JSONObject contains "expires", "domain", "path", or "secure"
- * members, they will be appended to the cookie specification string.
- * All other members are ignored.
+ * If the JSONObject contains other members, they will be appended to the cookie
+ * specification string. User-Agents are instructed to ignore unknown attributes,
+ * so ensure your JSONObject is using only known attributes.
+ * See also: https://tools.ietf.org/html/rfc6265
* @param jo A JSONObject
* @return A cookie specification string
* @throws JSONException if a called function fails
@@ -121,21 +145,21 @@ public class Cookie {
sb.append(escape(jo.getString("name")));
sb.append("=");
sb.append(escape(jo.getString("value")));
- if (jo.has("expires")) {
- sb.append(";expires=");
- sb.append(jo.getString("expires"));
- }
- if (jo.has("domain")) {
- sb.append(";domain=");
- sb.append(escape(jo.getString("domain")));
- }
- if (jo.has("path")) {
- sb.append(";path=");
- sb.append(escape(jo.getString("path")));
- }
- if (jo.optBoolean("secure")) {
- sb.append(";secure");
+
+ for(String key : jo.keySet()){
+ if("name".equalsIgnoreCase(key)
+ || "value".equalsIgnoreCase(key)) {
+ // already processed above
+ continue;
+ }
+ Object value = jo.opt(key);
+ if(value instanceof Boolean) {
+ sb.append(';').append(key);
+ } else {
+ sb.append(';').append(key).append('=').append(escape(value.toString()));
+ }
}
+
return sb.toString();
}
diff --git a/src/test/java/org/json/junit/CookieTest.java b/src/test/java/org/json/junit/CookieTest.java
index 74756aa..fc29391 100644
--- a/src/test/java/org/json/junit/CookieTest.java
+++ b/src/test/java/org/json/junit/CookieTest.java
@@ -79,16 +79,12 @@ public class CookieTest {
* Expects a JSONException.
*/
@Test
- public void malFormedAttributeException() {
+ public void booleanAttribute() {
String cookieStr = "this=Cookie;myAttribute";
- try {
- Cookie.toJSONObject(cookieStr);
- fail("Expecting an exception");
- } catch (JSONException e) {
- assertEquals("Expecting an exception message",
- "Missing '=' in cookie parameter. at 23 [character 24 line 1]",
- e.getMessage());
- }
+ JSONObject jo = Cookie.toJSONObject(cookieStr);
+ assertTrue("has key 'name'", jo.has("name"));
+ assertTrue("has key 'value'", jo.has("value"));
+ assertTrue("has key 'myAttribute'", jo.has("myAttribute"));
}
/**
@@ -104,7 +100,25 @@ public class CookieTest {
fail("Expecting an exception");
} catch (JSONException e) {
assertEquals("Expecting an exception message",
- "Expected '=' and instead saw '' at 0 [character 1 line 1]",
+ "Cookies must have a 'name'",
+ e.getMessage());
+ }
+ }
+ /**
+ *
+ * Attempts to create a JSONObject from an cookie string where the name is blank.
+ * Note: Cookie throws an exception, but CookieList does not.
+ * Expects a JSONException
+ */
+ @Test
+ public void emptyNameCookieException() {
+ String cookieStr = " = value ";
+ try {
+ Cookie.toJSONObject(cookieStr);
+ fail("Expecting an exception");
+ } catch (JSONException e) {
+ assertEquals("Expecting an exception message",
+ "Cookies must have a 'name'",
e.getMessage());
}
}
@@ -149,8 +163,8 @@ public class CookieTest {
}
/**
- * Cookie.toString() will omit the non-standard "thiswont=beIncluded"
- * attribute, but the attribute is still stored in the JSONObject.
+ * Cookie.toString() will emit the non-standard "thiswont=beIncluded"
+ * attribute, and the attribute is still stored in the JSONObject.
* This test confirms both behaviors.
*/
@Test
@@ -163,15 +177,15 @@ public class CookieTest {
"thisWont=beIncluded;"+
"secure";
String expectedCookieStr =
- "{\"path\":\"/\","+
+ "{\"thisWont\":\"beIncluded\","+
+ "\"path\":\"/\","+
"\"expires\":\"Wed, 19-Mar-2014 17:53:53 GMT\","+
"\"domain\":\".yahoo.com\","+
"\"name\":\"PH\","+
"\"secure\":true,"+
"\"value\":\"deleted\"}";
// Add the nonstandard attribute to the expected cookie string
- String expectedDirectCompareCookieStr =
- expectedCookieStr.replaceAll("\\{", "\\{\"thisWont\":\"beIncluded\",");
+ String expectedDirectCompareCookieStr = expectedCookieStr;
// convert all strings into JSONObjects
JSONObject jsonObject = Cookie.toJSONObject(cookieStr);
JSONObject expectedJsonObject = new JSONObject(expectedCookieStr);