Skip to content

Instantly share code, notes, and snippets.

@imminent
Last active December 14, 2015 06:38

Revisions

  1. imminent revised this gist Feb 27, 2013. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions AndroidCookieStore.java
    Original file line number Diff line number Diff line change
    @@ -65,12 +65,12 @@ public List<HttpCookie> get(URI _) {
    } catch (Exception error) {
    Ln.e(error.getCause());
    }
    return null;
    return newArrayList();
    }

    @Override
    public List<URI> getURIs() {
    return null;
    return newArrayList();
    }

    @Override
  2. imminent created this gist Feb 27, 2013.
    94 changes: 94 additions & 0 deletions AndroidCookieStore.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    import android.annotation.TargetApi;
    import android.content.SharedPreferences;
    import android.os.Build;
    import android.os.PatternMatcher;
    import com.keepandshare.android.utils.CryptographyUtil;
    import com.keepandshare.android.utils.Ln;

    import javax.inject.Singleton;
    import java.net.CookieStore;
    import java.net.HttpCookie;
    import java.net.URI;
    import java.util.List;

    import static com.google.common.collect.Lists.newArrayList;
    import static com.keepandshare.android.utils.SharedPreferencesCompat.apply;

    /**
    * A simple {@linkplain CookieStore cookie jar} that only cares to store the {@link #_COOKIE_AUTH_TOKEN}.
    * It persists the cookie through app launches and maintains it securely.
    * @author Dandré Allison
    */
    @Singleton
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    public class AndroidCookieStore implements CookieStore {

    /* Constructor */
    public AndroidCookieStore(SharedPreferences settings, CryptographyUtil crypto) {
    _settings = settings;
    _crypto = crypto;
    }

    /* Cookie Store */
    @Override
    public void add(URI _, HttpCookie cookie) {
    Ln.v("Adding Cookie: %s", cookie);

    // Stores the cookie in shared preferences
    if (_auth_token_pattern.match(cookie.getName())) {
    final SharedPreferences.Editor editor = _settings.edit();
    try {
    editor.putString(_KEY_AUTH_TOKEN, _crypto.encrypt(cookie.getValue()));
    } catch (Exception error) {
    Ln.e(error.getCause());
    }
    apply(editor);
    }
    }

    @Override
    public List<HttpCookie> getCookies() {
    return get(null);
    }

    @Override
    public List<HttpCookie> get(URI _) {
    // Loads in cookies from shared preferences
    final String auth_token = _settings.getString(_KEY_AUTH_TOKEN, null);
    if (auth_token != null)
    try {
    final HttpCookie cookie = new HttpCookie(_COOKIE_AUTH_TOKEN, _crypto.decrypt(auth_token));
    cookie.setPath("/");
    cookie.setVersion(0);
    Ln.v("Retrieved Cookie: %s", cookie);
    return newArrayList(cookie);
    } catch (Exception error) {
    Ln.e(error.getCause());
    }
    return null;
    }

    @Override
    public List<URI> getURIs() {
    return null;
    }

    @Override
    public boolean remove(URI _, HttpCookie __) {
    final boolean had_auth_token = _settings.getString(_KEY_AUTH_TOKEN, null) != null;
    final SharedPreferences.Editor editor = _settings.edit();
    apply(editor.remove(_KEY_AUTH_TOKEN));
    return had_auth_token;
    }

    @Override
    public boolean removeAll() {
    return remove(null,null);
    }

    private static final String _KEY_AUTH_TOKEN = "AUTH_TOKEN";
    private static final String _COOKIE_AUTH_TOKEN = "PHPSESSID";
    private static final PatternMatcher _auth_token_pattern = new PatternMatcher(_COOKIE_AUTH_TOKEN, PatternMatcher.PATTERN_LITERAL);
    private final SharedPreferences _settings;
    private final CryptographyUtil _crypto;
    }
    120 changes: 120 additions & 0 deletions RecommendedAndroidClient.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,120 @@
    import android.os.PatternMatcher;
    import com.github.kevinsawicki.http.HttpRequest;
    import com.keepandshare.android.utils.CircularByteBuffer;
    import com.keepandshare.android.utils.Ln;
    import org.apache.http.protocol.HttpContext;
    import retrofit.http.Header;
    import retrofit.http.client.Client;
    import retrofit.http.client.Request;
    import retrofit.http.client.Response;
    import retrofit.http.mime.TypedInput;
    import retrofit.http.mime.TypedOutput;

    import java.io.BufferedInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    import static com.google.common.collect.Maps.newHashMap;

    /**
    * Custom implementation of {@link Client} that can utilize an {@link HttpContext}.
    * @author Dandré Allison
    */
    public class RecommendedAndroidClient implements Client {

    @Override
    public Response execute(Request request) throws IOException {
    Ln.v("Request: %s '%s'", request.getMethod(), _sanitize_passwords.reset(request.getUrl()).replaceAll("p=***"));
    // Creates the request
    final HttpRequest http_response = new HttpRequest(request.getUrl(), request.getMethod())
    // Requests compressed responses
    .acceptGzipEncoding()
    // Automatically uncompresses response
    .uncompress(true);
    prepareRequest(http_response);

    final List<Header> headers = request.getHeaders();
    if (headers != null)
    http_response.headers(prepareHeaders(request.getHeaders()));

    final TypedOutput body = request.getBody();
    if (body != null) {
    final CircularByteBuffer byte_buffer = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
    request.getBody().writeTo(byte_buffer.getOutputStream());
    http_response.send(byte_buffer.getInputStream());
    }

    // Creates the response
    return parseResponse(http_response);
    }

    Response parseResponse(HttpRequest response) throws IOException {
    final InputStream stream = response.buffer();
    final int status = response.code();
    final String reason = response.message();

    final List<Header> headers = new ArrayList<Header>();
    String content_type = "application/octet-stream";
    for (Map.Entry<String, List<String>> header : response.headers().entrySet()) {
    final String name = header.getKey();
    final String value = header.getValue().get(0);
    if (_content_type_header.match(name))
    content_type = value;
    headers.add(new Header(name, value));
    }

    prepareResponse(response, stream);

    final TypedInputStream body = stream == null? null : new TypedInputStream(content_type, stream);

    return new Response(status, reason, headers, body);
    }

    /** Callback for additional preparation of the request before execution. */
    protected void prepareRequest(HttpRequest request) { }

    /** Callback for additional preparation of the response before parsing. */
    protected void prepareResponse(HttpRequest response, InputStream body) { }

    static class TypedInputStream implements TypedInput {

    public TypedInputStream(String mime_type, InputStream in) {
    _mime_type = mime_type;
    _in = new BufferedInputStream(in);
    }

    @Override
    public String mimeType() {
    return _mime_type;
    }

    @Override
    public long length() {
    return -1;
    }

    @Override
    public InputStream in() throws IOException {
    return _in;
    }

    private final String _mime_type;
    public final InputStream _in;
    }

    private Map<String, String> prepareHeaders(List<Header> headers) {
    if (headers == null) return null;
    final Map<String, String> header_map = newHashMap();
    for (Header header : headers)
    header_map.put(header.getName(), header.getValue());
    return header_map;
    }

    private static final PatternMatcher _content_type_header = new PatternMatcher(HttpRequest.HEADER_CONTENT_TYPE, PatternMatcher.PATTERN_LITERAL);
    private static final Matcher _sanitize_passwords = Pattern.compile("(p=)[^&]*").matcher("");
    }
    7 changes: 7 additions & 0 deletions Snippets
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    // Sets the AndroidCookieStore as the CookieStore to use for managing magic-cookies
    final CookieManager cookie_manager = new CookieManager(cookie_jar, CookiePolicy.ACCEPT_ORIGINAL_SERVER);
    CookieHandler.setDefault(new CookieManager());

    // Configures the RestAdapter to use RecommendedAndroidClient instead of AndroidHttpClient
    RestAdapter rest_adapter = new RestAdapter.Builder().setServer("http://www.example.com)
    .setClient(http_client).build();