From a37b3d86ffc2fef179d889dedef67f709fe11a3f Mon Sep 17 00:00:00 2001 From: Ecchilon Date: Tue, 29 Nov 2016 00:08:48 +0100 Subject: [PATCH] Fix thumbnail loading The thumbnails required an 'lv' cookie that was provided when loading the main page. Instead of again hacking this in again, the cookie management was slightly refactored. The 'getBody' request as well as the 'login' request now properly apply the 'Set-Cookie' headers in ExHentaiAuth, which stores them in a separate store. This does mean existing users will be forced to login again. Additionally bumped gradle because the Android Plugin was complaining. --- app/build.gradle | 1 + .../com/ecchilon/sadpanda/ExhentaiModule.java | 18 ++- .../com/ecchilon/sadpanda/api/DataLoader.java | 41 +------ .../ecchilon/sadpanda/auth/ExhentaiAuth.java | 105 +++++++----------- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 6 files changed, 62 insertions(+), 109 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index cb367db..d3f76e4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -57,6 +57,7 @@ dependencies { compile 'com.squareup.okhttp3:okhttp:3.0.1' compile 'org.apache.commons:commons-lang3:3.1' compile 'com.squareup.picasso:picasso:2.5.2' + compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13' compile 'com.github.chrisbanes.photoview:library:1.2.3' compile 'me.imid.swipebacklayout.lib:library:1.0.0' diff --git a/app/src/main/java/com/ecchilon/sadpanda/ExhentaiModule.java b/app/src/main/java/com/ecchilon/sadpanda/ExhentaiModule.java index 51cde5d..2a1a6b4 100644 --- a/app/src/main/java/com/ecchilon/sadpanda/ExhentaiModule.java +++ b/app/src/main/java/com/ecchilon/sadpanda/ExhentaiModule.java @@ -1,18 +1,19 @@ package com.ecchilon.sadpanda; + import android.app.Application; import android.content.Context; import com.ecchilon.sadpanda.auth.ExhentaiAuth; import com.google.inject.AbstractModule; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.Provides; import com.google.inject.Singleton; +import com.jakewharton.picasso.OkHttp3Downloader; import com.squareup.picasso.Picasso; import okhttp3.OkHttpClient; +import okhttp3.Request; -/** - * Created by Alex on 20-9-2014. - */ public class ExhentaiModule extends AbstractModule { @Inject @@ -26,8 +27,19 @@ public ExhentaiModule(Application application) { protected void configure() { bind(ExhentaiAuth.class).in(Singleton.class); + Provider provider = getProvider(ExhentaiAuth.class); + OkHttpClient client = new OkHttpClient.Builder() + .addInterceptor(chain -> { + Request newRequest = chain.request().newBuilder() + .addHeader("Cookie", provider.get().getSessionCookie()) + .build(); + return chain.proceed(newRequest); + }) + .build(); + Picasso.Builder builder = new Picasso.Builder(context); builder.listener(new SadPandaApp.PicassoListener()); + builder.downloader(new OkHttp3Downloader(client)); Picasso.setSingletonInstance(builder.build()); } diff --git a/app/src/main/java/com/ecchilon/sadpanda/api/DataLoader.java b/app/src/main/java/com/ecchilon/sadpanda/api/DataLoader.java index 1257aa6..099fe2f 100644 --- a/app/src/main/java/com/ecchilon/sadpanda/api/DataLoader.java +++ b/app/src/main/java/com/ecchilon/sadpanda/api/DataLoader.java @@ -29,7 +29,6 @@ import lombok.Value; import okhttp3.CacheControl; import okhttp3.FormBody; -import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -77,8 +76,6 @@ public static class GalleryIdToken { private final OkHttpClient client; private final ExhentaiAuth auth; - private String sHeader; - @Inject DataLoader(OkHttpClient client, ExhentaiAuth auth) { this.client = client; @@ -277,7 +274,7 @@ private Observable getContent(String url) { private Observable getContent(String url, boolean useCache) { return Observable.just(url).map(requestUrl -> { Request.Builder builder = new Request.Builder() - .addHeader(COOKIE, getCookieHeader()) + .addHeader(COOKIE, auth.getSessionCookie()) .url(requestUrl) .get(); if (!useCache) { @@ -465,7 +462,7 @@ private Observable updateFavorite(@NonNull String favCat, @NonNull String Request request = new Request.Builder() .url(String.format(FAVORITES_URL_EX, entry.getGalleryId(), entry.getToken())) - .addHeader(COOKIE, getCookieHeader()) + .addHeader(COOKIE, auth.getSessionCookie()) .post(body) .build(); @@ -507,12 +504,7 @@ private String getBody(Request request) { Response response = null; try { response = client.newCall(request).execute(); - if (this.sHeader == null) { - this.sHeader = getSHeader(response.headers()); - if (sHeader != null) { - return reloadWithSHeader(request); - } - } + auth.addCookies(response.headers(ExhentaiAuth.SET_COOKIE)); return response.body().string(); } catch (IOException e) { @@ -524,31 +516,4 @@ private String getBody(Request request) { } } } - - private String getSHeader(Headers headers) { - for (String header : headers.values(SET_COOKIE)) { - if (header.startsWith("s=")) { - return header.split(";")[0].substring(2); - } - } - - return null; - } - - /** - * Somehow Exhentai sends a cookie-header 's' along with its favorites. Because - */ - private String reloadWithSHeader(Request request) { - Request.Builder builder = request.newBuilder(); - builder.header(COOKIE, getCookieHeader()); - return getBody(builder.build()); - } - - private String getCookieHeader() { - String cookie = auth.getSessionCookie(); - if (sHeader != null) { - cookie += ";s=" + sHeader; - } - return cookie; - } } diff --git a/app/src/main/java/com/ecchilon/sadpanda/auth/ExhentaiAuth.java b/app/src/main/java/com/ecchilon/sadpanda/auth/ExhentaiAuth.java index 0304ade..dba623e 100644 --- a/app/src/main/java/com/ecchilon/sadpanda/auth/ExhentaiAuth.java +++ b/app/src/main/java/com/ecchilon/sadpanda/auth/ExhentaiAuth.java @@ -1,13 +1,15 @@ package com.ecchilon.sadpanda.auth; -import static com.ecchilon.sadpanda.auth.ExhentaiAuth.ExhentaiError.INCORRECT_AUTH; import static com.ecchilon.sadpanda.auth.ExhentaiAuth.ExhentaiError.UNKNOWN; import java.io.IOException; -import java.util.Collections; +import java.net.HttpCookie; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; import com.google.common.collect.Lists; @@ -22,6 +24,7 @@ import rx.schedulers.Schedulers; public class ExhentaiAuth { + public enum ExhentaiError { NO_USERNAME("You must enter a username"), USER_NOT_FOUND("You must already have registered for an account before you can log in"), @@ -47,14 +50,10 @@ public static class AuthException extends RuntimeException { } } - private static final String USERNAME_KEY = "pandaUserNameKey"; - public static final String MEMBER_KEY = "pandaMemberKey"; - public static final String HASH_KEY = "pandaHashKey"; - public static final String SESSION_KEY = "pandaSessionKey"; + public static final String SET_COOKIE = "Set-Cookie"; + private static final String COOKIE_PREFERENCES = "cookie_prefs"; - public static final String IPB_MEMBER_ID = "ipb_member_id"; - public static final String IPB_PASS_HASH = "ipb_pass_hash"; - public static final String IPB_SESSION_ID = "ipb_session_id"; + private static final String USERNAME_KEY = "pandaUserNameKey"; private static final String LOGIN = "https://forums.e-hentai.org/index.php?act=Login&CODE=01"; @@ -64,19 +63,16 @@ public static class AuthException extends RuntimeException { private final AtomicReference sessionCookie = new AtomicReference<>(); @Inject - public ExhentaiAuth(OkHttpClient client, SharedPreferences sharedPreferences) { + public ExhentaiAuth(OkHttpClient client, Context context) { this.client = client; - this.sharedPreferences = sharedPreferences; + this.sharedPreferences = context.getSharedPreferences(COOKIE_PREFERENCES, Context.MODE_PRIVATE); } public void logout() { sessionCookie.set(null); sharedPreferences.edit() - .remove(MEMBER_KEY) - .remove(HASH_KEY) - .remove(SESSION_KEY) - .remove(USERNAME_KEY) + .clear() .apply(); } @@ -104,7 +100,7 @@ public Observable login(final String username, String password) { .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(response -> { - String body = null; + String body; try { body = response.body().string(); } @@ -112,42 +108,8 @@ public Observable login(final String username, String password) { throw OnErrorThrowable.from(e); } if (body.contains("You are now logged in as: ")) { - List cookies = Lists.newArrayList(); - for(String cookie : response.headers("Set-Cookie")) { - Collections.addAll(cookies, cookie.split(";")); - } - - String memberId = null, passHash = null, sessionId = null; - - for (String cookie : cookies) { - String[] kvPair = cookie.split("="); - if(kvPair.length != 2) { - continue; - } - String key = kvPair[0]; - String value = kvPair[1]; - if(IPB_MEMBER_ID.equals(key)) { - memberId = value; - } - else if(IPB_PASS_HASH.equals(key)) { - passHash = value; - } - else if(IPB_SESSION_ID.equals(key)) { - sessionId = value; - } - } - - if((memberId == null || passHash == null || sessionId == null)) { - throw OnErrorThrowable.from(new AuthException(INCORRECT_AUTH)); - } - - sharedPreferences.edit() - .putString(MEMBER_KEY, memberId) - .putString(HASH_KEY, passHash) - .putString(SESSION_KEY, sessionId) - .putString(USERNAME_KEY, username) - .apply(); - + addCookies(response.headers(SET_COOKIE)); + sharedPreferences.edit().putString(USERNAME_KEY, username).apply(); return null; } else { @@ -163,18 +125,35 @@ else if(IPB_SESSION_ID.equals(key)) { } public boolean isLoggedIn() { - return sharedPreferences.contains(SESSION_KEY); + return sharedPreferences.contains(USERNAME_KEY); } public String getUserName() { return sharedPreferences.getString(USERNAME_KEY, null); } + public void addCookies(Collection cookies) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + boolean addedCookie = false; + for (String entry : cookies) { + for (HttpCookie cookie : HttpCookie.parse(entry)) { + editor.putString(cookie.getName(), cookie.getValue()); + addedCookie = true; + } + } + + if (addedCookie) { + sessionCookie.set(null); + } + + editor.apply(); + } + public String getSessionCookie() { synchronized (sessionCookie) { String cookie = sessionCookie.get(); - if(cookie == null) { - cookie = createCookie(); + if (cookie == null) { + cookie = createSessionCookie(); sessionCookie.set(cookie); } @@ -182,18 +161,14 @@ public String getSessionCookie() { } } - private String createCookie() { - String[] cookies = new String[] { - getCookieKeyValue(IPB_MEMBER_ID, MEMBER_KEY), - getCookieKeyValue(IPB_PASS_HASH, HASH_KEY), - getCookieKeyValue(IPB_SESSION_ID, SESSION_KEY) - }; + private String createSessionCookie() { + Map cookieMap = sharedPreferences.getAll(); + List cookies = Lists.newArrayListWithExpectedSize(cookieMap.size()); + for (Map.Entry entry : cookieMap.entrySet()) { + cookies.add(entry.getKey() + "=" + entry.getValue()); + } return TextUtils.join(";", cookies); } - - private String getCookieKeyValue(String key, String preferenceKey) { - return key + "=" + sharedPreferences.getString(preferenceKey, ""); - } } diff --git a/build.gradle b/build.gradle index 449e8c5..e3e29bc 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.2.0' classpath 'me.tatarka:gradle-retrolambda:3.2.4' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a0db497..ebf40a0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Mar 03 14:18:53 CET 2015 +#Mon Nov 28 22:29:10 CET 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip