Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
18 changes: 15 additions & 3 deletions app/src/main/java/com/ecchilon/sadpanda/ExhentaiModule.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -26,8 +27,19 @@ public ExhentaiModule(Application application) {
protected void configure() {
bind(ExhentaiAuth.class).in(Singleton.class);

Provider<ExhentaiAuth> 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());
}
Expand Down
41 changes: 3 additions & 38 deletions app/src/main/java/com/ecchilon/sadpanda/api/DataLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -277,7 +274,7 @@ private Observable<String> getContent(String url) {
private Observable<String> 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) {
Expand Down Expand Up @@ -465,7 +462,7 @@ private Observable<Void> 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();

Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
}
105 changes: 40 additions & 65 deletions app/src/main/java/com/ecchilon/sadpanda/auth/ExhentaiAuth.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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"),
Expand All @@ -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";

Expand All @@ -64,19 +63,16 @@ public static class AuthException extends RuntimeException {
private final AtomicReference<String> 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();
}

Expand Down Expand Up @@ -104,50 +100,16 @@ public Observable<Void> login(final String username, String password) {
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map(response -> {
String body = null;
String body;
try {
body = response.body().string();
}
catch (IOException e) {
throw OnErrorThrowable.from(e);
}
if (body.contains("You are now logged in as: ")) {
List<String> 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 {
Expand All @@ -163,37 +125,50 @@ 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<String> 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);
}

return cookie;
}
}

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<String, ?> cookieMap = sharedPreferences.getAll();
List<String> cookies = Lists.newArrayListWithExpectedSize(cookieMap.size());
for (Map.Entry<String, ?> 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, "");
}
}

2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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