/** * Escapes any double quotes in the given string. * * @param s the input string * @param beginIndex start index inclusive * @param endIndex exclusive * @return The (possibly) escaped string */ private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) { if (s == null || s.length() == 0 || s.indexOf('"') == -1) { return s; } StringBuilder b = new StringBuilder(); for (int i = beginIndex; i < endIndex; i++) { char c = s.charAt(i); if (c == '\\') { b.append(c); // ignore the character after an escape, just append it if (++i >= endIndex) { throw new IllegalArgumentException("Invalid escape character in cookie value."); } b.append(s.charAt(i)); } else if (c == '"') { b.append('\\').append('"'); } else { b.append(c); } } return b.toString(); }
// TODO RFC2965 fields also need to be passed public static void serializeClientCookies( StringBuilder buf, boolean versionOneStrictCompliance, boolean rfc6265Support, Cookie... cookies) { if (cookies.length == 0) { return; } final int version = cookies[0].getVersion(); if (!rfc6265Support && version == 1) { buf.append("$Version=\"1\"; "); } for (int i = 0; i < cookies.length; i++) { final Cookie cookie = cookies[i]; buf.append(cookie.getName()); buf.append('='); // Servlet implementation does not check anything else maybeQuote2(version, buf, cookie.getValue(), true, rfc6265Support); // If version == 1 - add domain and path if (!rfc6265Support && version == 1) { // $Domain="domain" final String domain = cookie.getDomain(); if (domain != null) { buf.append("; $Domain="); maybeQuote2(version, buf, domain, versionOneStrictCompliance, rfc6265Support); } // $Path="path" String path = cookie.getPath(); if (path != null) { buf.append("; $Path="); URLEncoder encoder = new URLEncoder(); encoder.addSafeCharacter('/'); encoder.addSafeCharacter('"'); path = encoder.encodeURL(path, true); maybeQuote2( version, buf, path, tspecials2NoSlash, false, versionOneStrictCompliance, rfc6265Support); } } if (i < cookies.length - 1) { buf.append("; "); } } }
static void put(StringBuilder dstBuffer, String s) { dstBuffer.append(s); }
static void putInt(StringBuilder dstBuffer, int intValue) { dstBuffer.append(intValue); }
static void put(StringBuilder dstBuffer, int c) { dstBuffer.append((char) c); }
public static int maybeQuote2( int version, StringBuilder buf, String value, String literals, boolean allowVersionSwitch, boolean versionOneStrictCompliance, boolean rfc6265Enabled) { if (value == null || value.length() == 0) { buf.append("\"\""); } else if (containsCTL(value, version)) { throw new IllegalArgumentException( "Control character in cookie value, consider BASE64 encoding your value"); } else if (alreadyQuoted(value)) { buf.append('"'); buf.append(escapeDoubleQuotes(value, 1, value.length() - 1)); buf.append('"'); } else if (allowVersionSwitch && versionOneStrictCompliance && version == 0 && !isToken2(value, literals)) { buf.append('"'); buf.append(escapeDoubleQuotes(value, 0, value.length())); buf.append('"'); version = 1; } else if (version == 0 && !isToken(value, literals)) { buf.append('"'); buf.append(escapeDoubleQuotes(value, 0, value.length())); buf.append('"'); } else if (version == 1 && !isToken2(value, literals)) { buf.append('"'); buf.append(escapeDoubleQuotes(value, 0, value.length())); buf.append('"'); } else if (version < 0 && rfc6265Enabled) { buf.append('"'); buf.append(escapeDoubleQuotes(value, 0, value.length())); buf.append('"'); } else { buf.append(value); } return version; }
// TODO RFC2965 fields also need to be passed public static void serializeServerCookie( final StringBuilder buf, final boolean versionOneStrictCompliance, final boolean rfc6265Support, final boolean alwaysAddExpires, final String name, final String value, int version, String path, final String domain, final String comment, final int maxAge, final boolean isSecure, final boolean isHttpOnly) { // Servlet implementation checks name buf.append(name); buf.append('='); // Servlet implementation does not check anything else version = maybeQuote2(version, buf, value, true, rfc6265Support); // Add version 1 specific information if (version == 1) { // Version=1 ... required buf.append("; Version=1"); // Comment=comment if (comment != null) { buf.append("; Comment="); maybeQuote2(version, buf, comment, versionOneStrictCompliance, rfc6265Support); } } // Add domain information, if present if (domain != null) { buf.append("; Domain="); maybeQuote2(version, buf, domain, versionOneStrictCompliance, rfc6265Support); } // Max-Age=secs ... or use old "Expires" format // TODO RFC2965 Discard if (maxAge >= 0) { if (version > 0) { buf.append("; Max-Age="); buf.append(maxAge); } // IE6, IE7 and possibly other browsers don't understand Max-Age. // They do understand Expires, even with V1 cookies! if (version == 0 || alwaysAddExpires) { // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format ) buf.append("; Expires="); // To expire immediately we need to set the time in past if (maxAge == 0) { buf.append(ancientDate); } else { buf.append( OLD_COOKIE_FORMAT .get() .format(new Date(System.currentTimeMillis() + maxAge * 1000L))); } } } // Path=path if (path != null) { buf.append("; Path="); URLEncoder encoder = new URLEncoder(); encoder.addSafeCharacter('/'); encoder.addSafeCharacter('"'); path = encoder.encodeURL(path, true); if (version == 0) { maybeQuote2(version, buf, path, versionOneStrictCompliance, rfc6265Support); } else { maybeQuote2( version, buf, path, tspecials2NoSlash, false, versionOneStrictCompliance, rfc6265Support); } } // Secure if (isSecure) { buf.append("; Secure"); } // httpOnly if (isHttpOnly) { buf.append("; HttpOnly"); } }