package com.webobjects.appserver;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Locale;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSKeyValueCodingAdditions;
import com.webobjects.foundation.NSTimeZone;
import com.webobjects.foundation.NSTimestamp;
public class WOCookie implements NSKeyValueCoding, NSKeyValueCoding.ErrorHandling,
NSKeyValueCodingAdditions, Serializable {
static final long serialVersionUID = 1L;
String _name;
String _value;
String _domain;
String _path;
boolean _isSecure;
NSTimestamp _expires;
int _timeout;
boolean _isHttpOnly;
SameSite _sameSite;
@Deprecated
static final SimpleDateFormat TheDateFormat;
static {
TheDateFormat = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss 'GMT'", new DateFormatSymbols(Locale.US));
TheDateFormat.setTimeZone(NSTimeZone.timeZoneWithName("GMT", true));
}
/**
* Formatter to use when handling timestamp columns. Each thread has its own
* copy.
*/
private static final ThreadLocal<SimpleDateFormat> TIMESTAMP_FORMATTER = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss 'GMT'", new DateFormatSymbols(Locale.US));
formatter.setTimeZone(NSTimeZone.timeZoneWithName("GMT", true));
return formatter;
}
};
@Deprecated
public static WOCookie cookieWithName(final String name, final String value, final String path,
final String domain, final NSTimestamp expires, final boolean isSecure) {
return new WOCookie(name, value, path, domain, expires, isSecure);
}
@Deprecated
public static WOCookie cookieWithName(final String name, final String value, final String path,
final String domain, final int timeout, final boolean isSecure) {
return new WOCookie(name, value, path, domain, timeout, isSecure);
}
@Deprecated
public static WOCookie cookieWithName(final String name, final String value) {
return new WOCookie(name, value);
}
public WOCookie(final String name, final String value, final String path, final String domain,
final NSTimestamp expires, final boolean isSecure) {
this(name, value, path, domain, expires, isSecure, false);
}
public WOCookie(final String name, final String value, final String path, final String domain,
final NSTimestamp expires, final boolean isSecure, final boolean httpOnly) {
if (name == null) {
throw new IllegalArgumentException("Cookie may not have null name.");
}
_name = name;
_value = value;
_path = path;
_domain = domain;
_expires = expires;
_isSecure = isSecure;
_isHttpOnly = httpOnly;
_sameSite = SameSite.NORMAL;
setTimeOut(-1);
}
public WOCookie(final String name, final String value, final String path, final String domain, final int timeout,
final boolean isSecure) {
this(name, value, path, domain, timeout, isSecure, false);
}
public WOCookie(final String name, final String value, final String path, final String domain, final int timeout,
final boolean isSecure, final boolean httpOnly) {
if (name == null) {
throw new IllegalArgumentException("Cookie may not have null name.");
}
_name = name;
_value = value;
_path = path;
_domain = domain;
setTimeOut(timeout);
_isSecure = isSecure;
_isHttpOnly = httpOnly;
_sameSite = SameSite.NORMAL;
}
public WOCookie(final String name, final String value) {
this(name, value, null, null, -1, false);
}
@Override
public String toString() {
String expiresString = _expires != null ? new StringBuilder().append(" expires=")
.append(TIMESTAMP_FORMATTER.get().format(_expires)).toString() : "";
String expires = _timeout < 0 ? "" : new StringBuilder().append(" max-age=").append(_timeout).toString();
String sameSite = _sameSite == SameSite.NORMAL ? "" :
new StringBuilder().append(" SameSite=").append(_sameSite.toString().toLowerCase()).toString();
return new StringBuilder().append('<').append(getClass().getName()).append(" name=").append(_name)
.append(" value=").append(_value).append(" path=").append(_path).append(" domain=").append(_domain)
.append(expiresString).append(expires).append(" isSecure=").append(_isSecure)
.append(" isHttpOnly=").append(_isHttpOnly).append(sameSite).append('>').toString();
}
public String headerString() {
return _headerString(false);
}
String _headerString(final boolean isRequest) {
StringBuilder header = new StringBuilder(140);
header.append(_name);
header.append('=');
if (_value != null && _value.indexOf(' ') != -1 && (!_value.startsWith("\"") || !_value.endsWith("\""))) {
header.append("\"");
header.append(_value);
header.append("\"");
} else if (_value == null) {
header.append(' ');
} else {
header.append(_value);
}
if (!isRequest) {
NSTimestamp localExpires = _expires;
header.append("; version=\"1\"");
if (_timeout >= 0) {
header.append("; max-age=");
header.append(_timeout);
if (_timeout == 0) {
localExpires = new NSTimestamp(0L);
} else {
localExpires = new NSTimestamp(System.currentTimeMillis() + (_timeout * 1000));
}
}
if (_expires != null) {
header.append("; expires=");
header.append(TIMESTAMP_FORMATTER.get().format(localExpires));
}
if (_path != null) {
header.append("; path=");
header.append(_path);
}
if (_domain != null) {
header.append("; domain=");
header.append(_domain);
}
if (_isSecure) {
header.append("; secure");
}
if (_isHttpOnly) {
header.append("; HttpOnly");
}
if (_sameSite != SameSite.NORMAL) {
header.append("; SameSite=");
header.append(_sameSite.toString().toLowerCase());
}
}
return header.toString();
}
public String name() {
return _name;
}
public void setName(final String name) {
_name = name;
}
public String value() {
return _value;
}
public void setValue(final String value) {
_value = value;
}
public String domain() {
return _domain;
}
public void setDomain(final String domain) {
_domain = domain;
}
public String path() {
return _path;
}
public void setPath(final String path) {
_path = path;
}
public NSTimestamp expires() {
return _expires;
}
public void setExpires(final NSTimestamp expires) {
_expires = expires;
}
public void setTimeOut(final int timeout) {
_timeout = timeout;
}
public int timeOut() {
return _timeout;
}
public boolean isSecure() {
return _isSecure;
}
public void setIsSecure(final boolean isSecure) {
_isSecure = isSecure;
}
public boolean isHttpOnly() {
return _isHttpOnly;
}
public void setIsHttpOnly(final boolean isHttpOnly) {
_isHttpOnly = isHttpOnly;
}
public SameSite sameSite() {
return _sameSite;
}
public void setSameSite(final SameSite sameSite) {
_sameSite = sameSite;
}
public static boolean canAccessFieldsDirectly() {
return true;
}
public Object valueForKey(final String key) {
return NSKeyValueCoding.DefaultImplementation.valueForKey(this, key);
}
public void takeValueForKey(final Object value, final String key) {
NSKeyValueCoding.DefaultImplementation.takeValueForKey(this, value, key);
}
public Object handleQueryWithUnboundKey(final String key) {
return NSKeyValueCoding.DefaultImplementation.handleQueryWithUnboundKey(this, key);
}
public void handleTakeValueForUnboundKey(final Object value, final String key) {
NSKeyValueCoding.DefaultImplementation.handleTakeValueForUnboundKey(this, value, key);
}
public void unableToSetNullForKey(final String key) {
NSKeyValueCoding.DefaultImplementation.unableToSetNullForKey(this, key);
}
public Object valueForKeyPath(final String key) {
return NSKeyValueCodingAdditions.DefaultImplementation.valueForKeyPath(this, key);
}
public void takeValueForKeyPath(final Object value, final String key) {
NSKeyValueCodingAdditions.DefaultImplementation.takeValueForKeyPath(this, value, key);
}
private void writeObject(final ObjectOutputStream out) throws IOException {
out.writeInt(_timeout);
out.writeUTF(_name);
out.writeUTF(_value);
out.writeUTF(_domain);
out.writeUTF(_path);
out.writeBoolean(_isSecure);
out.writeObject(_expires);
out.writeBoolean(_isHttpOnly);
out.writeObject(_sameSite);
}
private void readObject(final ObjectInputStream out) throws IOException, ClassNotFoundException {
_timeout = out.readInt();
_name = out.readUTF();
_value = out.readUTF();
_domain = out.readUTF();
_path = out.readUTF();
_isSecure = out.readBoolean();
_expires = (NSTimestamp) out.readObject();
_isHttpOnly = out.readBoolean();
_sameSite = (SameSite) out.readObject();
}
/**
* Possible values for same-site cookie setting.
*
* @see <a href="https://tools.ietf.org/html/draft-west-first-party-cookies-07">RFC draft</a>
*/
public static enum SameSite {
NORMAL,
LAX,
STRICT
}
}