com.citrix.sharefile.api.https.SFCookieManager Maven / Gradle / Ivy
package com.citrix.sharefile.api.https;
import com.citrix.sharefile.api.SFSDKDefaultAccessScope;
import com.citrix.sharefile.api.exceptions.SFFormsAuthenticationCookies;
import com.citrix.sharefile.api.log.Logger;
import com.citrix.sharefile.api.utils.SFDateFormat;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
public class SFCookieManager
{
private static final String TAG = "SFCookieManager";
public static class Cookie {
public String value;
public String path=null;
public String expires=null;
public Cookie(String value) {
this.value = value;
}
}
/**
* mStore is the map of all domains and their corresponding cookie stores.
* so if we have cookies from hosts like: citrix.sharefile.com or domian.host.com
*/
private final Map> mStore;
private static final String SET_COOKIE = "Set-Cookie";
private static final String WWWW_AUTHENTICATE = "WWW-Authenticate";
private static final String COOKIE_VALUE_DELIMITER = ";";
private static final String PATH = "path";
private static final String EXPIRES = "expires";
private static final String SET_COOKIE_SEPARATOR="; ";
private static final String COOKIE = "Cookie";
private static final char NAME_VALUE_SEPARATOR = '=';
private static final char DOT = '.';
public SFCookieManager()
{
mStore = new HashMap>();
}
public synchronized void clearAllCookies()
{
if(mStore!=null)
{
mStore.clear();
}
}
/**
* Allows the app to store cookies inside the cookie store so that it will automatically be set for connections to the
* specified domain
*/
public void storeAppSpecificCookies(URI uri,String cookieString)
{
Map domainStore = getDomainStoreFromHost(uri.getHost());
storeCookieToDomainStore(domainStore, cookieString);
}
/**
* Allows the app to store cookies inside the cookie store so that it will automatically be set for connections to the
* specified domain
*/
public void storeAppSpecificCookies(String urlstr,String cookieString)
{
try
{
URI uri = new URI(urlstr);
storeAppSpecificCookies(uri, cookieString);
}
catch (URISyntaxException e)
{
Logger.e(TAG,e);
}
}
private Map getDomainStoreFromHost(String host)
{
// let's determine the domain from where these cookies are being sent
String domain = getDomainFromHost(host);
Map domainStore; // this is where we will store cookies for this domain
// now let's check the store to see if we have an entry for this domain
if (mStore.containsKey(domain))
{
// we do, so lets retrieve it from the store
domainStore = mStore.get(domain);
}
else
{
// we don't, so let's create it and put it in the store
domainStore = new HashMap();
mStore.put(domain, domainStore);
}
return domainStore;
}
private void storeCookieToDomainStore(Map domainStore,String cookieString)
{
// the specification dictates that the first name/value pair
// in the string is the cookie name and value, so let's handle
// them as a special case:
Cookie cookie = null;
StringTokenizer st = new StringTokenizer(cookieString, COOKIE_VALUE_DELIMITER);
while ( st.hasMoreTokens() ) {
String token = st.nextToken();
int index = token.indexOf(NAME_VALUE_SEPARATOR);
if ( index<0 || index>= token.length() ) {
if ( cookie==null ) {
// e
Logger.e(TAG, "Invalid cookie string: " + cookieString);
return;
}
Logger.v(TAG, "Not a value/pair, ignore for now as we don't use it: " + token);
continue;
}
// value pair
String name = token.substring(0, index);
String value = token.substring(index + 1);
if ( cookie==null ) {
cookie = new Cookie(value);
domainStore.put(name, cookie);
}
//Use instead of ignorecase() for locale consistency
String nameLower = name.toLowerCase(Locale.US).trim();
if ( EXPIRES.equals(nameLower) ) {
cookie.expires = value;
} else if ( PATH.equals(nameLower) ) {
cookie.path = value;
}
/*else
{
// don't have any use for this part of the cookie
// ...
}*/
}
}
/**
* Retrieves and stores cookies returned by the host on the other side
* of the the open java.net.URLConnection.
*
* The connection MUST have been opened using the connect()
* method or a IOException will be thrown.
*
* @param conn a java.net.URLConnection - must be open, or IOException will be thrown
* @throws java.io.IOException Thrown if conn is not open.
*/
@SFSDKDefaultAccessScope void readCookiesFromConnection(URLConnection conn) throws IOException
{
Map domainStore = getDomainStoreFromHost(conn.getURL().getHost());
// OK, now we are ready to get the cookies out of the URLConnection
String headerName;
for (int i=1; (headerName = conn.getHeaderFieldKey(i)) != null; i++)
{
if (headerName.equalsIgnoreCase(SET_COOKIE))
{
storeCookieToDomainStore(domainStore, conn.getHeaderField(i));
}
}
}
/**
* Retreivees the www-authentication-headers for personal cloud connectors when there is a not authorized exception
* @param conn
* @throws IOException
*/
@SFSDKDefaultAccessScope
SFFormsAuthenticationCookies readFormsAuthCookies(URLConnection conn) throws IOException
{
String headerName;
SFFormsAuthenticationCookies SFFormsAuthenticationCookies = null;
for (int i=1; (headerName = conn.getHeaderFieldKey(i)) != null; i++)
{
if (headerName.equalsIgnoreCase(WWWW_AUTHENTICATE)) {
if (SFFormsAuthenticationCookies == null) {
String headerValue = conn.getHeaderField(i);
String[] values = parseAuthenticationResponseHeader(headerValue);
if(values != null && values.length == 2) {
SFFormsAuthenticationCookies = new SFFormsAuthenticationCookies();
SFFormsAuthenticationCookies.setLoginURL(values[0]);
SFFormsAuthenticationCookies.setToken(values[1]);
}
return SFFormsAuthenticationCookies;
}
}
}
return SFFormsAuthenticationCookies;
}
private String[] parseAuthenticationResponseHeader(String headerValue) {
String[] result = new String[2];
String[] split = headerValue.split(" ");
if(split.length == 4) {
result[0] = split[1];
result[1] = split[3];
return result;
}
return null;
}
/**
* Prior to opening a URLConnection, calling this method will set all
* unexpired cookies that match the path or subpaths for the underlying URL
*
* The connection MUST NOT have been opened
* method or an IOException will be thrown.
*
* @param conn a java.net.URLConnection - must NOT be open, or IOException will be thrown
* @throws java.io.IOException Thrown if conn has already been opened.
*/
@SFSDKDefaultAccessScope void setCookies(URLConnection conn) throws IOException
{
// let's determine the domain and path to retrieve the appropriate cookies
URL url = conn.getURL();
String domain = getDomainFromHost(url.getHost());
String path = url.getPath();
Map domainStore = mStore.get(domain);
if (domainStore == null) return;
StringBuilder cookieStringBuffer = new StringBuilder();
Iterator> cookieNames = domainStore.keySet().iterator();
while(cookieNames.hasNext())
{
String cookieName = (String)cookieNames.next();
Cookie cookie = domainStore.get(cookieName);
// check cookie to ensure path matches and cookie is not expired
// if all is cool, add cookie to header string
if (comparePaths(cookie.path, path) && isNotExpired(cookie.expires) )
{
cookieStringBuffer.append(cookieName);
cookieStringBuffer.append("=");
cookieStringBuffer.append(cookie.value);
if (cookieNames.hasNext()) cookieStringBuffer.append(SET_COOKIE_SEPARATOR);
}
}
try
{
conn.setRequestProperty(COOKIE, cookieStringBuffer.toString());
}
catch (java.lang.IllegalStateException ise)
{
throw new IOException("Illegal State! Cookies cannot be set on a URLConnection that is already connected. "
+ "Only call setCookies(java.net.URLConnection) AFTER calling java.net.URLConnection.connect().");
}
}
private String getDomainFromHost(String host)
{
if (host.indexOf(DOT) != host.lastIndexOf(DOT))
{
return host.substring(host.indexOf(DOT) + 1);
}
else
{
return host;
}
}
private boolean isNotExpired(String cookieExpires)
{
if (cookieExpires == null) return true;
Date now = new Date();
Date expireDate = SFDateFormat.parse(cookieExpires);
if(expireDate==null){
//parse will fail if using unknown date formats Lets be safe and say it is good.
return true;
}
return now.compareTo(expireDate) <= 0;
}
private boolean comparePaths(String cookiePath, String targetPath)
{
if (cookiePath == null)
{
return true;
}
else if (cookiePath.equals("/"))
{
return true;
}
return targetPath.regionMatches(0, cookiePath, 0, cookiePath.length());
}
/**
* Returns a string representation of stored cookies organized by domain.
*/
public String toString()
{
return mStore.toString();
}
private void removeCookiesForDomain(String domain)
{
mStore.remove(domain);
}
/**
* Removes a specific cookie for the domain.
* This is needed to support legacy connectors
* Ideally the platform should send us back cookies with unique paths
* @param uri
* @param key
*/
public void removeCookieStartsWith(URI uri, String key) {
Map domainStore = getDomainStoreFromHost(uri.getHost());
//do this to avoid concurrent issues
HashSet keySet = new HashSet<>();
keySet.addAll(domainStore.keySet());
for (String entry : keySet)
{
if(entry.startsWith(key)){
domainStore.remove(entry);
}
}
}
/**
* removes all cookies for the domain in the given URI
*/
public void removeCookies(URI uri)
{
removeCookiesForDomain(getDomainFromHost(uri.getHost()));
}
/**
* removes all cookies for the domain in the given URI
*/
public void removeCookies(String urlstr)
{
try
{
URI uri = new URI(urlstr);
removeCookies(uri);
}
catch (URISyntaxException e)
{
Logger.e(TAG,e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy