/**
   * 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");
    }
  }